Skip to content

Commit 00042d2

Browse files
authored
Merge pull request #3642 from magento-engcom/2.3-develop-prs
[EngCom] Public Pull Requests - 2.3-develop
2 parents 4c99249 + 1c9a0a8 commit 00042d2

File tree

9 files changed

+295
-23
lines changed

9 files changed

+295
-23
lines changed

app/code/Magento/WebapiAsync/Code/Generator/Config/RemoteServiceReader/Communication.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,8 @@ public function read($scope = null)
6767
CommunicationConfig::HANDLER_TYPE => $serviceClass,
6868
CommunicationConfig::HANDLER_METHOD => $serviceMethod,
6969
],
70-
]
70+
],
71+
false
7172
);
7273
$rewriteTopicParams = [
7374
CommunicationConfig::TOPIC_IS_SYNCHRONOUS => false,

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

Lines changed: 46 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
use Magento\Framework\Exception\LocalizedException;
1616
use Magento\Webapi\Model\Config\Converter;
1717

18+
/**
19+
* Class for accessing to Webapi_Async configuration.
20+
*/
1821
class Config implements \Magento\AsynchronousOperations\Model\ConfigInterface
1922
{
2023
/**
@@ -55,7 +58,7 @@ public function __construct(
5558
}
5659

5760
/**
58-
* {@inheritdoc}
61+
* @inheritdoc
5962
*/
6063
public function getServices()
6164
{
@@ -73,26 +76,30 @@ public function getServices()
7376
}
7477

7578
/**
76-
* {@inheritdoc}
79+
* @inheritdoc
7780
*/
7881
public function getTopicName($routeUrl, $httpMethod)
7982
{
8083
$services = $this->getServices();
81-
$topicName = $this->generateTopicNameByRouteData(
84+
$lookupKey = $this->generateLookupKeyByRouteData(
8285
$routeUrl,
8386
$httpMethod
8487
);
8588

86-
if (array_key_exists($topicName, $services) === false) {
89+
if (array_key_exists($lookupKey, $services) === false) {
8790
throw new LocalizedException(
88-
__('WebapiAsync config for "%topicName" does not exist.', ['topicName' => $topicName])
91+
__('WebapiAsync config for "%lookupKey" does not exist.', ['lookupKey' => $lookupKey])
8992
);
9093
}
9194

92-
return $services[$topicName][self::SERVICE_PARAM_KEY_TOPIC];
95+
return $services[$lookupKey][self::SERVICE_PARAM_KEY_TOPIC];
9396
}
9497

9598
/**
99+
* Generate topic data for all defined services
100+
*
101+
* Topic data is indexed by a lookup key that is derived from route data
102+
*
96103
* @return array
97104
*/
98105
private function generateTopicsDataFromWebapiConfig()
@@ -105,11 +112,18 @@ private function generateTopicsDataFromWebapiConfig()
105112
$serviceInterface = $httpMethodData[Converter::KEY_SERVICE][Converter::KEY_SERVICE_CLASS];
106113
$serviceMethod = $httpMethodData[Converter::KEY_SERVICE][Converter::KEY_SERVICE_METHOD];
107114

108-
$topicName = $this->generateTopicNameByRouteData(
115+
$lookupKey = $this->generateLookupKeyByRouteData(
109116
$routeUrl,
110117
$httpMethod
111118
);
112-
$services[$topicName] = [
119+
120+
$topicName = $this->generateTopicNameFromService(
121+
$serviceInterface,
122+
$serviceMethod,
123+
$httpMethod
124+
);
125+
126+
$services[$lookupKey] = [
113127
self::SERVICE_PARAM_KEY_INTERFACE => $serviceInterface,
114128
self::SERVICE_PARAM_KEY_METHOD => $serviceMethod,
115129
self::SERVICE_PARAM_KEY_TOPIC => $topicName,
@@ -122,7 +136,7 @@ private function generateTopicsDataFromWebapiConfig()
122136
}
123137

124138
/**
125-
* Generate topic name based on service type and method name.
139+
* Generate lookup key name based on route and method
126140
*
127141
* Perform the following conversion:
128142
* self::TOPIC_PREFIX + /V1/products + POST => async.V1.products.POST
@@ -131,19 +145,39 @@ private function generateTopicsDataFromWebapiConfig()
131145
* @param string $httpMethod
132146
* @return string
133147
*/
134-
private function generateTopicNameByRouteData($routeUrl, $httpMethod)
148+
private function generateLookupKeyByRouteData($routeUrl, $httpMethod)
135149
{
136-
return self::TOPIC_PREFIX . $this->generateTopicName($routeUrl, $httpMethod, '/', false);
150+
return self::TOPIC_PREFIX . $this->generateKey($routeUrl, $httpMethod, '/', false);
137151
}
138152

139153
/**
154+
* Generate topic name based on service type and method name.
155+
*
156+
* Perform the following conversion:
157+
* self::TOPIC_PREFIX + Magento\Catalog\Api\ProductRepositoryInterface + save + POST
158+
* => async.magento.catalog.api.productrepositoryinterface.save.POST
159+
*
160+
* @param string $serviceInterface
161+
* @param string $serviceMethod
162+
* @param string $httpMethod
163+
* @return string
164+
*/
165+
private function generateTopicNameFromService($serviceInterface, $serviceMethod, $httpMethod)
166+
{
167+
$typeName = strtolower(sprintf('%s.%s', $serviceInterface, $serviceMethod));
168+
return strtolower(self::TOPIC_PREFIX . $this->generateKey($typeName, $httpMethod, '\\', false));
169+
}
170+
171+
/**
172+
* Join and simplify input type and method into a string that can be used as an array key
173+
*
140174
* @param string $typeName
141175
* @param string $methodName
142176
* @param string $delimiter
143177
* @param bool $lcfirst
144178
* @return string
145179
*/
146-
private function generateTopicName($typeName, $methodName, $delimiter = '\\', $lcfirst = true)
180+
private function generateKey($typeName, $methodName, $delimiter = '\\', $lcfirst = true)
147181
{
148182
$parts = explode($delimiter, ltrim($typeName, $delimiter));
149183
foreach ($parts as &$part) {
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
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\Cache;
10+
11+
use Magento\WebapiAsync\Controller\Rest\AsynchronousSchemaRequestProcessor;
12+
use Magento\Framework\Webapi\Rest\Request;
13+
14+
/**
15+
* Class Webapi
16+
*/
17+
class Webapi
18+
{
19+
/**
20+
* Cache key for Async Routes
21+
*/
22+
const ASYNC_ROUTES_CONFIG_CACHE_ID = 'async-routes-services-config';
23+
24+
/**
25+
* @var AsynchronousSchemaRequestProcessor
26+
*/
27+
private $asynchronousSchemaRequestProcessor;
28+
29+
/**
30+
* @var \Magento\Framework\Webapi\Rest\Request
31+
*/
32+
private $request;
33+
34+
/**
35+
* ServiceMetadata constructor.
36+
*
37+
* @param Request $request
38+
* @param AsynchronousSchemaRequestProcessor $asynchronousSchemaRequestProcessor
39+
*/
40+
public function __construct(
41+
\Magento\Framework\Webapi\Rest\Request $request,
42+
AsynchronousSchemaRequestProcessor $asynchronousSchemaRequestProcessor
43+
) {
44+
$this->request = $request;
45+
$this->asynchronousSchemaRequestProcessor = $asynchronousSchemaRequestProcessor;
46+
}
47+
48+
/**
49+
* Change identifier in case if Async request before cache load
50+
*
51+
* @param \Magento\Webapi\Model\Cache\Type\Webapi $subject
52+
* @param string $identifier
53+
* @return null|string
54+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
55+
*/
56+
public function beforeLoad(\Magento\Webapi\Model\Cache\Type\Webapi $subject, $identifier)
57+
{
58+
if ($this->asynchronousSchemaRequestProcessor->canProcess($this->request)
59+
&& $identifier === \Magento\Webapi\Model\ServiceMetadata::ROUTES_CONFIG_CACHE_ID) {
60+
return self::ASYNC_ROUTES_CONFIG_CACHE_ID;
61+
}
62+
return null;
63+
}
64+
65+
/**
66+
* Change identifier in case if Async request before cache save
67+
*
68+
* @param \Magento\Webapi\Model\Cache\Type\Webapi $subject
69+
* @param string $data
70+
* @param string $identifier
71+
* @param array $tags
72+
* @param int|bool|null $lifeTime
73+
* @return array|null
74+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
75+
*/
76+
public function beforeSave(
77+
\Magento\Webapi\Model\Cache\Type\Webapi $subject,
78+
$data,
79+
$identifier,
80+
array $tags = [],
81+
$lifeTime = null
82+
) {
83+
if ($this->asynchronousSchemaRequestProcessor->canProcess($this->request)
84+
&& $identifier === \Magento\Webapi\Model\ServiceMetadata::ROUTES_CONFIG_CACHE_ID) {
85+
return [$data, self::ASYNC_ROUTES_CONFIG_CACHE_ID, $tags, $lifeTime];
86+
}
87+
return null;
88+
}
89+
90+
/**
91+
* Change identifier in case if Async request before remove cache
92+
*
93+
* @param \Magento\Webapi\Model\Cache\Type\Webapi $subject
94+
* @param string $identifier
95+
* @return null|string
96+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
97+
*/
98+
public function beforeRemove(\Magento\Webapi\Model\Cache\Type\Webapi $subject, $identifier)
99+
{
100+
if ($this->asynchronousSchemaRequestProcessor->canProcess($this->request)
101+
&& $identifier === \Magento\Webapi\Model\ServiceMetadata::ROUTES_CONFIG_CACHE_ID) {
102+
return self::ASYNC_ROUTES_CONFIG_CACHE_ID;
103+
}
104+
return null;
105+
}
106+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
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\Test\Unit\Model;
10+
11+
use Magento\Framework\Serialize\SerializerInterface;
12+
use Magento\Webapi\Model\Cache\Type\Webapi;
13+
use Magento\Webapi\Model\Config as WebapiConfig;
14+
use Magento\WebapiAsync\Model\Config;
15+
use Magento\Webapi\Model\Config\Converter;
16+
17+
class ConfigTest extends \PHPUnit\Framework\TestCase
18+
{
19+
/**
20+
* @var Config
21+
*/
22+
private $config;
23+
24+
/**
25+
* @var Webapi|\PHPUnit_Framework_MockObject_MockObject
26+
*/
27+
private $webapiCacheMock;
28+
29+
/**
30+
* @var WebapiConfig|\PHPUnit_Framework_MockObject_MockObject
31+
*/
32+
private $configMock;
33+
34+
/**
35+
* @var SerializerInterface|\PHPUnit_Framework_MockObject_MockObject
36+
*/
37+
private $serializerMock;
38+
39+
protected function setUp()
40+
{
41+
$objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
42+
43+
$this->webapiCacheMock = $this->createMock(\Magento\Webapi\Model\Cache\Type\Webapi::class);
44+
$this->configMock = $this->createMock(WebapiConfig::class);
45+
$this->serializerMock = $this->createMock(SerializerInterface::class);
46+
47+
$this->config = $objectManager->getObject(
48+
Config::class,
49+
[
50+
'cache' => $this->webapiCacheMock,
51+
'webApiConfig' => $this->configMock,
52+
'serializer' => $this->serializerMock
53+
]
54+
);
55+
}
56+
57+
public function testGetServicesSetsTopicFromServiceContractName()
58+
{
59+
$services = [
60+
Converter::KEY_ROUTES => [
61+
'/V1/products' => [
62+
'POST' => [
63+
'service' => [
64+
'class' => \Magento\Catalog\Api\ProductRepositoryInterface::class,
65+
'method' => 'save',
66+
]
67+
]
68+
]
69+
]
70+
];
71+
$this->configMock->expects($this->once())
72+
->method('getServices')
73+
->willReturn($services);
74+
75+
/* example of what $this->config->getServices() returns
76+
$result = [
77+
'async.V1.products.POST' => [
78+
'interface' => 'Magento\Catalog\Api\ProductRepositoryInterface',
79+
'method' => 'save',
80+
'topic' => 'async.magento.catalog.api.productrepositoryinterface.save.post',
81+
]
82+
];
83+
*/
84+
$result = $this->config->getServices();
85+
86+
$expectedTopic = 'async.magento.catalog.api.productrepositoryinterface.save.post';
87+
$lookupKey = 'async.V1.products.POST';
88+
$this->assertArrayHasKey($lookupKey, $result);
89+
$this->assertEquals($result[$lookupKey]['topic'], $expectedTopic);
90+
}
91+
}

app/code/Magento/WebapiAsync/etc/di.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
<type name="Magento\Webapi\Model\ServiceMetadata">
1111
<plugin name="webapiServiceMetadataAsync" type="Magento\WebapiAsync\Plugin\ServiceMetadata" />
1212
</type>
13+
<type name="Magento\Webapi\Model\Cache\Type\Webapi">
14+
<plugin name="webapiCacheAsync" type="Magento\WebapiAsync\Plugin\Cache\Webapi" />
15+
</type>
1316
<virtualType name="Magento\WebapiAsync\Model\VirtualType\Rest\Config" type="Magento\Webapi\Model\Rest\Config">
1417
<arguments>
1518
<argument name="config" xsi:type="object">Magento\WebapiAsync\Model\BulkServiceConfig</argument>

dev/tests/integration/testsuite/Magento/AsynchronousOperations/Model/MassScheduleTest.php

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,10 @@ public function sendBulk($products)
128128
}
129129
$this->clearProducts();
130130

131-
$result = $this->massSchedule->publishMass('async.V1.products.POST', $products);
131+
$result = $this->massSchedule->publishMass(
132+
'async.magento.catalog.api.productrepositoryinterface.save.post',
133+
$products
134+
);
132135

133136
//assert bulk accepted with no errors
134137
$this->assertFalse($result->isErrors());
@@ -206,7 +209,7 @@ public function testScheduleMassOneEntityFailure($products)
206209

207210
$expectedErrorMessage = "Data item corresponding to \"product\" " .
208211
"must be specified in the message with topic " .
209-
"\"async.V1.products.POST\".";
212+
"\"async.magento.catalog.api.productrepositoryinterface.save.post\".";
210213
$this->assertEquals(
211214
$expectedErrorMessage,
212215
$reasonException->getMessage()

0 commit comments

Comments
 (0)