Skip to content

Commit cf6b9d0

Browse files
committed
B2B-2258: Add caching capability to the storeConfig GraphQl query
1 parent e88d25d commit cf6b9d0

File tree

2 files changed

+172
-32
lines changed

2 files changed

+172
-32
lines changed

dev/tests/api-functional/testsuite/Magento/GraphQl/Store/StoreConfigCacheTest.php

Lines changed: 154 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -15,33 +15,54 @@
1515
use Magento\Store\Api\StoreRepositoryInterface;
1616
use Magento\Store\Api\StoreResolverInterface;
1717
use Magento\Store\Model\ScopeInterface;
18+
use Magento\TestFramework\App\ApiMutableScopeConfig;
19+
use Magento\TestFramework\Config\Model\ConfigStorage;
1820
use Magento\TestFramework\Helper\Bootstrap;
19-
use Magento\TestFramework\ObjectManager;
2021

2122
/**
2223
* Test storeConfig query cache
2324
*/
2425
class StoreConfigCacheTest extends GraphQLPageCacheAbstract
2526
{
26-
27-
/** @var ObjectManager */
27+
/**
28+
* @var \Magento\Framework\ObjectManagerInterface
29+
*/
2830
private $objectManager;
2931

32+
/**
33+
* @var ApiMutableScopeConfig
34+
*/
35+
private $config;
36+
37+
/**
38+
* @var ConfigStorage
39+
*/
40+
private $configStorage;
41+
42+
/**
43+
* @var array
44+
*/
45+
private $origConfigs = [];
46+
47+
/**
48+
* @var array
49+
*/
50+
private $notExistingOrigConfigs = [];
51+
52+
/**
53+
* @var StoreConfigInterface
54+
*/
55+
private $defaultStoreConfig;
56+
3057
/**
3158
* @inheritDoc
3259
*/
3360
protected function setUp(): void
3461
{
3562
$this->objectManager = Bootstrap::getObjectManager();
36-
}
63+
$this->configStorage = $this->objectManager->get(ConfigStorage::class);
64+
$this->config = $this->objectManager->get(ApiMutableScopeConfig::class);
3765

38-
/**
39-
* @magentoConfigFixture default/system/full_page_cache/caching_application 2
40-
* @magentoApiDataFixture Magento/Store/_files/store.php
41-
* @throws NoSuchEntityException
42-
*/
43-
public function testGetStoreConfig(): void
44-
{
4566
/** @var StoreConfigManagerInterface $storeConfigManager */
4667
$storeConfigManager = $this->objectManager->get(StoreConfigManagerInterface::class);
4768
/** @var StoreResolverInterface $storeResolver */
@@ -52,15 +73,28 @@ public function testGetStoreConfig(): void
5273
$store = $storeRepository->getById($defaultStoreId);
5374
$defaultStoreCode = $store->getCode();
5475
/** @var StoreConfigInterface $storeConfig */
55-
$storeConfig = current($storeConfigManager->getStoreConfigs([$defaultStoreCode]));
56-
$defaultLocale = $storeConfig->getLocale();
76+
$this->defaultStoreConfig = current($storeConfigManager->getStoreConfigs([$defaultStoreCode]));
77+
}
78+
79+
/**
80+
* storeConfig query is cached.
81+
*
82+
* @magentoConfigFixture default/system/full_page_cache/caching_application 2
83+
* @magentoApiDataFixture Magento/Store/_files/store.php
84+
* @throws NoSuchEntityException
85+
*/
86+
public function testGetStoreConfig(): void
87+
{
88+
$defaultStoreId = $this->defaultStoreConfig->getId();
89+
$defaultStoreCode = $this->defaultStoreConfig->getCode();
90+
$defaultLocale = $this->defaultStoreConfig->getLocale();
5791
$query = $this->getQuery();
5892

5993
// Query default store config
6094
$responseDefaultStore = $this->graphQlQueryWithResponseHeaders($query);
6195
$this->assertArrayHasKey(CacheIdCalculator::CACHE_ID_HEADER, $responseDefaultStore['headers']);
6296
$defaultStoreCacheId = $responseDefaultStore['headers'][CacheIdCalculator::CACHE_ID_HEADER];
63-
// Verify we obtain a cache MISS the first time
97+
// Verify we obtain a cache MISS the 1st time
6498
$defaultStoreResponse = $this->assertCacheMissAndReturnResponse(
6599
$query,
66100
[CacheIdCalculator::CACHE_ID_HEADER => $defaultStoreCacheId]
@@ -70,7 +104,7 @@ public function testGetStoreConfig(): void
70104
$this->assertEquals($defaultStoreId, $defaultStoreResponseResult['id']);
71105
$this->assertEquals($defaultStoreCode, $defaultStoreResponseResult['code']);
72106
$this->assertEquals($defaultLocale, $defaultStoreResponseResult['locale']);
73-
// Verify we obtain a cache HIT the second time
107+
// Verify we obtain a cache HIT the 2nd time
74108
$defaultStoreResponseHit = $this->assertCacheHitAndReturnResponse(
75109
$query,
76110
[CacheIdCalculator::CACHE_ID_HEADER => $defaultStoreCacheId]
@@ -87,7 +121,7 @@ public function testGetStoreConfig(): void
87121
$this->assertArrayHasKey(CacheIdCalculator::CACHE_ID_HEADER, $responseTestStore['headers']);
88122
$testStoreCacheId = $responseTestStore['headers'][CacheIdCalculator::CACHE_ID_HEADER];
89123
$this->assertNotEquals($testStoreCacheId, $defaultStoreCacheId);
90-
// Verify we obtain a cache MISS the first time
124+
// Verify we obtain a cache MISS the 1st time
91125
$testStoreResponse = $this->assertCacheMissAndReturnResponse(
92126
$query,
93127
[
@@ -99,7 +133,7 @@ public function testGetStoreConfig(): void
99133
$testStoreResponseResult = $testStoreResponse['body']['storeConfig'];
100134
$this->assertEquals($testStoreCode, $testStoreResponseResult['code']);
101135
$this->assertEquals($defaultLocale, $testStoreResponseResult['locale']);
102-
// Verify we obtain a cache HIT the second time
136+
// Verify we obtain a cache HIT the 2nd time
103137
$testStoreResponseHit = $this->assertCacheHitAndReturnResponse(
104138
$query,
105139
[
@@ -111,13 +145,63 @@ public function testGetStoreConfig(): void
111145
$testStoreResponseHitResult = $testStoreResponseHit['body']['storeConfig'];
112146
$this->assertEquals($testStoreCode, $testStoreResponseHitResult['code']);
113147
$this->assertEquals($defaultLocale, $testStoreResponseHitResult['locale']);
148+
}
149+
150+
/**
151+
* Store scoped config change triggers purging only the cache of the changed store.
152+
*
153+
* @magentoConfigFixture default/system/full_page_cache/caching_application 2
154+
* @magentoApiDataFixture Magento/Store/_files/store.php
155+
* @throws NoSuchEntityException
156+
*/
157+
public function testCachePurgedWithStoreScopeConfigChange(): void
158+
{
159+
$defaultStoreId = $this->defaultStoreConfig->getId();
160+
$defaultStoreCode = $this->defaultStoreConfig->getCode();
161+
$defaultLocale = $this->defaultStoreConfig->getLocale();
162+
$query = $this->getQuery();
163+
164+
// Query default store config
165+
$responseDefaultStore = $this->graphQlQueryWithResponseHeaders($query);
166+
$this->assertArrayHasKey(CacheIdCalculator::CACHE_ID_HEADER, $responseDefaultStore['headers']);
167+
$defaultStoreCacheId = $responseDefaultStore['headers'][CacheIdCalculator::CACHE_ID_HEADER];
168+
// Verify we obtain a cache MISS the 1st time
169+
$defaultStoreResponse = $this->assertCacheMissAndReturnResponse(
170+
$query,
171+
[CacheIdCalculator::CACHE_ID_HEADER => $defaultStoreCacheId]
172+
);
173+
$this->assertArrayHasKey('storeConfig', $defaultStoreResponse['body']);
174+
$defaultStoreResponseResult = $defaultStoreResponse['body']['storeConfig'];
175+
$this->assertEquals($defaultStoreId, $defaultStoreResponseResult['id']);
176+
$this->assertEquals($defaultStoreCode, $defaultStoreResponseResult['code']);
177+
$this->assertEquals($defaultLocale, $defaultStoreResponseResult['locale']);
178+
179+
// Query test store config
180+
$testStoreCode = 'test';
181+
$responseTestStore = $this->graphQlQueryWithResponseHeaders($query, [], '', ['Store' => $testStoreCode]);
182+
$this->assertArrayHasKey(CacheIdCalculator::CACHE_ID_HEADER, $responseTestStore['headers']);
183+
$testStoreCacheId = $responseTestStore['headers'][CacheIdCalculator::CACHE_ID_HEADER];
184+
$this->assertNotEquals($testStoreCacheId, $defaultStoreCacheId);
185+
// Verify we obtain a cache MISS the 1st time
186+
$testStoreResponse = $this->assertCacheMissAndReturnResponse(
187+
$query,
188+
[
189+
CacheIdCalculator::CACHE_ID_HEADER => $testStoreCacheId,
190+
'Store' => $testStoreCode
191+
]
192+
);
193+
$this->assertArrayHasKey('storeConfig', $testStoreResponse['body']);
194+
$testStoreResponseResult = $testStoreResponse['body']['storeConfig'];
195+
$this->assertEquals($testStoreCode, $testStoreResponseResult['code']);
196+
$this->assertEquals($defaultLocale, $testStoreResponseResult['locale']);
114197

115198
// Change test store locale
199+
$localeConfigPath = 'general/locale/code';
116200
$newLocale = 'de_DE';
117-
$this->setConfig('general/locale/code', $newLocale, ScopeInterface::SCOPE_STORES, $testStoreCode);
201+
$this->setConfig($localeConfigPath, $newLocale, ScopeInterface::SCOPE_STORE, $testStoreCode);
118202

119203
// Query default store config after test store config change
120-
// Verify we obtain a cache HIT the 3rd time
204+
// Verify we obtain a cache HIT the 2nd time, the cache is not purged
121205
$defaultStoreResponseHit2 = $this->assertCacheHitAndReturnResponse(
122206
$query,
123207
[CacheIdCalculator::CACHE_ID_HEADER => $defaultStoreCacheId]
@@ -129,7 +213,7 @@ public function testGetStoreConfig(): void
129213
$this->assertEquals($defaultLocale, $defaultStoreResponseHit2Result['locale']);
130214

131215
// Query test store config after test store config change
132-
// Verify we obtain a cache MISS the 3rd time
216+
// Verify we obtain a cache MISS the 2nd time, the cache is purged
133217
$testStoreResponseMiss = $this->assertCacheMissAndReturnResponse(
134218
$query,
135219
[
@@ -141,7 +225,7 @@ public function testGetStoreConfig(): void
141225
$testStoreResponseMissResult = $testStoreResponseMiss['body']['storeConfig'];
142226
$this->assertEquals($testStoreCode, $testStoreResponseMissResult['code']);
143227
$this->assertEquals($newLocale, $testStoreResponseMissResult['locale']);
144-
// Verify we obtain a cache HIT the 4th time
228+
// Verify we obtain a cache HIT the 3rd time
145229
$testStoreResponseHit2 = $this->assertCacheHitAndReturnResponse(
146230
$query,
147231
[
@@ -198,25 +282,63 @@ private function getQuery(): string
198282
return $query;
199283
}
200284

285+
protected function tearDown(): void
286+
{
287+
$this->restoreConfig();
288+
parent::tearDown();
289+
}
290+
201291
/**
202292
* Set configuration
203293
*
204294
* @param string $path
205295
* @param string $value
206-
* @param string|null $scope
296+
* @param string $scopeType
207297
* @param string|null $scopeCode
208298
* @return void
209-
* @throws \Magento\Framework\Exception\LocalizedException
210299
*/
211-
private function setConfig(string $path, string $value, ?string $scope = null, ?string $scopeCode = null) : void
300+
private function setConfig(
301+
string $path,
302+
string $value,
303+
string $scopeType,
304+
?string $scopeCode = null
305+
): void {
306+
if ($this->configStorage->checkIsRecordExist($path, $scopeType, $scopeCode)) {
307+
$this->origConfigs[] = [
308+
'path' => $path,
309+
'value' => $this->configStorage->getValueFromDb($path, $scopeType, $scopeCode),
310+
'scopeType' => $scopeType,
311+
'scopeCode' => $scopeCode
312+
];
313+
} else {
314+
$this->notExistingOrigConfigs[] = [
315+
'path' => $path,
316+
'scopeType' => $scopeType,
317+
'scopeCode' => $scopeCode
318+
];
319+
}
320+
$this->config->setValue($path, $value, $scopeType, $scopeCode);
321+
}
322+
323+
private function restoreConfig()
212324
{
213-
$options = '';
214-
$options .= $scope ? "--scope=$scope " : '';
215-
$options .= $scopeCode ? "--scope-code=$scopeCode " : '';
216-
$options .= "$path $value";
217-
$appDir = dirname(Bootstrap::getInstance()->getAppTempDir());
218-
$out = '';
219-
// phpcs:ignore Magento2.Security.InsecureFunction
220-
exec("php -f {$appDir}/bin/magento config:set $options", $out);
325+
foreach ($this->origConfigs as $origConfig) {
326+
$this->config->setValue(
327+
$origConfig['path'],
328+
$origConfig['value'],
329+
$origConfig['scopeType'],
330+
$origConfig['scopeCode']
331+
);
332+
}
333+
$this->origConfigs = [];
334+
335+
foreach ($this->notExistingOrigConfigs as $notExistingOrigConfig) {
336+
$this->configStorage->deleteConfigFromDb(
337+
$notExistingOrigConfig['path'],
338+
$notExistingOrigConfig['scopeType'],
339+
$notExistingOrigConfig['scopeCode']
340+
);
341+
}
342+
$this->notExistingOrigConfigs = [];
221343
}
222344
}

dev/tests/integration/framework/Magento/TestFramework/Config/Model/ConfigStorage.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,4 +120,22 @@ private function normalizeScope(string $scope): string
120120

121121
return $scope;
122122
}
123+
124+
/**
125+
* Delete configuration from db
126+
*
127+
* @param string $path
128+
* @param string $scope
129+
* @param string|null $scopeCode
130+
* @return void
131+
*/
132+
public function deleteConfigFromDb(
133+
string $path,
134+
string $scope = ScopeConfigInterface::SCOPE_TYPE_DEFAULT,
135+
?string $scopeCode = null
136+
) {
137+
$scope = $this->normalizeScope($scope);
138+
$scopeId = $this->getIdByScope($scope, $scopeCode);
139+
$this->configResource->deleteConfig($path, $scope, $scopeId);
140+
}
123141
}

0 commit comments

Comments
 (0)