From d3478ec9484895a8d2833449cb0f6e91a1c6cc79 Mon Sep 17 00:00:00 2001 From: Sergei Predvoditelev Date: Sat, 12 Feb 2022 20:21:55 +0300 Subject: [PATCH] First implementation (#1) --- CHANGELOG.md | 2 +- README.md | 25 ++++++------ composer.json | 33 ++++++++++++---- config/common.php | 18 +++++++++ config/params.php | 10 +++++ phpunit.xml.dist | 3 +- psalm.xml | 2 +- src/.gitkeep | 0 src/RulesContainer.php | 68 ++++++++++++++++++++++++++++++++ tests/.gitkeep | 0 tests/ConfigTest.php | 75 ++++++++++++++++++++++++++++++++++++ tests/RulesContainerTest.php | 64 ++++++++++++++++++++++++++++++ tests/Support/AuthorRule.php | 19 +++++++++ 13 files changed, 296 insertions(+), 23 deletions(-) create mode 100644 config/common.php create mode 100644 config/params.php delete mode 100644 src/.gitkeep create mode 100644 src/RulesContainer.php delete mode 100644 tests/.gitkeep create mode 100644 tests/ConfigTest.php create mode 100644 tests/RulesContainerTest.php create mode 100644 tests/Support/AuthorRule.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a8fbfe..4b37b68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -# _____ Change Log +# Yii RBAC Rules Container Change Log ## 1.0.0 under development diff --git a/README.md b/README.md index 000fff9..4e3d523 100644 --- a/README.md +++ b/README.md @@ -2,20 +2,21 @@ -

Yii _____

+

Yii RBAC Rules Container


-[![Latest Stable Version](https://poser.pugx.org/yiisoft/_____/v/stable.png)](https://packagist.org/packages/yiisoft/_____) -[![Total Downloads](https://poser.pugx.org/yiisoft/_____/downloads.png)](https://packagist.org/packages/yiisoft/_____) -[![Build status](https://github.com/yiisoft/_____/workflows/build/badge.svg)](https://github.com/yiisoft/_____/actions?query=workflow%3Abuild) -[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/yiisoft/_____/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/yiisoft/_____/?branch=master) -[![Code Coverage](https://scrutinizer-ci.com/g/yiisoft/_____/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/yiisoft/_____/?branch=master) -[![Mutation testing badge](https://img.shields.io/endpoint?style=flat&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2Fyiisoft%2F_____%2Fmaster)](https://dashboard.stryker-mutator.io/reports/github.com/yiisoft/_____/master) -[![static analysis](https://github.com/yiisoft/_____/workflows/static%20analysis/badge.svg)](https://github.com/yiisoft/_____/actions?query=workflow%3A%22static+analysis%22) -[![type-coverage](https://shepherd.dev/github/yiisoft/_____/coverage.svg)](https://shepherd.dev/github/yiisoft/_____) +[![Latest Stable Version](https://poser.pugx.org/yiisoft/rbac-rules-container/v/stable.png)](https://packagist.org/packages/yiisoft/rbac-rules-container) +[![Total Downloads](https://poser.pugx.org/yiisoft/rbac-rules-container/downloads.png)](https://packagist.org/packages/yiisoft/rbac-rules-container) +[![Build status](https://github.com/yiisoft/rbac-rules-container/workflows/build/badge.svg)](https://github.com/yiisoft/rbac-rules-container/actions?query=workflow%3Abuild) +[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/yiisoft/rbac-rules-container/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/yiisoft/rbac-rules-container/?branch=master) +[![Code Coverage](https://scrutinizer-ci.com/g/yiisoft/rbac-rules-container/badges/coverage.png?b=master)](https://scrutinizer-ci.com/g/yiisoft/rbac-rules-container/?branch=master) +[![Mutation testing badge](https://img.shields.io/endpoint?style=flat&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2Fyiisoft%2Frbac-rules-container%2Fmaster)](https://dashboard.stryker-mutator.io/reports/github.com/yiisoft/rbac-rules-container/master) +[![static analysis](https://github.com/yiisoft/rbac-rules-container/workflows/static%20analysis/badge.svg)](https://github.com/yiisoft/rbac-rules-container/actions?query=workflow%3A%22static+analysis%22) +[![type-coverage](https://shepherd.dev/github/yiisoft/rbac-rules-container/coverage.svg)](https://shepherd.dev/github/yiisoft/rbac-rules-container) -The package ... +The package provides rules container for [Yii RBAC (Role-Based Access Control)](https://github.com/yiisoft/rbac) package +based on [Yii Factory](https://github.com/yiisoft/factory). ## Requirements @@ -26,7 +27,7 @@ The package ... The package could be installed with composer: ```shell -composer require yiisoft/_____ --prefer-dist +composer require yiisoft/rbac-rules-container --prefer-dist ``` ## General usage @@ -60,7 +61,7 @@ The code is statically analyzed with [Psalm](https://psalm.dev/). To run static ## License -The Yii _____ is free software. It is released under the terms of the BSD License. +The Yii RBAC Rules Container is free software. It is released under the terms of the BSD License. Please see [`LICENSE`](./LICENSE.md) for more information. Maintained by [Yii Software](https://www.yiiframework.com/). diff --git a/composer.json b/composer.json index 8ff9ff9..ffe82b3 100644 --- a/composer.json +++ b/composer.json @@ -1,19 +1,23 @@ { "name": "yiisoft/rbac-rules-container", "type": "library", - "description": "_____", + "description": "RBAC rules container based on \"yiisoft/factory\"", "keywords": [ - "_____" + "yii", + "rbac", + "rules", + "factory", + "container" ], "homepage": "https://www.yiiframework.com/", "license": "BSD-3-Clause", "support": { - "issues": "https://github.com/yiisoft/_____/issues?state=open", + "issues": "https://github.com/yiisoft/rbac-rules-container/issues?state=open", "forum": "https://www.yiiframework.com/forum/", "wiki": "https://www.yiiframework.com/wiki/", "irc": "irc://irc.freenode.net/yii", "chat": "https://t.me/yii3en", - "source": "https://github.com/yiisoft/_____" + "source": "https://github.com/yiisoft/rbac-rules-container" }, "funding": [ { @@ -28,22 +32,26 @@ "minimum-stability": "dev", "prefer-stable": true, "require": { - "php": "^7.4|^8.0" + "php": "^7.4|^8.0", + "yiisoft/factory": "^1.0", + "yiisoft/rbac": "^3.0@dev" }, "require-dev": { "phpunit/phpunit": "^9.5", "roave/infection-static-analysis-plugin": "^1.16", "spatie/phpunit-watcher": "^1.23", - "vimeo/psalm": "^4.18" + "vimeo/psalm": "^4.18", + "yiisoft/di": "^1.0", + "yiisoft/test-support": "^1.3" }, "autoload": { "psr-4": { - "Yiisoft\\_____\\": "src" + "Yiisoft\\Rbac\\Rules\\Container\\": "src" } }, "autoload-dev": { "psr-4": { - "Yiisoft\\_____\\Tests\\": "tests" + "Yiisoft\\Rbac\\Rules\\Container\\Tests\\": "tests" } }, "config": { @@ -53,6 +61,15 @@ "composer/package-versions-deprecated": true } }, + "extra": { + "config-plugin-options": { + "source-directory": "config" + }, + "config-plugin": { + "params": "params.php", + "common": "common.php" + } + }, "scripts": { "test": "phpunit --testdox --no-interaction", "test-watch": "phpunit-watcher watch" diff --git a/config/common.php b/config/common.php new file mode 100644 index 0000000..cbf1025 --- /dev/null +++ b/config/common.php @@ -0,0 +1,18 @@ + [ + 'class' => RulesContainer::class, + '__construct()' => [ + 'definitions' => $params['yiisoft/rbac-rules-container']['rules'], + 'validate' => $params['yiisoft/rbac-rules-container']['validate'], + ], + ], +]; diff --git a/config/params.php b/config/params.php new file mode 100644 index 0000000..2a06bd2 --- /dev/null +++ b/config/params.php @@ -0,0 +1,10 @@ + [ + 'rules' => [], + 'validate' => true, + ], +]; diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 976580a..6113945 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -18,13 +18,14 @@ - + ./tests + ./config ./src diff --git a/psalm.xml b/psalm.xml index b899031..3f815b4 100644 --- a/psalm.xml +++ b/psalm.xml @@ -1,6 +1,6 @@ + */ + private array $instances = []; + + /** + * @psalm-param array $definitions + * + * @throws InvalidConfigException + */ + public function __construct(ContainerInterface $container, array $definitions, bool $validate = true) + { + $this->factory = new Factory($container, $definitions, $validate); + } + + /** + * @param string $name + * + * @return RuleInterface + * @throws RuleInterfaceNotImplementedException + * @throws RuleNotFoundException + * @throws CircularReferenceException + * @throws InvalidConfigException + * @throws NotInstantiableException + */ + public function create(string $name): RuleInterface + { + if (!array_key_exists($name, $this->instances)) { + try { + $rule = $this->factory->create($name); + } catch (NotFoundException $e) { + throw new RuleNotFoundException($name, 0, $e); + } + + if (!$rule instanceof RuleInterface) { + throw new RuleInterfaceNotImplementedException($name); + } + + $this->instances[$name] = $rule; + } + + return $this->instances[$name]; + } +} diff --git a/tests/.gitkeep b/tests/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/tests/ConfigTest.php b/tests/ConfigTest.php new file mode 100644 index 0000000..b2e93d8 --- /dev/null +++ b/tests/ConfigTest.php @@ -0,0 +1,75 @@ +createContainer(); + + $rulesContainer = $container->get(RulesFactoryInterface::class); + + $this->assertInstanceOf(RulesContainer::class, $rulesContainer); + } + + public function testDisableValidation(): void + { + $params = $this->getParams(); + $params['yiisoft/rbac-rules-container']['rules'] = ['rule' => 42]; + $params['yiisoft/rbac-rules-container']['validate'] = false; + $container = $this->createContainer($params); + + $rulesContainer = $container->get(RulesFactoryInterface::class); + + $this->expectException(InvalidConfigException::class); + $rulesContainer->create('rule'); + } + + public function testRules(): void + { + $params = $this->getParams(); + $params['yiisoft/rbac-rules-container']['rules'] = ['rule' => AuthorRule::class]; + $container = $this->createContainer($params); + + $rulesContainer = $container->get(RulesFactoryInterface::class); + $rule = $rulesContainer->create('rule'); + + $this->assertInstanceOf(AuthorRule::class, $rule); + } + + private function createContainer(?array $params = null): Container + { + return new Container( + ContainerConfig::create()->withDefinitions( + $this->getCommonDefinitions($params) + ) + ); + } + + private function getCommonDefinitions(?array $params = null): array + { + if ($params === null) { + $params = $this->getParams(); + } + return require dirname(__DIR__) . '/config/common.php'; + } + + private function getParams(): array + { + return require dirname(__DIR__) . '/config/params.php'; + } +} diff --git a/tests/RulesContainerTest.php b/tests/RulesContainerTest.php new file mode 100644 index 0000000..e1bcc47 --- /dev/null +++ b/tests/RulesContainerTest.php @@ -0,0 +1,64 @@ +create(AuthorRule::class); + + $this->assertInstanceOf(AuthorRule::class, $rule); + } + + public function testNotFound(): void + { + $rulesContainer = new RulesContainer(new SimpleContainer(), []); + + $this->expectException(RuleNotFoundException::class); + $this->expectExceptionMessage('Rule "not-exists-rule" not found.'); + $this->expectExceptionCode(0); + $rulesContainer->create('not-exists-rule'); + } + + public function testNotRuleInterface(): void + { + $rulesContainer = new RulesContainer(new SimpleContainer(), ['rule' => new stdClass()]); + + $this->expectException(RuleInterfaceNotImplementedException::class); + $this->expectExceptionMessage('Rule "rule" should implement "' . RuleInterface::class . '".'); + $this->expectExceptionCode(0); + $rulesContainer->create('rule'); + } + + public function testValidation(): void + { + $container = new SimpleContainer(); + $definitions = ['rule' => 42]; + + $this->expectException(InvalidConfigException::class); + new RulesContainer($container, $definitions); + } + + public function testDisableValidation(): void + { + $rulesContainer = new RulesContainer(new SimpleContainer(), ['rule' => 42], false); + + $this->expectException(InvalidConfigException::class); + $rulesContainer->create('rule'); + } +} diff --git a/tests/Support/AuthorRule.php b/tests/Support/AuthorRule.php new file mode 100644 index 0000000..5bf4705 --- /dev/null +++ b/tests/Support/AuthorRule.php @@ -0,0 +1,19 @@ +