Skip to content

Commit daedb68

Browse files
committed
fix: system config webhook check
fixes shopware/shopware#7537
1 parent 31224c3 commit daedb68

File tree

4 files changed

+268
-11
lines changed

4 files changed

+268
-11
lines changed

src/Webhook/Registration/WebhookSubscriber.php

+15-7
Original file line numberDiff line numberDiff line change
@@ -62,33 +62,41 @@ public function removeSalesChannelWebhookConfiguration(EntityDeletedEvent $event
6262

6363
/**
6464
* system-config should be written by {@see SettingsSaver} only, which checks the webhook on its own.
65-
* Just in case new/changed credentials will be saved via the normal system config save route.
65+
* Just in case new/changed credentials will be saved via the normal system config.
6666
*/
6767
public function checkWebhookBefore(BeforeSystemConfigMultipleChangedEvent $event): void
6868
{
6969
$routeName = (string) $this->requestStack->getMainRequest()?->attributes->getString('_route');
7070

71-
if (!\str_contains($routeName, 'api.action.core.save.system-config')) {
71+
if (\str_contains($routeName, 'api.action.paypal.settings.save')) {
7272
return;
7373
}
7474

75-
/** @var array<string, array<string, mixed>> $config */
75+
/** @var array<string, mixed> $config */
7676
$config = $event->getConfig();
77-
$this->webhookSystemConfigHelper->checkWebhookBefore($config);
77+
78+
if ($this->webhookSystemConfigHelper->needsCheck($config)) {
79+
$this->webhookSystemConfigHelper->checkWebhookBefore([$event->getSalesChannelId() => $config]);
80+
}
7881
}
7982

8083
/**
8184
* system-config should be written by {@see SettingsSaver} only, which checks the webhook on its own.
82-
* Just in case new/changed credentials will be saved via the normal system config save route.
85+
* Just in case new/changed credentials will be saved via the normal system config.
8386
*/
8487
public function checkWebhookAfter(SystemConfigMultipleChangedEvent $event): void
8588
{
8689
$routeName = (string) $this->requestStack->getMainRequest()?->attributes->getString('_route');
8790

88-
if (!\str_contains($routeName, 'api.action.core.save.system-config')) {
91+
if (\str_contains($routeName, 'api.action.paypal.settings.save')) {
8992
return;
9093
}
9194

92-
$this->webhookSystemConfigHelper->checkWebhookAfter(\array_keys($event->getConfig()));
95+
/** @var array<string, mixed> $config */
96+
$config = $event->getConfig();
97+
98+
if ($this->webhookSystemConfigHelper->needsCheck($config)) {
99+
$this->webhookSystemConfigHelper->checkWebhookAfter([$event->getSalesChannelId()]);
100+
}
93101
}
94102
}

src/Webhook/Registration/WebhookSystemConfigHelper.php

+10-2
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public function checkWebhookBefore(array $newData): array
5959
$errors = [];
6060

6161
foreach ($newData as $salesChannelId => $newSettings) {
62-
if ($salesChannelId === 'null') {
62+
if (!$salesChannelId || $salesChannelId === 'null') {
6363
$salesChannelId = null;
6464
}
6565

@@ -102,7 +102,7 @@ public function checkWebhookAfter(array $salesChannelIds): array
102102
$errors = [];
103103

104104
foreach ($salesChannelIds as $salesChannelId) {
105-
if ($salesChannelId === 'null') {
105+
if (!$salesChannelId || $salesChannelId === 'null') {
106106
$salesChannelId = null;
107107
}
108108

@@ -129,6 +129,14 @@ public function checkWebhookAfter(array $salesChannelIds): array
129129
return $errors;
130130
}
131131

132+
/**
133+
* @param array<string, mixed> $config
134+
*/
135+
public function needsCheck(array $config): bool
136+
{
137+
return !empty($this->filterSettings($config));
138+
}
139+
132140
private function fetchSettings(?string $salesChannelId, bool $inherit = false): array
133141
{
134142
$settings = [];

tests/Webhook/Registration/WebhookSubscriberTest.php

+160-2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
use Swag\PayPal\Webhook\Registration\WebhookSystemConfigHelper;
3030
use Swag\PayPal\Webhook\WebhookRegistry;
3131
use Swag\PayPal\Webhook\WebhookService;
32+
use Symfony\Component\HttpFoundation\Request;
3233
use Symfony\Component\HttpFoundation\RequestStack;
3334
use Symfony\Component\Routing\RouterInterface;
3435

@@ -44,9 +45,15 @@ class WebhookSubscriberTest extends TestCase
4445

4546
private SystemConfigService $systemConfigService;
4647

48+
private WebhookSystemConfigHelper $webhookSystemConfigHelper;
49+
50+
private RequestStack $requestStack;
51+
4752
protected function setUp(): void
4853
{
4954
$this->systemConfigService = SystemConfigServiceMock::createWithCredentials();
55+
$this->webhookSystemConfigHelper = $this->createMock(WebhookSystemConfigHelper::class);
56+
$this->requestStack = new RequestStack();
5057
}
5158

5259
public function testRemoveWebhookWithInheritedConfiguration(): void
@@ -73,6 +80,157 @@ public function testRemoveWebhookWithNoConfiguration(): void
7380
static::assertEmpty($this->systemConfigService->getString(Settings::WEBHOOK_ID, TestDefaults::SALES_CHANNEL));
7481
}
7582

83+
public function testCheckWebhookBefore(): void
84+
{
85+
$event = new BeforeSystemConfigMultipleChangedEvent(['some-key' => 'some-value'], null);
86+
87+
$this->webhookSystemConfigHelper
88+
->expects(static::once())
89+
->method('checkWebhookBefore')
90+
->with(['' => ['some-key' => 'some-value']]);
91+
92+
$this->webhookSystemConfigHelper
93+
->expects(static::once())
94+
->method('needsCheck')
95+
->with(['some-key' => 'some-value'])
96+
->willReturn(true);
97+
98+
$this->createWebhookSubscriber(['' => null, TestDefaults::SALES_CHANNEL => null])
99+
->checkWebhookBefore($event);
100+
}
101+
102+
public function testCheckWebhookBeforeWithSalesChannelId(): void
103+
{
104+
$event = new BeforeSystemConfigMultipleChangedEvent(['some-key' => 'some-value'], 'some-sales-channel-id');
105+
106+
$this->webhookSystemConfigHelper
107+
->expects(static::once())
108+
->method('checkWebhookBefore')
109+
->with(['some-sales-channel-id' => ['some-key' => 'some-value']]);
110+
111+
$this->webhookSystemConfigHelper
112+
->expects(static::once())
113+
->method('needsCheck')
114+
->with(['some-key' => 'some-value'])
115+
->willReturn(true);
116+
117+
$this->createWebhookSubscriber(['' => null, TestDefaults::SALES_CHANNEL => null])
118+
->checkWebhookBefore($event);
119+
}
120+
121+
public function testCheckWebhookBeforeWithSaveRoute(): void
122+
{
123+
$request = new Request(attributes: ['_route' => 'api.action.paypal.settings.save']);
124+
$this->requestStack->push($request);
125+
126+
$event = new BeforeSystemConfigMultipleChangedEvent(['some-key' => 'some-value'], 'some-sales-channel-id');
127+
128+
$this->webhookSystemConfigHelper
129+
->expects(static::never())
130+
->method('checkWebhookBefore');
131+
132+
$this->webhookSystemConfigHelper
133+
->expects(static::never())
134+
->method('needsCheck');
135+
136+
$this->createWebhookSubscriber(['' => null, TestDefaults::SALES_CHANNEL => null])
137+
->checkWebhookBefore($event);
138+
}
139+
140+
public function testCheckWebhookBeforeWithNoCheckNeeded(): void
141+
{
142+
$event = new BeforeSystemConfigMultipleChangedEvent(['some-key' => 'some-value'], 'some-sales-channel-id');
143+
144+
$this->webhookSystemConfigHelper
145+
->expects(static::never())
146+
->method('checkWebhookBefore');
147+
148+
$this->webhookSystemConfigHelper
149+
->expects(static::once())
150+
->method('needsCheck')
151+
->with(['some-key' => 'some-value'])
152+
->willReturn(false);
153+
154+
$this->createWebhookSubscriber(['' => null, TestDefaults::SALES_CHANNEL => null])
155+
->checkWebhookBefore($event);
156+
}
157+
158+
public function testCheckWebhookAfter(): void
159+
{
160+
$event = new SystemConfigMultipleChangedEvent(['some-key' => 'some-value'], null);
161+
162+
$this->webhookSystemConfigHelper
163+
->expects(static::once())
164+
->method('checkWebhookAfter')
165+
->with(['']);
166+
167+
$this->webhookSystemConfigHelper
168+
->expects(static::once())
169+
->method('needsCheck')
170+
->with(['some-key' => 'some-value'])
171+
->willReturn(true);
172+
173+
$this->createWebhookSubscriber(['' => null, TestDefaults::SALES_CHANNEL => null])
174+
->checkWebhookAfter($event);
175+
}
176+
177+
public function testCheckWebhookAfterWithSalesChannelId(): void
178+
{
179+
$event = new SystemConfigMultipleChangedEvent(['some-key' => 'some-value'], 'some-sales-channel-id');
180+
181+
$this->webhookSystemConfigHelper
182+
->expects(static::once())
183+
->method('checkWebhookAfter')
184+
->with(['some-sales-channel-id']);
185+
186+
$this->webhookSystemConfigHelper
187+
->expects(static::once())
188+
->method('needsCheck')
189+
->with(['some-key' => 'some-value'])
190+
->willReturn(true);
191+
192+
$this->createWebhookSubscriber(['' => null, TestDefaults::SALES_CHANNEL => null])
193+
->checkWebhookAfter($event);
194+
}
195+
196+
public function testCheckWebhookAfterWithSaveRoute(): void
197+
{
198+
$request = new Request(attributes: ['_route' => 'api.action.paypal.settings.save']);
199+
$this->requestStack->push($request);
200+
201+
$event = new SystemConfigMultipleChangedEvent(['some-key' => 'some-value'], 'some-sales-channel-id');
202+
203+
$this->webhookSystemConfigHelper
204+
->expects(static::once())
205+
->method('checkWebhookAfter')
206+
->with(['some-sales-channel-id']);
207+
208+
$this->webhookSystemConfigHelper
209+
->expects(static::never())
210+
->method('needsCheck');
211+
212+
$this->createWebhookSubscriber(['' => null, TestDefaults::SALES_CHANNEL => null])
213+
->checkWebhookAfter($event);
214+
}
215+
216+
public function testCheckWebhookAfterWithNoCheckNeeded(): void
217+
{
218+
$event = new SystemConfigMultipleChangedEvent(['some-key' => 'some-value'], 'some-sales-channel-id');
219+
220+
$this->webhookSystemConfigHelper
221+
->expects(static::never())
222+
->method('checkWebhookAfter');
223+
224+
$this->webhookSystemConfigHelper
225+
->expects(static::once())
226+
->method('needsCheck')
227+
->with(['some-key' => 'some-value'])
228+
->willReturn(false);
229+
230+
$this->createWebhookSubscriber(['' => null, TestDefaults::SALES_CHANNEL => null])
231+
->checkWebhookBefore($event);
232+
}
233+
76234
public function testSubscribedEvents(): void
77235
{
78236
static::assertEqualsCanonicalizing([
@@ -105,8 +263,8 @@ private function createWebhookSubscriber(array $configuration): WebhookSubscribe
105263
new NullLogger(),
106264
$this->systemConfigService,
107265
$webhookService,
108-
$this->createMock(WebhookSystemConfigHelper::class),
109-
new RequestStack(),
266+
$this->webhookSystemConfigHelper,
267+
$this->requestStack,
110268
);
111269
}
112270

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<?php declare(strict_types=1);
2+
/*
3+
* (c) shopware AG <[email protected]>
4+
* For the full copyright and license information, please view the LICENSE
5+
* file that was distributed with this source code.
6+
*/
7+
8+
namespace Swag\PayPal\Test\Webhook\Registration;
9+
10+
use PHPUnit\Framework\Attributes\CoversClass;
11+
use PHPUnit\Framework\Attributes\DataProvider;
12+
use PHPUnit\Framework\TestCase;
13+
use Psr\Log\NullLogger;
14+
use Shopware\Core\Framework\Log\Package;
15+
use Shopware\Core\System\SystemConfig\SystemConfigService;
16+
use Swag\PayPal\Setting\Service\SettingsValidationServiceInterface;
17+
use Swag\PayPal\Setting\Settings;
18+
use Swag\PayPal\Webhook\Registration\WebhookSystemConfigHelper;
19+
use Swag\PayPal\Webhook\WebhookServiceInterface;
20+
21+
/**
22+
* @internal
23+
*/
24+
#[Package('checkout')]
25+
#[CoversClass(WebhookSystemConfigHelper::class)]
26+
class WebhookSystemConfigHelperTest extends TestCase
27+
{
28+
private const WEBHOOK_KEYS = [
29+
Settings::CLIENT_ID,
30+
Settings::CLIENT_SECRET,
31+
Settings::CLIENT_ID_SANDBOX,
32+
Settings::CLIENT_SECRET_SANDBOX,
33+
Settings::SANDBOX,
34+
Settings::WEBHOOK_ID,
35+
];
36+
37+
private WebhookSystemConfigHelper $helper;
38+
39+
protected function setUp(): void
40+
{
41+
$this->helper = new WebhookSystemConfigHelper(
42+
new NullLogger(),
43+
$this->createMock(WebhookServiceInterface::class),
44+
$this->createMock(SystemConfigService::class),
45+
$this->createMock(SettingsValidationServiceInterface::class),
46+
);
47+
}
48+
49+
/**
50+
* @param array<string, mixed> $config
51+
*/
52+
#[DataProvider('providerNeedsCheck')]
53+
public function testNeedsCheck(bool $expected, array $config): void
54+
{
55+
static::assertSame($expected, $this->helper->needsCheck($config));
56+
}
57+
58+
public static function providerNeedsCheck(): \Generator
59+
{
60+
foreach (self::WEBHOOK_KEYS as $key) {
61+
yield \sprintf('needs check for "%s"', $key) => [
62+
true,
63+
[$key => 'some-value'],
64+
];
65+
66+
yield \sprintf('needs check for "%s" with null value', $key) => [
67+
true,
68+
[$key => null],
69+
];
70+
}
71+
72+
foreach (Settings::DEFAULT_VALUES as $key => $value) {
73+
if (\in_array($key, self::WEBHOOK_KEYS, true)) {
74+
continue;
75+
}
76+
77+
yield \sprintf('no check for "%s"', $key) => [
78+
false,
79+
[$key => $value],
80+
];
81+
}
82+
}
83+
}

0 commit comments

Comments
 (0)