Skip to content

Commit d8ee1e0

Browse files
authored
Merge branch '2.4-develop' into AC-1685
2 parents c58195c + 00cc5b4 commit d8ee1e0

File tree

15 files changed

+720
-36
lines changed

15 files changed

+720
-36
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?xml version="1.0"?>
2+
<!--
3+
/**
4+
* Representation of Webapi module in System Configuration (Magento admin panel).
5+
*
6+
* Copyright © Magento, Inc. All rights reserved.
7+
* See COPYING.txt for license details.
8+
*/
9+
-->
10+
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
11+
<system>
12+
<section id="webapi">
13+
<group id="graphql_validation" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
14+
<label>GraphQl Input Limits</label>
15+
<field id="input_limit_enabled" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1">
16+
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
17+
<label>Enable Input Limits</label>
18+
<config_path>graphql/validation/input_limit_enabled</config_path>
19+
</field>
20+
<field id="maximum_page_size" translate="label comment" type="text" sortOrder="15" showInDefault="1" showInWebsite="1" showInStore="1">
21+
<label>Maximum Page Size</label>
22+
<comment>Maximum number of items allowed in a paginated search result.</comment>
23+
<config_path>graphql/validation/maximum_page_size</config_path>
24+
<depends>
25+
<field id="input_limit_enabled">1</field>
26+
</depends>
27+
</field>
28+
</group>
29+
</section>
30+
</system>
31+
</config>
32+

app/code/Magento/Webapi/etc/adminhtml/system.xml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,31 @@
2020
<comment>If empty, UTF-8 will be used.</comment>
2121
</field>
2222
</group>
23+
<group id="validation" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
24+
<label>Web Api Input Limits</label>
25+
<field id="input_limit_enabled" translate="label" type="select" sortOrder="5" showInDefault="1" showInWebsite="1" showInStore="1">
26+
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
27+
<label>Enable Input Limits</label>
28+
</field>
29+
<field id="complex_array_limit" translate="label comment" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
30+
<label>Input List Limit</label>
31+
<comment>Maximum number of items allowed in an entity's array property.</comment>
32+
<depends>
33+
<field id="input_limit_enabled">1</field>
34+
</depends>
35+
</field>
36+
<field id="maximum_page_size" translate="label comment" type="text" sortOrder="15" showInDefault="1" showInWebsite="1" showInStore="1">
37+
<label>Maximum Page Size</label>
38+
<comment>Maximum number of items allowed in a paginated search result.</comment>
39+
<depends>
40+
<field id="input_limit_enabled">1</field>
41+
</depends>
42+
</field>
43+
<field id="default_page_size" translate="label comment" type="text" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1">
44+
<label>Default Page Size</label>
45+
<comment>Default number of items a paginated search result.</comment>
46+
</field>
47+
</group>
2348
</section>
2449
</system>
2550
</config>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
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\Framework\GraphQl\Query\Resolver\Argument\Validator\IOLimit;
10+
11+
use Magento\Framework\App\Config\ScopeConfigInterface;
12+
use Magento\Store\Model\ScopeInterface;
13+
14+
/**
15+
* Provides configuration related to the GraphQL input limit validation
16+
*/
17+
class IOLimitConfigProvider
18+
{
19+
/**
20+
* Path to the configuration setting for if the input limiting is enabled
21+
*/
22+
public const CONFIG_PATH_INPUT_LIMIT_ENABLED = 'graphql/validation/input_limit_enabled';
23+
24+
/**
25+
* Path to the configuration setting for maximum page size allowed
26+
*/
27+
public const CONFIG_PATH_MAXIMUM_PAGE_SIZE = 'graphql/validation/maximum_page_size';
28+
29+
/**
30+
* @var ScopeConfigInterface
31+
*/
32+
private $scopeConfig;
33+
34+
/**
35+
* @param ScopeConfigInterface $scopeConfig
36+
*/
37+
public function __construct(ScopeConfigInterface $scopeConfig)
38+
{
39+
$this->scopeConfig = $scopeConfig;
40+
}
41+
42+
/**
43+
* Get the stored configuration for if the input limiting is enabled
44+
*/
45+
public function isInputLimitingEnabled(): bool
46+
{
47+
return $this->scopeConfig->isSetFlag(
48+
self::CONFIG_PATH_INPUT_LIMIT_ENABLED,
49+
ScopeInterface::SCOPE_STORE
50+
);
51+
}
52+
53+
/**
54+
* Get the stored configuration for the maximum page size
55+
*/
56+
public function getMaximumPageSize(): ?int
57+
{
58+
$value = $this->scopeConfig->getValue(
59+
self::CONFIG_PATH_MAXIMUM_PAGE_SIZE,
60+
ScopeInterface::SCOPE_STORE
61+
);
62+
return isset($value) ? (int)$value : null;
63+
}
64+
}

lib/internal/Magento/Framework/GraphQl/Query/Resolver/Argument/Validator/SearchCriteriaValidator.php

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@
88

99
namespace Magento\Framework\GraphQl\Query\Resolver\Argument\Validator;
1010

11+
use Magento\Framework\App\ObjectManager;
1112
use Magento\Framework\GraphQl\Config\Element\Field;
1213
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
14+
use Magento\Framework\GraphQl\Query\Resolver\Argument\Validator\IOLimit\IOLimitConfigProvider;
1315
use Magento\Framework\GraphQl\Query\Resolver\Argument\ValidatorInterface;
1416

1517
/**
@@ -22,22 +24,36 @@ class SearchCriteriaValidator implements ValidatorInterface
2224
*/
2325
private $maxPageSize;
2426

27+
/**
28+
* @var IOLimitConfigProvider|null
29+
*/
30+
private $configProvider;
31+
2532
/**
2633
* @param int $maxPageSize
34+
* @param IOLimitConfigProvider|null $configProvider
2735
*/
28-
public function __construct(int $maxPageSize)
36+
public function __construct(int $maxPageSize, ?IOLimitConfigProvider $configProvider = null)
2937
{
3038
$this->maxPageSize = $maxPageSize;
39+
$this->configProvider = $configProvider ?? ObjectManager::getInstance()
40+
->get(IOLimitConfigProvider::class);
3141
}
3242

3343
/**
3444
* @inheritDoc
3545
*/
3646
public function validate(Field $field, $args): void
3747
{
38-
if (isset($args['pageSize']) && $args['pageSize'] > $this->maxPageSize) {
48+
if (!$this->configProvider->isInputLimitingEnabled()) {
49+
return;
50+
}
51+
52+
$max = $this->configProvider->getMaximumPageSize() ?? $this->maxPageSize;
53+
54+
if (isset($args['pageSize']) && $args['pageSize'] > $max) {
3955
throw new GraphQlInputException(
40-
__("Maximum pageSize is %max", ['max' => $this->maxPageSize])
56+
__("Maximum pageSize is %max", ['max' => $max])
4157
);
4258
}
4359
}

lib/internal/Magento/Framework/GraphQl/Test/Unit/Query/Resolver/Argument/Validator/SearchCriteriaValidatorTest.php

Lines changed: 73 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,34 +10,102 @@
1010

1111
use Magento\Framework\GraphQl\Config\Element\Field;
1212
use Magento\Framework\GraphQl\Exception\GraphQlInputException;
13+
use Magento\Framework\GraphQl\Query\Resolver\Argument\Validator\IOLimit\IOLimitConfigProvider;
1314
use Magento\Framework\GraphQl\Query\Resolver\Argument\Validator\SearchCriteriaValidator;
15+
use PHPUnit\Framework\MockObject\MockObject;
1416
use PHPUnit\Framework\TestCase;
1517

1618
/**
1719
* Verify behavior of graphql search criteria validator
1820
*/
1921
class SearchCriteriaValidatorTest extends TestCase
2022
{
23+
/**
24+
* @var IOLimitConfigProvider|MockObject
25+
*/
26+
private $configProvider;
27+
28+
/**
29+
* @var SearchCriteriaValidator
30+
*/
31+
private $validator;
32+
33+
protected function setUp(): void
34+
{
35+
$this->configProvider = self::getMockBuilder(IOLimitConfigProvider::class)
36+
->disableOriginalConstructor()
37+
->getMock();
38+
$this->validator = new SearchCriteriaValidator(3, $this->configProvider);
39+
}
40+
41+
/**
42+
* @doesNotPerformAssertions
43+
*/
44+
public function testValidValueWithDisabledDefaultConfig()
45+
{
46+
$this->configProvider->method('isInputLimitingEnabled')
47+
->willReturn(false);
48+
$field = self::getMockBuilder(Field::class)
49+
->disableOriginalConstructor()
50+
->getMock();
51+
$this->validator->validate($field, ['pageSize' => 50]);
52+
}
53+
2154
/**
2255
* @doesNotPerformAssertions
2356
*/
2457
public function testValidValue()
2558
{
26-
$validator = new SearchCriteriaValidator(3);
59+
$this->configProvider->method('isInputLimitingEnabled')
60+
->willReturn(false);
2761
$field = self::getMockBuilder(Field::class)
2862
->disableOriginalConstructor()
2963
->getMock();
30-
$validator->validate($field, ['pageSize' => 3]);
64+
$this->validator->validate($field, ['pageSize' => 3]);
3165
}
3266

33-
public function testValidInvalidMaxValue()
67+
/**
68+
* @doesNotPerformAssertions
69+
*/
70+
public function testValidValueWithConfig()
71+
{
72+
$this->configProvider->method('isInputLimitingEnabled')
73+
->willReturn(true);
74+
$this->configProvider->method('getMaximumPageSize')
75+
->willReturn(10);
76+
77+
$field = self::getMockBuilder(Field::class)
78+
->disableOriginalConstructor()
79+
->getMock();
80+
$this->validator->validate($field, ['pageSize' => 10]);
81+
}
82+
83+
public function testInvalidMaxValue()
3484
{
3585
$this->expectException(GraphQlInputException::class);
3686
$this->expectExceptionMessage("Maximum pageSize is 3");
37-
$validator = new SearchCriteriaValidator(3);
87+
88+
$this->configProvider->method('isInputLimitingEnabled')
89+
->willReturn(true);
90+
$field = self::getMockBuilder(Field::class)
91+
->disableOriginalConstructor()
92+
->getMock();
93+
$this->validator->validate($field, ['pageSize' => 4]);
94+
}
95+
96+
public function testInvalidValueWithConfig()
97+
{
98+
$this->expectException(GraphQlInputException::class);
99+
$this->expectExceptionMessage("Maximum pageSize is 10");
100+
101+
$this->configProvider->method('isInputLimitingEnabled')
102+
->willReturn(true);
103+
$this->configProvider->method('getMaximumPageSize')
104+
->willReturn(10);
105+
38106
$field = self::getMockBuilder(Field::class)
39107
->disableOriginalConstructor()
40108
->getMock();
41-
$validator->validate($field, ['pageSize' => 4]);
109+
$this->validator->validate($field, ['pageSize' => 11]);
42110
}
43111
}

lib/internal/Magento/Framework/Webapi/ServiceInputProcessor.php

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
<?php
22
/**
3-
* Service Input Processor
4-
*
53
* Copyright © Magento, Inc. All rights reserved.
64
* See COPYING.txt for license details.
75
*/
@@ -24,6 +22,7 @@
2422
use Magento\Framework\Webapi\Exception as WebapiException;
2523
use Magento\Framework\Webapi\CustomAttribute\PreprocessorInterface;
2624
use Laminas\Code\Reflection\ClassReflection;
25+
use Magento\Framework\Webapi\Validator\IOLimit\DefaultPageSizeSetter;
2726
use Magento\Framework\Webapi\Validator\ServiceInputValidatorInterface;
2827

2928
/**
@@ -35,7 +34,7 @@
3534
*/
3635
class ServiceInputProcessor implements ServicePayloadConverterInterface
3736
{
38-
const EXTENSION_ATTRIBUTES_TYPE = \Magento\Framework\Api\ExtensionAttributesInterface::class;
37+
public const EXTENSION_ATTRIBUTES_TYPE = \Magento\Framework\Api\ExtensionAttributesInterface::class;
3938

4039
/**
4140
* @var \Magento\Framework\Reflection\TypeProcessor
@@ -97,6 +96,11 @@ class ServiceInputProcessor implements ServicePayloadConverterInterface
9796
*/
9897
private $defaultPageSize;
9998

99+
/**
100+
* @var DefaultPageSizeSetter|null
101+
*/
102+
private $defaultPageSizeSetter;
103+
100104
/**
101105
* Initialize dependencies.
102106
*
@@ -110,6 +114,8 @@ class ServiceInputProcessor implements ServicePayloadConverterInterface
110114
* @param array $customAttributePreprocessors
111115
* @param ServiceInputValidatorInterface|null $serviceInputValidator
112116
* @param int $defaultPageSize
117+
* @param DefaultPageSizeSetter|null $defaultPageSizeSetter
118+
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
113119
*/
114120
public function __construct(
115121
TypeProcessor $typeProcessor,
@@ -121,7 +127,8 @@ public function __construct(
121127
ConfigInterface $config = null,
122128
array $customAttributePreprocessors = [],
123129
ServiceInputValidatorInterface $serviceInputValidator = null,
124-
int $defaultPageSize = 20
130+
int $defaultPageSize = 20,
131+
?DefaultPageSizeSetter $defaultPageSizeSetter = null
125132
) {
126133
$this->typeProcessor = $typeProcessor;
127134
$this->objectManager = $objectManager;
@@ -136,6 +143,8 @@ public function __construct(
136143
$this->serviceInputValidator = $serviceInputValidator
137144
?: ObjectManager::getInstance()->get(ServiceInputValidatorInterface::class);
138145
$this->defaultPageSize = $defaultPageSize >= 10 ? $defaultPageSize : 10;
146+
$this->defaultPageSizeSetter = $defaultPageSizeSetter ?? ObjectManager::getInstance()
147+
->get(DefaultPageSizeSetter::class);
139148
}
140149

141150
/**
@@ -309,10 +318,8 @@ protected function _createFromArray($className, $data)
309318
}
310319
}
311320

312-
if ($object instanceof SearchCriteriaInterface
313-
&& $object->getPageSize() === null
314-
) {
315-
$object->setPageSize($this->defaultPageSize);
321+
if ($object instanceof SearchCriteriaInterface) {
322+
$this->defaultPageSizeSetter->processSearchCriteria($object, $this->defaultPageSize);
316323
}
317324

318325
return $object;

0 commit comments

Comments
 (0)