Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public function getConfigTreeBuilder(): TreeBuilder
->canBeDisabled()
->children()
->enumNode('storage_engine')
->values(array('redis','memcache','doctrine', 'php_redis', 'php_redis_cluster', 'simple_cache', 'cache'))
->values(['redis','memcache','doctrine', 'php_redis', 'php_redis_cluster', 'simple_cache', 'cache'])
->defaultValue('redis')
->info('The storage engine where all the rates will be stored')
->end()
Expand Down Expand Up @@ -109,19 +109,20 @@ public function getConfigTreeBuilder(): TreeBuilder
->end()
->end()
->arrayNode('path_limits')
->defaultValue(array())
->defaultValue([])
->info('Rate limits for paths')
->prototype('array')
->children()
->scalarNode('path')
->isRequired()
->cannotBeEmpty()
->end()
->arrayNode('methods')
->prototype('enum')
->values(array('*', 'GET', 'POST', 'PUT', 'DELETE', 'PATCH'))
->values(['*', 'GET', 'POST', 'PUT', 'DELETE', 'PATCH'])
->end()
->requiresAtLeastOneElement()
->defaultValue(array('*'))
->defaultValue(['*'])
->end()
->integerNode('limit')
->isRequired()
Expand Down
58 changes: 37 additions & 21 deletions Tests/DependencyInjection/ConfigurationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,23 @@
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
use Symfony\Component\Config\Definition\Processor;

/**
* ConfigurationTest
*/
class ConfigurationTest extends WebTestCase
{
/**
* @var Processor
*/
private $processor;
private Processor $processor;

public function setUp():void
{
$this->processor = new Processor();
}

private function getConfigs(array $configArray)
private function getConfigs(array $configArray): array
{
$configuration = new Configuration();

return $this->processor->processConfiguration($configuration, array($configArray));
}

public function testUnconfiguredConfiguration()
public function testUnconfiguredConfiguration(): void
{
$configuration = $this->getConfigs(array());

Expand Down Expand Up @@ -59,15 +53,15 @@ public function testUnconfiguredConfiguration()
), $configuration);
}

public function testDisabledConfiguration()
public function testDisabledConfiguration(): void
{
$configuration = $this->getConfigs(array('enabled' => false));

$this->assertArrayHasKey('enabled', $configuration);
$this->assertFalse($configuration['enabled']);
}

public function testPathLimitConfiguration()
public function testPathLimitConfiguration(): void
{
$pathLimits = array(
'api' => array(
Expand All @@ -86,7 +80,7 @@ public function testPathLimitConfiguration()
$this->assertEquals($pathLimits, $configuration['path_limits']);
}

public function testMultiplePathLimitConfiguration()
public function testMultiplePathLimitConfiguration(): void
{
$pathLimits = array(
'api' => array(
Expand All @@ -111,7 +105,7 @@ public function testMultiplePathLimitConfiguration()
$this->assertEquals($pathLimits, $configuration['path_limits']);
}

public function testDefaultPathLimitMethods()
public function testDefaultPathLimitMethods(): void
{
$pathLimits = array(
'api' => array(
Expand All @@ -137,28 +131,50 @@ public function testDefaultPathLimitMethods()
$this->assertEquals($pathLimits, $configuration['path_limits']);
}

public function testMustBeBasedOnExceptionClass()
public function testMustBeBasedOnExceptionClass(): void
{
$this->expectException(InvalidConfigurationException::class);
$configuration = $this->getConfigs(array('rate_response_exception' => '\StdClass'));
$this->getConfigs(array('rate_response_exception' => '\StdClass'));
}

/**
* @testWith [""]
* [null]
*/
public function testEmptyPathIsNotAllowed(mixed $path): void
{
$pathLimits = [
'api' => [
'path' => $path,
'methods' => ['GET'],
'limit' => 200,
'period' => 10
],
];

$this->expectException(InvalidConfigurationException::class);

$this->getConfigs([
'path_limits' => $pathLimits
]);
}

/**
*
*/
public function testMustBeBasedOnExceptionClass2()
public function testMustBeBasedOnExceptionClass2(): void
{
$configuration = $this->getConfigs(array('rate_response_exception' => '\InvalidArgumentException'));
$this->getConfigs(array('rate_response_exception' => '\InvalidArgumentException'));

# no exception triggered is ok.
$this->assertTrue(true);
$this->expectNotToPerformAssertions();
}

public function testMustBeBasedOnExceptionOrNull()
public function testMustBeBasedOnExceptionOrNull(): void
{
$configuration = $this->getConfigs(array('rate_response_exception' => null));
$this->getConfigs(array('rate_response_exception' => null));

# no exception triggered is ok.
$this->assertTrue(true);
$this->expectNotToPerformAssertions();
}
}
13 changes: 6 additions & 7 deletions Util/PathLimitProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,19 @@

class PathLimitProcessor
{
private array $pathLimits;

public function __construct(array $pathLimits)
/**
* @param array<array{path: string, methods: array<string>, limit: int<-1, max>, period: positive-int}> $pathLimits
*/
public function __construct(private array $pathLimits)
{
$this->pathLimits = $pathLimits;

// Clean up any extra slashes from the config
foreach ($this->pathLimits as &$pathLimit) {
$pathLimit['path'] = trim($pathLimit['path'], '/');
}

// Order the configs so that the most specific paths
// are matched first
usort($this->pathLimits, static function($a, $b) {
usort($this->pathLimits, static function($a, $b): int {
return substr_count($b['path'], '/') - substr_count($a['path'], '/');
});
}
Expand All @@ -44,7 +43,7 @@ public function getRateLimit(Request $request): ?RateLimit
return null;
}

public function getMatchedPath(Request $request)
public function getMatchedPath(Request $request): string
{
$path = trim($request->getPathInfo(), '/');
$method = $request->getMethod();
Expand Down
18 changes: 0 additions & 18 deletions phpstan-baseline.neon
Original file line number Diff line number Diff line change
Expand Up @@ -384,18 +384,6 @@ parameters:
count: 1
path: Service/Storage/StorageInterface.php

-
message: '#^Method Noxlogic\\RateLimitBundle\\Util\\PathLimitProcessor\:\:__construct\(\) has parameter \$pathLimits with no value type specified in iterable type array\.$#'
identifier: missingType.iterableValue
count: 1
path: Util/PathLimitProcessor.php

-
message: '#^Method Noxlogic\\RateLimitBundle\\Util\\PathLimitProcessor\:\:getMatchedPath\(\) has no return type specified\.$#'
identifier: missingType.return
count: 1
path: Util/PathLimitProcessor.php

-
message: '#^Method Noxlogic\\RateLimitBundle\\Util\\PathLimitProcessor\:\:methodMatched\(\) has parameter \$expectedMethods with no value type specified in iterable type array\.$#'
identifier: missingType.iterableValue
Expand Down Expand Up @@ -437,9 +425,3 @@ parameters:
identifier: missingType.parameter
count: 1
path: Util/PathLimitProcessor.php

-
message: '#^Property Noxlogic\\RateLimitBundle\\Util\\PathLimitProcessor\:\:\$pathLimits type has no value type specified in iterable type array\.$#'
identifier: missingType.iterableValue
count: 1
path: Util/PathLimitProcessor.php