From 07437ffda0f105759205e45a62028c37b163c8a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20D?= Date: Sat, 26 Nov 2022 13:04:38 +0100 Subject: [PATCH] chore: Create AccountAdapter tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rubén D --- .../Controllers/Account/ViewController.php | 6 +- .../Helpers/Account/AccountHelper.php | 12 +- .../Helpers/Account/AccountRequestHelper.php | 8 +- composer.json | 4 +- composer.lock | 26 ++-- lib/SP/Core/Exceptions/SPException.php | 72 ++++------ lib/SP/DataModel/Dto/AccountAclDto.php | 4 +- ...ilsResponse.php => AccountEnrichedDto.php} | 39 +++--- .../Account/AccountServiceInterface.php | 10 +- lib/SP/Domain/Account/Out/AccountAdapter.php | 27 ++-- .../Account/Out/AccountAdapterInterface.php | 12 +- .../Account/Services/AccountService.php | 12 +- lib/SP/Domain/Common/Out/AdapterBase.php | 5 + lib/SP/Domain/Common/Out/DataModelBase.php | 8 ++ .../CustomField/Out/CustomFieldAdapter.php | 3 +- .../Out/CustomFieldAdapterInterface.php | 2 +- lib/SP/Mvc/Controller/ItemTrait.php | 47 +++---- .../View/Components/ItemAdapterInterface.php | 12 +- lib/SP/Mvc/View/Components/SelectItem.php | 38 ++--- .../Mvc/View/Components/SelectItemAdapter.php | 79 +++++------ .../Domain/Account/Out/AccountAdapterTest.php | 132 ++++++++++++++++++ .../Services/AccountAclServiceTest.php | 6 +- tests/SP/Generators/AccountDataGenerator.php | 87 ++++++++++++ tests/SP/Generators/CustomFieldGenerator.php | 60 ++++++++ tests/SP/Generators/DataGenerator.php | 46 ++++++ 25 files changed, 535 insertions(+), 222 deletions(-) rename lib/SP/DataModel/Dto/{AccountDetailsResponse.php => AccountEnrichedDto.php} (84%) create mode 100644 tests/SP/Domain/Account/Out/AccountAdapterTest.php create mode 100644 tests/SP/Generators/AccountDataGenerator.php create mode 100644 tests/SP/Generators/CustomFieldGenerator.php create mode 100644 tests/SP/Generators/DataGenerator.php diff --git a/app/modules/api/Controllers/Account/ViewController.php b/app/modules/api/Controllers/Account/ViewController.php index ce25ef2b..d00dead1 100644 --- a/app/modules/api/Controllers/Account/ViewController.php +++ b/app/modules/api/Controllers/Account/ViewController.php @@ -30,7 +30,7 @@ use League\Fractal\Resource\Item; use SP\Core\Acl\ActionsInterface; use SP\Core\Events\Event; use SP\Core\Events\EventMessage; -use SP\DataModel\Dto\AccountDetailsResponse; +use SP\DataModel\Dto\AccountEnrichedDto; use SP\Domain\Api\Services\ApiResponse; use SP\Util\Util; @@ -59,7 +59,7 @@ final class ViewController extends AccountBase $this->accountService->incrementViewCounter($id); - $accountResponse = new AccountDetailsResponse($id, $accountDetails); + $accountResponse = new AccountEnrichedDto($accountDetails); $this->accountService ->withUsersById($accountResponse) @@ -91,4 +91,4 @@ final class ViewController extends AccountBase processException($e); } } -} \ No newline at end of file +} diff --git a/app/modules/web/Controllers/Helpers/Account/AccountHelper.php b/app/modules/web/Controllers/Helpers/Account/AccountHelper.php index 69babf85..e9fb804d 100644 --- a/app/modules/web/Controllers/Helpers/Account/AccountHelper.php +++ b/app/modules/web/Controllers/Helpers/Account/AccountHelper.php @@ -33,7 +33,7 @@ use SP\Core\Exceptions\ConstraintException; use SP\Core\Exceptions\QueryException; use SP\Core\Exceptions\SPException; use SP\DataModel\Dto\AccountAclDto; -use SP\DataModel\Dto\AccountDetailsResponse; +use SP\DataModel\Dto\AccountEnrichedDto; use SP\DataModel\ItemPreset\AccountPermission; use SP\DataModel\ItemPreset\AccountPrivate; use SP\DataModel\ProfileData; @@ -125,7 +125,7 @@ final class AccountHelper extends AccountHelperBase /** * Sets account's view variables * - * @param AccountDetailsResponse $accountDetailsResponse + * @param AccountEnrichedDto $accountDetailsResponse * @param int $actionId * * @throws \SP\Core\Acl\AccountPermissionException @@ -138,7 +138,7 @@ final class AccountHelper extends AccountHelperBase * @throws \SP\Infrastructure\Common\Repositories\NoSuchItemException */ public function setViewForAccount( - AccountDetailsResponse $accountDetailsResponse, + AccountEnrichedDto $accountDetailsResponse, int $actionId ): void { $this->accountId = $accountDetailsResponse->getAccountVData()->getId(); @@ -288,14 +288,14 @@ final class AccountHelper extends AccountHelperBase /** * Comprobar si el usuario dispone de acceso al módulo * - * @param AccountDetailsResponse $accountDetailsResponse + * @param AccountEnrichedDto $accountDetailsResponse * * @return AccountAcl * @throws AccountPermissionException * @throws ConstraintException * @throws QueryException */ - protected function checkAccess(AccountDetailsResponse $accountDetailsResponse): AccountAcl + protected function checkAccess(AccountEnrichedDto $accountDetailsResponse): AccountAcl { $accountAcl = $this->accountAclService->getAcl( $this->actionId, @@ -459,4 +459,4 @@ final class AccountHelper extends AccountHelperBase $this->setViewCommon(); } -} \ No newline at end of file +} diff --git a/app/modules/web/Controllers/Helpers/Account/AccountRequestHelper.php b/app/modules/web/Controllers/Helpers/Account/AccountRequestHelper.php index e200008f..aa076bc4 100644 --- a/app/modules/web/Controllers/Helpers/Account/AccountRequestHelper.php +++ b/app/modules/web/Controllers/Helpers/Account/AccountRequestHelper.php @@ -25,7 +25,7 @@ namespace SP\Modules\Web\Controllers\Helpers\Account; -use SP\DataModel\Dto\AccountDetailsResponse; +use SP\DataModel\Dto\AccountEnrichedDto; use SP\Domain\Account\Services\AccountAcl; /** @@ -36,7 +36,7 @@ final class AccountRequestHelper extends AccountHelperBase /** * Sets account's view variables * - * @param AccountDetailsResponse $accountDetailsResponse + * @param AccountEnrichedDto $accountDetailsResponse * @param int $actionId * * @return bool @@ -46,7 +46,7 @@ final class AccountRequestHelper extends AccountHelperBase * @throws \SP\Domain\User\Services\UpdatedMasterPassException */ public function setViewForRequest( - AccountDetailsResponse $accountDetailsResponse, + AccountEnrichedDto $accountDetailsResponse, int $actionId ): bool { $this->accountId = $accountDetailsResponse->getAccountVData()->getId(); @@ -73,4 +73,4 @@ final class AccountRequestHelper extends AccountHelperBase return true; } -} \ No newline at end of file +} diff --git a/composer.json b/composer.json index 7431321c..58d9e5ba 100644 --- a/composer.json +++ b/composer.json @@ -51,8 +51,8 @@ "ext-zlib": "*", "ext-libxml": "*", "ext-mbstring": "*", - "league/fractal": "^0.19.2", - "symfony/console": "^v5.1.2", + "league/fractal": "^0.20", + "symfony/console": "^v5.1", "symfony/lock": "^v5.0", "ocramius/proxy-manager": "~2.0", "aura/sqlquery": "~2.8" diff --git a/composer.lock b/composer.lock index 12cb8155..59161a1c 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "3208d5c05083a6ccc17baff9dfde8070", + "content-hash": "6fb2448797e2a31979571f067d57d035", "packages": [ { "name": "ademarre/binary-to-text-php", @@ -1520,28 +1520,30 @@ }, { "name": "league/fractal", - "version": "0.19.2", + "version": "0.20.1", "source": { "type": "git", "url": "https://github.com/thephpleague/fractal.git", - "reference": "06dc15f6ba38f2dde2f919d3095d13b571190a7c" + "reference": "8b9d39b67624db9195c06f9c1ffd0355151eaf62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/fractal/zipball/06dc15f6ba38f2dde2f919d3095d13b571190a7c", - "reference": "06dc15f6ba38f2dde2f919d3095d13b571190a7c", + "url": "https://api.github.com/repos/thephpleague/fractal/zipball/8b9d39b67624db9195c06f9c1ffd0355151eaf62", + "reference": "8b9d39b67624db9195c06f9c1ffd0355151eaf62", "shasum": "" }, "require": { - "php": ">=5.4" + "php": ">=7.4" }, "require-dev": { "doctrine/orm": "^2.5", "illuminate/contracts": "~5.0", - "mockery/mockery": "~0.9", + "mockery/mockery": "^1.3", "pagerfanta/pagerfanta": "~1.0.0", - "phpunit/phpunit": "^4.8.35 || ^7.5", - "squizlabs/php_codesniffer": "~1.5|~2.0|~3.4", + "phpstan/phpstan": "^1.4", + "phpunit/phpunit": "^9.5", + "squizlabs/php_codesniffer": "~3.4", + "vimeo/psalm": "^4.22", "zendframework/zend-paginator": "~2.3" }, "suggest": { @@ -1552,7 +1554,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "0.13-dev" + "dev-master": "0.20.x-dev" } }, "autoload": { @@ -1582,9 +1584,9 @@ ], "support": { "issues": "https://github.com/thephpleague/fractal/issues", - "source": "https://github.com/thephpleague/fractal/tree/0.19.2" + "source": "https://github.com/thephpleague/fractal/tree/0.20.1" }, - "time": "2020-01-24T23:17:29+00:00" + "time": "2022-04-11T12:47:17+00:00" }, { "name": "monolog/monolog", diff --git a/lib/SP/Core/Exceptions/SPException.php b/lib/SP/Core/Exceptions/SPException.php index 56f38af4..38209c68 100644 --- a/lib/SP/Core/Exceptions/SPException.php +++ b/lib/SP/Core/Exceptions/SPException.php @@ -4,7 +4,7 @@ * * @author nuxsmin * @link https://syspass.org - * @copyright 2012-2021, Rubén Domínguez nuxsmin@$syspass.org + * @copyright 2012-2022, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. * @@ -36,52 +36,35 @@ class SPException extends Exception /** * Constantes para tipos de excepción */ - const OK = 0; - const CRITICAL = 1; - const WARNING = 2; - const ERROR = 3; - const INFO = 4; - /** - * @var int Tipo de excepción - */ - protected $type; - /** - * @var string Ayuda de la excepción - */ - protected $hint; + public const OK = 0; + public const CRITICAL = 1; + public const WARNING = 2; + public const ERROR = 3; + public const INFO = 4; + + protected int $type; + protected ?string $hint; /** * SPException constructor. * - * @param string $message - * @param int $type - * @param string $hint - * @param int $code - * @param Exception|null $previous + * @param string $message + * @param int $type + * @param string|null $hint + * @param int $code + * @param Exception|null $previous */ - public function __construct($message, $type = self::ERROR, $hint = null, $code = 0, Exception $previous = null) - { + public function __construct( + string $message, + int $type = self::ERROR, + ?string $hint = null, + int $code = 0, + Exception $previous = null + ) { $this->type = $type; $this->hint = $hint; - parent::__construct($message, (int)$code, $previous); - } - - /** - * @param $type - * - * @return mixed - */ - public static function getExceptionTypeName($type) - { - $typeName = [ - self::OK => 'ok', - self::CRITICAL => 'critical', - self::WARNING => 'warning', - self::ERROR => 'error' - ]; - - return $typeName[$type]; + parent::__construct($message, $code, $previous); } /** @@ -89,13 +72,10 @@ class SPException extends Exception */ public function __toString() { - return __CLASS__ . ": [{$this->code}]: {$this->message} ({$this->hint})\n"; + return __CLASS__.": [{$this->code}]: {$this->message} ({$this->hint})\n"; } - /** - * @return string - */ - public function getHint() + public function getHint(): ?string { return $this->hint; } @@ -103,8 +83,8 @@ class SPException extends Exception /** * @return int|string */ - public function getType() + public function getType(): int|string { return $this->type; } -} \ No newline at end of file +} diff --git a/lib/SP/DataModel/Dto/AccountAclDto.php b/lib/SP/DataModel/Dto/AccountAclDto.php index f16de1c1..2123ce24 100644 --- a/lib/SP/DataModel/Dto/AccountAclDto.php +++ b/lib/SP/DataModel/Dto/AccountAclDto.php @@ -82,11 +82,11 @@ final class AccountAclDto } /** - * @param AccountDetailsResponse $accountDetailsResponse + * @param AccountEnrichedDto $accountDetailsResponse * * @return AccountAclDto */ - public static function makeFromAccount(AccountDetailsResponse $accountDetailsResponse): AccountAclDto + public static function makeFromAccount(AccountEnrichedDto $accountDetailsResponse): AccountAclDto { return new self( $accountDetailsResponse->getId(), diff --git a/lib/SP/DataModel/Dto/AccountDetailsResponse.php b/lib/SP/DataModel/Dto/AccountEnrichedDto.php similarity index 84% rename from lib/SP/DataModel/Dto/AccountDetailsResponse.php rename to lib/SP/DataModel/Dto/AccountEnrichedDto.php index eb1172df..399f2502 100644 --- a/lib/SP/DataModel/Dto/AccountDetailsResponse.php +++ b/lib/SP/DataModel/Dto/AccountEnrichedDto.php @@ -4,7 +4,7 @@ * * @author nuxsmin * @link https://syspass.org - * @copyright 2012-2021, Rubén Domínguez nuxsmin@$syspass.org + * @copyright 2012-2022, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. * @@ -28,11 +28,11 @@ use SP\DataModel\AccountVData; use SP\DataModel\ItemData; /** - * Class AccountDetailsResponse + * Class AccountEnrichedDto */ -class AccountDetailsResponse +class AccountEnrichedDto { - private int $id; + private int $id; private AccountVData $accountVData; /** * @var ItemData[] Los usuarios secundarios de la cuenta. @@ -50,15 +50,22 @@ class AccountDetailsResponse /** * AccountDetailsResponse constructor. * - * @param int $id - * @param AccountVData $accountVData + * @param AccountVData $accountVData */ - public function __construct(int $id, AccountVData $accountVData) + public function __construct(AccountVData $accountVData) { - $this->id = $id; + $this->id = $accountVData->getId(); $this->accountVData = $accountVData; } + /** + * @return int + */ + public function getId(): int + { + return $this->id; + } + /** * @return ItemData[] */ @@ -68,7 +75,7 @@ class AccountDetailsResponse } /** - * @param ItemData[] $users + * @param ItemData[] $users */ public function setUsers(array $users): void { @@ -84,7 +91,7 @@ class AccountDetailsResponse } /** - * @param ItemData[] $userGroups + * @param ItemData[] $userGroups */ public function setUserGroups(array $userGroups): void { @@ -100,7 +107,7 @@ class AccountDetailsResponse } /** - * @param ItemData[] $tags + * @param ItemData[] $tags */ public function setTags(array $tags): void { @@ -114,12 +121,4 @@ class AccountDetailsResponse { return $this->accountVData; } - - /** - * @return int - */ - public function getId(): int - { - return $this->id; - } -} \ No newline at end of file +} diff --git a/lib/SP/Domain/Account/AccountServiceInterface.php b/lib/SP/Domain/Account/AccountServiceInterface.php index 160ac569..f1b203e8 100644 --- a/lib/SP/Domain/Account/AccountServiceInterface.php +++ b/lib/SP/Domain/Account/AccountServiceInterface.php @@ -30,7 +30,7 @@ use SP\Core\Exceptions\QueryException; use SP\Core\Exceptions\SPException; use SP\DataModel\AccountExtData; use SP\DataModel\AccountHistoryData; -use SP\DataModel\Dto\AccountDetailsResponse; +use SP\DataModel\Dto\AccountEnrichedDto; use SP\DataModel\ItemSearchData; use SP\Domain\Account\Out\AccountData; use SP\Domain\Account\Out\AccountPassData; @@ -52,19 +52,19 @@ interface AccountServiceInterface * @throws QueryException * @throws ConstraintException */ - public function withUsersById(AccountDetailsResponse $accountDetailsResponse): AccountServiceInterface; + public function withUsersById(AccountEnrichedDto $accountDetailsResponse): AccountServiceInterface; /** * @throws QueryException * @throws ConstraintException */ - public function withUserGroupsById(AccountDetailsResponse $accountDetailsResponse): AccountServiceInterface; + public function withUserGroupsById(AccountEnrichedDto $accountDetailsResponse): AccountServiceInterface; /** * @throws QueryException * @throws ConstraintException */ - public function withTagsById(AccountDetailsResponse $accountDetailsResponse): AccountServiceInterface; + public function withTagsById(AccountEnrichedDto $accountDetailsResponse): AccountServiceInterface; /** * @throws \SP\Core\Exceptions\ConstraintException @@ -114,7 +114,7 @@ interface AccountServiceInterface * @throws NoSuchItemException * @throws ConstraintException */ - public function getById(int $id): AccountDetailsResponse; + public function getById(int $id): AccountEnrichedDto; /** * Updates external items for the account diff --git a/lib/SP/Domain/Account/Out/AccountAdapter.php b/lib/SP/Domain/Account/Out/AccountAdapter.php index 7e1c36b3..c86aee4f 100644 --- a/lib/SP/Domain/Account/Out/AccountAdapter.php +++ b/lib/SP/Domain/Account/Out/AccountAdapter.php @@ -26,8 +26,9 @@ namespace SP\Domain\Account\Out; use League\Fractal\Resource\Collection; use SP\Core\Acl\ActionsInterface; -use SP\DataModel\Dto\AccountDetailsResponse; +use SP\DataModel\Dto\AccountEnrichedDto; use SP\Domain\Common\Out\AdapterBase; +use SP\Domain\Config\In\ConfigDataInterface; use SP\Domain\CustomField\CustomFieldServiceInterface; use SP\Domain\CustomField\Out\CustomFieldAdapter; use SP\Mvc\Controller\ItemTrait; @@ -37,13 +38,23 @@ use SP\Util\Link; /** * Class AccountAdapter * - * @package SP\Domain\Account\Out + * @template T of AccountEnrichedDto + * @template-implements AccountAdapterInterface */ final class AccountAdapter extends AdapterBase implements AccountAdapterInterface { use ItemTrait; - protected $availableIncludes = ['customFields']; + protected array $availableIncludes = ['customFields']; + + private CustomFieldServiceInterface $customFieldService; + + public function __construct(ConfigDataInterface $configData, CustomFieldServiceInterface $customFieldService) + { + parent::__construct($configData); + + $this->customFieldService = $customFieldService; + } /** * @throws \SP\Core\Exceptions\ConstraintException @@ -51,17 +62,15 @@ final class AccountAdapter extends AdapterBase implements AccountAdapterInterfac * @throws \SP\Core\Exceptions\SPException * @throws \SP\Domain\Common\Services\ServiceException */ - public function includeCustomFields( - AccountDetailsResponse $data, - CustomFieldServiceInterface $customFieldService - ): Collection { + public function includeCustomFields(AccountEnrichedDto $data,): Collection + { return $this->collection( - $this->getCustomFieldsForItem(ActionsInterface::ACCOUNT, $data->getId(), $customFieldService), + $this->getCustomFieldsForItem(ActionsInterface::ACCOUNT, $data->getId(), $this->customFieldService), new CustomFieldAdapter($this->configData) ); } - public function transform(AccountDetailsResponse $data): array + public function transform(AccountEnrichedDto $data): array { $account = $data->getAccountVData(); diff --git a/lib/SP/Domain/Account/Out/AccountAdapterInterface.php b/lib/SP/Domain/Account/Out/AccountAdapterInterface.php index e10f5338..5702e696 100644 --- a/lib/SP/Domain/Account/Out/AccountAdapterInterface.php +++ b/lib/SP/Domain/Account/Out/AccountAdapterInterface.php @@ -25,13 +25,10 @@ namespace SP\Domain\Account\Out; use League\Fractal\Resource\Collection; -use SP\DataModel\Dto\AccountDetailsResponse; -use SP\Domain\CustomField\CustomFieldServiceInterface; +use SP\DataModel\Dto\AccountEnrichedDto; /** * Class AccountAdapter - * - * @package SP\Adapters */ interface AccountAdapterInterface { @@ -41,10 +38,7 @@ interface AccountAdapterInterface * @throws \SP\Core\Exceptions\SPException * @throws \SP\Domain\Common\Services\ServiceException */ - public function includeCustomFields( - AccountDetailsResponse $data, - CustomFieldServiceInterface $customFieldService - ): Collection; + public function includeCustomFields(AccountEnrichedDto $data,): Collection; - public function transform(AccountDetailsResponse $data): array; + public function transform(AccountEnrichedDto $data): array; } diff --git a/lib/SP/Domain/Account/Services/AccountService.php b/lib/SP/Domain/Account/Services/AccountService.php index 4896bb13..81dd36fa 100644 --- a/lib/SP/Domain/Account/Services/AccountService.php +++ b/lib/SP/Domain/Account/Services/AccountService.php @@ -33,7 +33,7 @@ use SP\Core\Exceptions\QueryException; use SP\Core\Exceptions\SPException; use SP\DataModel\AccountExtData; use SP\DataModel\AccountHistoryData; -use SP\DataModel\Dto\AccountDetailsResponse; +use SP\DataModel\Dto\AccountEnrichedDto; use SP\DataModel\Dto\AccountHistoryCreateDto; use SP\DataModel\ItemPreset\AccountPermission; use SP\DataModel\ItemPreset\AccountPrivate; @@ -100,7 +100,7 @@ final class AccountService extends Service implements AccountServiceInterface * @throws QueryException * @throws ConstraintException */ - public function withUsersById(AccountDetailsResponse $accountDetailsResponse): AccountServiceInterface + public function withUsersById(AccountEnrichedDto $accountDetailsResponse): AccountServiceInterface { $accountDetailsResponse->setUsers( $this->accountToUserRepository->getUsersByAccountId($accountDetailsResponse->getId()) @@ -114,7 +114,7 @@ final class AccountService extends Service implements AccountServiceInterface * @throws QueryException * @throws ConstraintException */ - public function withUserGroupsById(AccountDetailsResponse $accountDetailsResponse): AccountServiceInterface + public function withUserGroupsById(AccountEnrichedDto $accountDetailsResponse): AccountServiceInterface { $accountDetailsResponse->setUserGroups( $this->accountToUserGroupRepository->getUserGroupsByAccountId($accountDetailsResponse->getId()) @@ -128,7 +128,7 @@ final class AccountService extends Service implements AccountServiceInterface * @throws QueryException * @throws ConstraintException */ - public function withTagsById(AccountDetailsResponse $accountDetailsResponse): AccountServiceInterface + public function withTagsById(AccountEnrichedDto $accountDetailsResponse): AccountServiceInterface { $accountDetailsResponse->setTags( $this->accountToTagRepository->getTagsByAccountId($accountDetailsResponse->getId()) @@ -311,7 +311,7 @@ final class AccountService extends Service implements AccountServiceInterface * @throws NoSuchItemException * @throws ConstraintException */ - public function getById(int $id): AccountDetailsResponse + public function getById(int $id): AccountEnrichedDto { $result = $this->accountRepository->getById($id); @@ -319,7 +319,7 @@ final class AccountService extends Service implements AccountServiceInterface throw new NoSuchItemException(__u('The account doesn\'t exist')); } - return new AccountDetailsResponse($id, $result->getData()); + return new AccountEnrichedDto($result->getData()); } /** diff --git a/lib/SP/Domain/Common/Out/AdapterBase.php b/lib/SP/Domain/Common/Out/AdapterBase.php index 0d849ead..0c38508b 100644 --- a/lib/SP/Domain/Common/Out/AdapterBase.php +++ b/lib/SP/Domain/Common/Out/AdapterBase.php @@ -25,6 +25,7 @@ namespace SP\Domain\Common\Out; use League\Fractal\TransformerAbstract; +use RuntimeException; use SP\Domain\Config\In\ConfigDataInterface; /** @@ -39,5 +40,9 @@ abstract class AdapterBase extends TransformerAbstract public function __construct(ConfigDataInterface $configData) { $this->configData = $configData; + + if (!method_exists(static::class, 'transform')) { + throw new RuntimeException('\'transform\' method must be implemented'); + } } } diff --git a/lib/SP/Domain/Common/Out/DataModelBase.php b/lib/SP/Domain/Common/Out/DataModelBase.php index 8a2a9661..f88df252 100644 --- a/lib/SP/Domain/Common/Out/DataModelBase.php +++ b/lib/SP/Domain/Common/Out/DataModelBase.php @@ -70,4 +70,12 @@ abstract class DataModelBase return property_exists($this, $name) || isset($this->properties[$name]); } + final public function toArray(): array + { + if (count($this->properties) !== 0) { + return $this->properties; + } + + return get_object_vars($this); + } } diff --git a/lib/SP/Domain/CustomField/Out/CustomFieldAdapter.php b/lib/SP/Domain/CustomField/Out/CustomFieldAdapter.php index 34d4593c..7576374f 100644 --- a/lib/SP/Domain/CustomField/Out/CustomFieldAdapter.php +++ b/lib/SP/Domain/CustomField/Out/CustomFieldAdapter.php @@ -24,7 +24,6 @@ namespace SP\Domain\CustomField\Out; - use SP\Domain\Common\Out\AdapterBase; use SP\Domain\CustomField\Services\CustomFieldItem; @@ -48,4 +47,4 @@ final class CustomFieldAdapter extends AdapterBase implements CustomFieldAdapter 'required' => $data->required, ]; } -} \ No newline at end of file +} diff --git a/lib/SP/Domain/CustomField/Out/CustomFieldAdapterInterface.php b/lib/SP/Domain/CustomField/Out/CustomFieldAdapterInterface.php index 476f4389..bf7d7c1e 100644 --- a/lib/SP/Domain/CustomField/Out/CustomFieldAdapterInterface.php +++ b/lib/SP/Domain/CustomField/Out/CustomFieldAdapterInterface.php @@ -35,4 +35,4 @@ use SP\Domain\CustomField\Services\CustomFieldItem; interface CustomFieldAdapterInterface { public function transform(CustomFieldItem $data): array; -} \ No newline at end of file +} diff --git a/lib/SP/Mvc/Controller/ItemTrait.php b/lib/SP/Mvc/Controller/ItemTrait.php index d0c8baef..65e53fc5 100644 --- a/lib/SP/Mvc/Controller/ItemTrait.php +++ b/lib/SP/Mvc/Controller/ItemTrait.php @@ -33,11 +33,10 @@ use SP\Domain\CustomField\Services\CustomFieldItem; use SP\Domain\CustomField\Services\CustomFieldService; use SP\Http\RequestInterface; use SP\Util\Filter; +use function SP\__u; /** * Trait ItemTrait - * - * @package SP\Modules\Web\Controllers\Traits */ trait ItemTrait { @@ -83,7 +82,7 @@ trait ItemTrait $customFields[] = $customField; } catch (CryptoException $e) { - throw new SPException(__u('Internal error'), SPException::ERROR); + throw new SPException(__u('Internal error'), SPException::ERROR, null, 0, $e); } } @@ -106,11 +105,11 @@ trait ItemTrait */ protected function addCustomFieldsForItem( int $moduleId, - $itemId, + int|array $itemId, RequestInterface $request, CustomFieldServiceInterface $customFieldService ): void { - $customFields = $this->getCustomFieldsFromRequest($request); + $customFields = self::getCustomFieldsFromRequest($request); if (!empty($customFields)) { try { @@ -124,11 +123,24 @@ trait ItemTrait $customFieldService->create($customFieldData); } } catch (CryptoException $e) { - throw new SPException(__u('Internal error'), SPException::ERROR); + throw new SPException(__u('Internal error'), SPException::ERROR, null, 0, $e); } } } + /** + * @param \SP\Http\RequestInterface $request + * + * @return array|null + */ + private static function getCustomFieldsFromRequest(RequestInterface $request): ?array + { + return $request->analyzeArray( + 'customfield', + fn($values) => array_map(static fn($value) => Filter::getString($value), $values) + ); + } + /** * Eliminar los campos personalizados del elemento * @@ -142,7 +154,7 @@ trait ItemTrait */ protected function deleteCustomFieldsForItem( int $moduleId, - $itemId, + array|int $itemId, CustomFieldServiceInterface $customFieldService ): void { if (is_array($itemId)) { @@ -166,11 +178,11 @@ trait ItemTrait */ protected function updateCustomFieldsForItem( int $moduleId, - $itemId, + int|array $itemId, RequestInterface $request, CustomFieldServiceInterface $customFieldService ): void { - $customFields = $this->getCustomFieldsFromRequest($request); + $customFields = self::getCustomFieldsFromRequest($request); if (!empty($customFields)) { try { @@ -186,7 +198,7 @@ trait ItemTrait } } } catch (CryptoException $e) { - throw new SPException(__u('Internal error'), SPException::ERROR); + throw new SPException(__u('Internal error'), SPException::ERROR, null, 0, $e); } } } @@ -208,17 +220,4 @@ trait ItemTrait { return $request->analyzeArray('items'); } - - /** - * @param \SP\Http\RequestInterface $request - * - * @return array|null - */ - private static function getCustomFieldsFromRequest(RequestInterface $request): ?array - { - return $request->analyzeArray( - 'customfield', - fn($values) => array_map(static fn($value) => Filter::getString($value), $values) - ); - } -} \ No newline at end of file +} diff --git a/lib/SP/Mvc/View/Components/ItemAdapterInterface.php b/lib/SP/Mvc/View/Components/ItemAdapterInterface.php index 2ccf7fe5..28f71444 100644 --- a/lib/SP/Mvc/View/Components/ItemAdapterInterface.php +++ b/lib/SP/Mvc/View/Components/ItemAdapterInterface.php @@ -4,7 +4,7 @@ * * @author nuxsmin * @link https://syspass.org - * @copyright 2012-2021, Rubén Domínguez nuxsmin@$syspass.org + * @copyright 2012-2022, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. * @@ -36,26 +36,26 @@ interface ItemAdapterInterface * * @return array */ - public function getItemsFromModel(); + public function getItemsFromModel(): array; /** * Returns a JSON like collection of items for a select component * * @return string */ - public function getJsonItemsFromModel(); + public function getJsonItemsFromModel(): string; /** * Returns a collection of items for a select component * * @return array */ - public function getItemsFromArray(); + public function getItemsFromArray(): array; /** * Returns a collection of items for a select component * * @return string */ - public function getJsonItemsFromArray(); -} \ No newline at end of file + public function getJsonItemsFromArray(): string; +} diff --git a/lib/SP/Mvc/View/Components/SelectItem.php b/lib/SP/Mvc/View/Components/SelectItem.php index 207fe495..0c28e1d4 100644 --- a/lib/SP/Mvc/View/Components/SelectItem.php +++ b/lib/SP/Mvc/View/Components/SelectItem.php @@ -4,7 +4,7 @@ * * @author nuxsmin * @link https://syspass.org - * @copyright 2012-2021, Rubén Domínguez nuxsmin@$syspass.org + * @copyright 2012-2022, Rubén Domínguez nuxsmin@$syspass.org * * This file is part of sysPass. * @@ -33,20 +33,20 @@ use JsonSerializable; */ final class SelectItem implements JsonSerializable { - protected $id; - protected string $name; - protected $item; - protected bool $selected = false; - protected bool $skip = false; + protected int|string $id; + protected string $name; + protected mixed $item; + protected bool $selected = false; + protected bool $skip = false; /** * SelectItem constructor. * - * @param int|string $id - * @param string $name - * @param mixed $item + * @param int|string $id + * @param string $name + * @param mixed $item */ - public function __construct($id, string $name, $item = null) + public function __construct(int|string $id, string $name, mixed $item = null) { $this->id = $id; $this->name = $name; @@ -56,7 +56,7 @@ final class SelectItem implements JsonSerializable /** * @return int|string */ - public function getId() + public function getId(): int|string { return $this->id; } @@ -77,13 +77,17 @@ final class SelectItem implements JsonSerializable } /** + * @param string $property + * * @return mixed */ - public function getItemProperty(string $property) + public function getItemProperty(string $property): mixed { - return null !== $this->item && isset($this->item->{$property}) - ? $this->item->{$property} - : null; + if (null !== $this->item) { + return $this->item->{$property} ?? null; + } + + return null; } public function isSkip(): bool @@ -99,8 +103,8 @@ final class SelectItem implements JsonSerializable /** * @inheritDoc */ - public function jsonSerialize() + public function jsonSerialize(): array { return ['id' => $this->id, 'name' => $this->name]; } -} \ No newline at end of file +} diff --git a/lib/SP/Mvc/View/Components/SelectItemAdapter.php b/lib/SP/Mvc/View/Components/SelectItemAdapter.php index 64975fcb..898f377e 100644 --- a/lib/SP/Mvc/View/Components/SelectItemAdapter.php +++ b/lib/SP/Mvc/View/Components/SelectItemAdapter.php @@ -28,6 +28,7 @@ use RuntimeException; use SP\Core\Exceptions\SPException; use SP\Domain\Common\Out\DataModelInterface; use SP\Http\Json; +use function SP\__u; /** * Class SelectItemAdapter @@ -103,27 +104,25 @@ final class SelectItemAdapter implements ItemAdapterInterface /** * Returns a collection of items for a select component and set selected ones from an array * - * @param array $selected - * @param string|int|null $skip + * @param array $selected + * @param string|int|null $skip * * @return SelectItem[] */ public function getItemsFromModelSelected( array $selected, - $skip = null - ): array - { + mixed $skip = null + ): array { $items = $this->getItemsFromModel(); - foreach ($items as $item) { - if ($skip !== null && $item->getId() === $skip) { - $item->setSkip(true); + array_walk( + $items, + static function (SelectItem $item) use ($selected, $skip) { + $item->setSkip($item->getId() === $skip); + /** @noinspection TypeUnsafeArraySearchInspection */ + $item->setSelected(in_array($item->getId(), $selected)); } - - if (in_array($item->getId(), $selected, false)) { - $item->setSelected(true); - } - } + ); return $items; } @@ -135,21 +134,16 @@ final class SelectItemAdapter implements ItemAdapterInterface */ public function getItemsFromModel(): array { - $out = []; + return array_map( + static function ($item) { + if (!$item instanceof DataModelInterface) { + throw new RuntimeException(__u('Wrong object type')); + } - foreach ($this->items as $item) { - if (!$item instanceof DataModelInterface) { - throw new RuntimeException(__u('Wrong object type')); - } - - $out[] = new SelectItem( - $item->getId(), - $item->getName(), - $item - ); - } - - return $out; + return new SelectItem($item->getId(), $item->getName(), $item); + }, + $this->items + ); } /** @@ -157,20 +151,19 @@ final class SelectItemAdapter implements ItemAdapterInterface * * @return SelectItem[] */ - public function getItemsFromArraySelected( - array $selected, - bool $useValueAsKey = false - ): array + public function getItemsFromArraySelected(array $selected, bool $useValueAsKey = false): array { $items = $this->getItemsFromArray(); - foreach ($items as $item) { - $value = $useValueAsKey ? $item->getName() : $item->getId(); + array_walk( + $items, + static function (SelectItem $item) use ($selected, $useValueAsKey) { + $value = $useValueAsKey ? $item->getName() : $item->getId(); - if (in_array($value, $selected, false)) { - $item->setSelected(true); + /** @noinspection TypeUnsafeArraySearchInspection */ + $item->setSelected(in_array($value, $selected)); } - } + ); return $items; } @@ -182,12 +175,10 @@ final class SelectItemAdapter implements ItemAdapterInterface */ public function getItemsFromArray(): array { - $out = []; - - foreach ($this->items as $key => $value) { - $out[] = new SelectItem($key, $value); - } - - return $out; + return array_map( + static fn($key, $value) => new SelectItem($key, $value), + array_keys($this->items), + array_values($this->items) + ); } -} \ No newline at end of file +} diff --git a/tests/SP/Domain/Account/Out/AccountAdapterTest.php b/tests/SP/Domain/Account/Out/AccountAdapterTest.php new file mode 100644 index 00000000..441a0a41 --- /dev/null +++ b/tests/SP/Domain/Account/Out/AccountAdapterTest.php @@ -0,0 +1,132 @@ +. + */ + +namespace SP\Tests\Domain\Account\Out; + +use League\Fractal\Manager; +use League\Fractal\Resource\Item; +use SP\Domain\Account\Out\AccountAdapter; +use SP\Domain\CustomField\CustomFieldServiceInterface; +use SP\Mvc\View\Components\SelectItemAdapter; +use SP\Tests\Generators\AccountDataGenerator; +use SP\Tests\Generators\CustomFieldGenerator; +use SP\Tests\UnitaryTestCase; + +/** + * Class AccountAdapterTest + */ +class AccountAdapterTest extends UnitaryTestCase +{ + public function testAdapt(): void + { + $dataGenerator = AccountDataGenerator::factory(); + + $adapter = new AccountAdapter( + $this->config->getConfigData(), + $this->createStub(CustomFieldServiceInterface::class) + ); + $accountData = $dataGenerator->getAccountData(); + + $out = $adapter->transform($accountData); + + $this->assertEquals($accountData->getId(), $out['id']); + $this->assertEquals( + SelectItemAdapter::factory($accountData->getTags())->getItemsFromModel(), + $out['tags'] + ); + $this->assertEquals( + SelectItemAdapter::factory($accountData->getUsers())->getItemsFromModel(), + $out['users'] + ); + $this->assertEquals( + SelectItemAdapter::factory($accountData->getUserGroups())->getItemsFromModel(), + $out['userGroups'] + ); + + $accountVData = $accountData->getAccountVData(); + + $this->assertEquals($accountVData->getName(), $out['name']); + $this->assertEquals($accountVData->getClientId(), $out['clientId']); + $this->assertEquals($accountVData->getClientName(), $out['clientName']); + $this->assertEquals($accountVData->getCategoryId(), $out['categoryId']); + $this->assertEquals($accountVData->getCategoryName(), $out['categoryName']); + $this->assertEquals($accountVData->getUserId(), $out['userId']); + $this->assertEquals($accountVData->getUserName(), $out['userName']); + $this->assertEquals($accountVData->getUserLogin(), $out['userLogin']); + $this->assertEquals($accountVData->getUserGroupId(), $out['userGroupId']); + $this->assertEquals($accountVData->getUserGroupName(), $out['userGroupName']); + $this->assertEquals($accountVData->getUserEditId(), $out['userEditId']); + $this->assertEquals($accountVData->getUserEditName(), $out['userEditName']); + $this->assertEquals($accountVData->getUserEditLogin(), $out['userEditLogin']); + $this->assertEquals($accountVData->getLogin(), $out['login']); + $this->assertEquals($accountVData->getUrl(), $out['url']); + $this->assertEquals($accountVData->getNotes(), $out['notes']); + $this->assertEquals($accountVData->getOtherUserEdit(), $out['otherUserEdit']); + $this->assertEquals($accountVData->getOtherUserGroupEdit(), $out['otherUserGroupEdit']); + $this->assertEquals($accountVData->getDateAdd(), $out['dateAdd']); + $this->assertEquals($accountVData->getDateEdit(), $out['dateEdit']); + $this->assertEquals($accountVData->getCountView(), $out['countView']); + $this->assertEquals($accountVData->getCountDecrypt(), $out['countDecrypt']); + $this->assertEquals($accountVData->getIsPrivate(), $out['isPrivate']); + $this->assertEquals($accountVData->getIsPrivateGroup(), $out['isPrivateGroup']); + $this->assertEquals($accountVData->getPassDate(), $out['passDate']); + $this->assertEquals($accountVData->getPassDateChange(), $out['passDateChange']); + $this->assertEquals($accountVData->getParentId(), $out['parentId']); + $this->assertEquals($accountVData->getPublicLinkHash(), $out['publicLinkHash']); + $this->assertNull($out['customFields']); + + $this->assertEquals('self', $out['links'][0]['rel']); + $this->assertNotEmpty($out['links'][0]['uri']); + } + + public function testIncludeCustomFields(): void + { + $customFieldData = CustomFieldGenerator::factory()->buildSimpleModel(); + $customFieldsService = $this->createStub(CustomFieldServiceInterface::class); + $customFieldsService->expects(self::once()) + ->method('getForModuleAndItemId') + ->willReturn([$customFieldData]); + + $adapter = new AccountAdapter($this->config->getConfigData(), $customFieldsService); + + $fractal = new Manager(); + $fractal->parseIncludes('customFields'); + $out = $fractal->createData( + new Item(AccountDataGenerator::factory()->getAccountData(), $adapter) + )->toArray(); + + $this->assertArrayHasKey('customFields', $out['data']); + $this->assertEquals($customFieldData->type, $out['data']['customFields']['data'][0]['typeName']); + $this->assertEquals($customFieldData->typeText, $out['data']['customFields']['data'][0]['typeText']); + $this->assertEquals($customFieldData->definitionId, $out['data']['customFields']['data'][0]['definitionId']); + $this->assertEquals( + $customFieldData->definitionName, + $out['data']['customFields']['data'][0]['definitionName'] + ); + $this->assertEquals($customFieldData->help, $out['data']['customFields']['data'][0]['help']); + $this->assertEquals($customFieldData->value, $out['data']['customFields']['data'][0]['value']); + $this->assertEquals($customFieldData->encrypted, $out['data']['customFields']['data'][0]['isEncrypted']); + $this->assertEquals($customFieldData->required, $out['data']['customFields']['data'][0]['required']); + } +} diff --git a/tests/SP/Domain/Account/Services/AccountAclServiceTest.php b/tests/SP/Domain/Account/Services/AccountAclServiceTest.php index 0b7b3fe7..4f7b284a 100644 --- a/tests/SP/Domain/Account/Services/AccountAclServiceTest.php +++ b/tests/SP/Domain/Account/Services/AccountAclServiceTest.php @@ -29,7 +29,6 @@ use SP\Core\Acl\ActionsInterface; use SP\Core\Exceptions\ConstraintException; use SP\Core\Exceptions\QueryException; use SP\DataModel\Dto\AccountAclDto; -use SP\DataModel\Dto\AccountDetailsResponse; use SP\DataModel\ItemData; use SP\Domain\Account\Services\AccountAcl; use SP\Domain\Account\Services\AccountAclService; @@ -59,9 +58,8 @@ class AccountAclServiceTest extends UnitaryTestCase ActionsInterface::ACCOUNT_COPY_PASS, ActionsInterface::ACCOUNT_DELETE, ]; - private static array $accounts; - protected AccountDetailsResponse $account; - private AccountAclService $accountAclService; + private static array $accounts; + private AccountAclService $accountAclService; public static function setUpBeforeClass(): void { diff --git a/tests/SP/Generators/AccountDataGenerator.php b/tests/SP/Generators/AccountDataGenerator.php new file mode 100644 index 00000000..60cd7c37 --- /dev/null +++ b/tests/SP/Generators/AccountDataGenerator.php @@ -0,0 +1,87 @@ +. + */ + +namespace SP\Tests\Generators; + +use SP\DataModel\AccountVData; +use SP\DataModel\Dto\AccountEnrichedDto; +use SP\DataModel\ItemData; + +/** + * Class AccountDataGenerator + */ +final class AccountDataGenerator extends DataGenerator +{ + public function getAccountData(): AccountEnrichedDto + { + $accountData = new AccountVData([ + 'id' => $this->faker->randomNumber(), + 'name' => $this->faker->name, + 'clientId' => $this->faker->randomNumber(), + 'clientName' => $this->faker->name, + 'categoryId' => $this->faker->randomNumber(), + 'categoryName' => $this->faker->name, + 'userId' => $this->faker->randomNumber(), + 'userName' => $this->faker->userName, + 'userLogin' => $this->faker->name, + 'userGroupId' => $this->faker->randomNumber(), + 'userGroupName' => $this->faker->name, + 'userEditId' => $this->faker->randomNumber(), + 'userEditName' => $this->faker->userName, + 'userEditLogin' => $this->faker->name, + 'login' => $this->faker->name, + 'url' => $this->faker->url, + 'notes' => $this->faker->text, + 'otherUserEdit' => $this->faker->boolean, + 'otherUserGroupEdit' => $this->faker->boolean, + 'dateAdd' => $this->faker->unixTime, + 'dateEdit' => $this->faker->unixTime, + 'countView' => $this->faker->randomNumber(), + 'countDecrypt' => $this->faker->randomNumber(), + 'isPrivate' => $this->faker->boolean, + 'isPrivateGroup' => $this->faker->boolean, + 'passDate' => $this->faker->unixTime, + 'passDateChange' => $this->faker->unixTime, + 'parentId' => $this->faker->randomNumber(), + 'publicLinkHash' => $this->faker->sha1, + ]); + $out = new AccountEnrichedDto($accountData); + $out->setUsers($this->buildItemData()); + $out->setTags($this->buildItemData()); + $out->setUserGroups($this->buildItemData()); + + return $out; + } + + /** + * @return ItemData[] + */ + public function buildItemData(): array + { + return array_map( + fn() => new ItemData(['id' => $this->faker->randomNumber(), 'name' => $this->faker->name]), + range(0, 9) + ); + } +} diff --git a/tests/SP/Generators/CustomFieldGenerator.php b/tests/SP/Generators/CustomFieldGenerator.php new file mode 100644 index 00000000..a86b7cac --- /dev/null +++ b/tests/SP/Generators/CustomFieldGenerator.php @@ -0,0 +1,60 @@ +. + */ + +namespace SP\Tests\Generators; + +use SP\Domain\Common\Out\SimpleModel; + +/** + * Class CustomFieldGenerator + */ +final class CustomFieldGenerator extends DataGenerator +{ + public function buildSimpleModel(bool $useEncryption = false): SimpleModel + { + $data = null; + $key = null; + + if ($useEncryption) { + $data = $this->faker->text; + $key = $this->faker->sha1; + } + + return new SimpleModel([ + 'required' => $this->faker->boolean, + 'showInList' => $this->faker->boolean, + 'help' => $this->faker->text, + 'definitionId' => $this->faker->randomNumber(), + 'definitionName' => $this->faker->name, + 'typeId' => $this->faker->randomNumber(), + 'typeName' => $this->faker->name, + 'typeText' => $this->faker->text, + 'moduleId' => $this->faker->randomNumber(), + 'formId' => $this->faker->randomNumber(), + 'isEncrypted' => $this->faker->boolean, + 'data' => $data, + 'key' => $key, + ]); + } +} diff --git a/tests/SP/Generators/DataGenerator.php b/tests/SP/Generators/DataGenerator.php new file mode 100644 index 00000000..0c45c38f --- /dev/null +++ b/tests/SP/Generators/DataGenerator.php @@ -0,0 +1,46 @@ +. + */ + +namespace SP\Tests\Generators; + +use Faker\Factory; +use Faker\Generator; + +/** + * Class DataGenerator + */ +abstract class DataGenerator +{ + protected Generator $faker; + + private function __construct() + { + $this->faker = Factory::create(); + } + + public static function factory(): static + { + return new static(); + } +}