Create::__construct()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 20
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 9
c 1
b 0
f 0
dl 0
loc 20
rs 9.9666
cc 1
nc 1
nop 8

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace AbterPhp\Admin\Console\Commands\ApiClient;
6
7
use AbterPhp\Admin\Domain\Entities\AdminResource;
8
use AbterPhp\Admin\Domain\Entities\ApiClient;
9
use AbterPhp\Admin\Orm\AdminResourceRepo;
10
use AbterPhp\Admin\Orm\ApiClientRepo;
11
use AbterPhp\Admin\Orm\UserRepo;
12
use AbterPhp\Framework\Authorization\CacheManager;
13
use AbterPhp\Framework\Crypto\Crypto;
14
use Hackzilla\PasswordGenerator\Generator\ComputerPasswordGenerator as PasswordGenerator;
15
use Opulence\Console\Commands\Command;
16
use Opulence\Console\Requests\Argument;
17
use Opulence\Console\Requests\ArgumentTypes;
18
use Opulence\Console\Requests\Option;
19
use Opulence\Console\Requests\OptionTypes;
20
use Opulence\Console\Responses\IResponse;
21
use Opulence\Console\StatusCodes;
22
use Opulence\Orm\IUnitOfWork;
23
use Opulence\Orm\OrmException;
24
use ZxcvbnPhp\Zxcvbn;
25
26
class Create extends Command
27
{
28
    public const COMMAND_NAME            = 'apiclient:create';
29
    public const COMMAND_DESCRIPTION     = 'Creates a new API client';
30
    public const COMMAND_SUCCESS         = '<success>New API client is created. ID: <b>%s</b></success>';
31
    public const COMMAND_DRY_RUN_MESSAGE = '<info>Dry run prevented creating new API client.</info>';
32
33
    public const ARGUMENT_USER        = 'user';
34
    public const ARGUMENT_DESCRIPTION = 'description';
35
    public const ARGUMENT_RESOURCES   = 'resources';
36
37
    public const OPTION_DRY_RUN    = 'dry-run';
38
    public const SHORTENED_DRY_RUN = 'd';
39
40
    public const RESPONSE_SECRET = '<info>Secret generated: <b>%s</b></info>';
41
42
    protected UserRepo $userRepo;
43
44
    protected AdminResourceRepo $adminResourceRepo;
45
46
    protected ApiClientRepo $apiClientRepo;
47
48
    protected PasswordGenerator $passwordGenerator;
49
50
    protected Crypto $crypto;
51
52
    protected IUnitOfWork $unitOfWork;
53
54
    protected CacheManager $cacheManager;
55
56
    protected Zxcvbn $zxcvbn;
57
58
    /**
59
     * Create constructor.
60
     *
61
     * @param UserRepo          $userRepo
62
     * @param AdminResourceRepo $adminResourceRepo
63
     * @param ApiClientRepo     $apiClientRepo
64
     * @param PasswordGenerator $passwordGenerator
65
     * @param Crypto            $crypto
66
     * @param IUnitOfWork       $unitOfWork
67
     * @param CacheManager      $cacheManager
68
     * @param Zxcvbn            $zxcvbn
69
     */
70
    public function __construct(
71
        UserRepo $userRepo,
72
        AdminResourceRepo $adminResourceRepo,
73
        ApiClientRepo $apiClientRepo,
74
        PasswordGenerator $passwordGenerator,
75
        Crypto $crypto,
76
        IUnitOfWork $unitOfWork,
77
        CacheManager $cacheManager,
78
        Zxcvbn $zxcvbn
79
    ) {
80
        $this->userRepo          = $userRepo;
81
        $this->adminResourceRepo = $adminResourceRepo;
82
        $this->apiClientRepo     = $apiClientRepo;
83
        $this->passwordGenerator = $passwordGenerator;
84
        $this->crypto            = $crypto;
85
        $this->unitOfWork        = $unitOfWork;
86
        $this->cacheManager      = $cacheManager;
87
        $this->zxcvbn            = $zxcvbn;
88
89
        parent::__construct();
90
    }
91
92
    /**
93
     * @inheritdoc
94
     */
95
    protected function define()
96
    {
97
        $this->setName(static::COMMAND_NAME)
98
            ->setDescription(static::COMMAND_DESCRIPTION)
99
            ->addArgument(
100
                new Argument(
101
                    static::ARGUMENT_USER,
102
                    ArgumentTypes::REQUIRED,
103
                    'User Identifier (Email or Username)'
104
                )
105
            )
106
            ->addArgument(
107
                new Argument(
108
                    static::ARGUMENT_DESCRIPTION,
109
                    ArgumentTypes::REQUIRED,
110
                    'Description'
111
                )
112
            )
113
            ->addArgument(
114
                new Argument(
115
                    static::ARGUMENT_RESOURCES,
116
                    ArgumentTypes::REQUIRED,
117
                    'Resources (Comma separated list)'
118
                )
119
            )
120
            ->addOption(
121
                new Option(
122
                    static::OPTION_DRY_RUN,
123
                    static::SHORTENED_DRY_RUN,
124
                    OptionTypes::OPTIONAL_VALUE,
125
                    'Dry run (default: 0)',
126
                    '0'
127
                )
128
            );
129
    }
130
131
    /**
132
     * @inheritdoc
133
     */
134
    protected function doExecute(IResponse $response)
135
    {
136
        $userIdentifier = $this->getArgumentValue(static::ARGUMENT_USER);
137
138
        try {
139
            $user = $this->userRepo->find($userIdentifier);
140
            if (!$user) {
141
                throw new \RuntimeException();
142
            }
143
144
            $adminResources = $this->getAdminResources($user->getId());
145
146
            $rawSecret        = $this->passwordGenerator->generatePassword();
147
            $preparedPassword = $this->crypto->prepareSecret($rawSecret);
148
            $packedPassword   = $this->crypto->hashCrypt($preparedPassword);
149
150
            $apiClient = $this->getApiClient($user->getId(), $packedPassword, $adminResources);
151
152
            $this->apiClientRepo->add($apiClient);
153
        } catch (\Exception $e) {
154
            if ($e->getPrevious()) {
155
                $response->writeln(sprintf('<error>%s</error>', $e->getPrevious()->getMessage()));
156
            }
157
            $response->writeln(sprintf('<fatal>%s</fatal>', $e->getMessage()));
158
159
            return StatusCodes::FATAL;
160
        }
161
162
163
        $dryRun = (bool)$this->getOptionValue(static::OPTION_DRY_RUN);
164
        if ($dryRun) {
165
            $this->unitOfWork->dispose();
166
            $response->writeln(static::COMMAND_DRY_RUN_MESSAGE);
167
168
            return StatusCodes::OK;
169
        }
170
171
        try {
172
            $this->unitOfWork->commit();
173
            $this->cacheManager->clearAll();
174
        } catch (\Exception $e) {
175
            if ($e->getPrevious()) {
176
                $response->writeln(sprintf('<error>%s</error>', $e->getPrevious()->getMessage()));
177
            }
178
            $response->writeln(sprintf('<fatal>%s</fatal>', $e->getMessage()));
179
180
            return StatusCodes::FATAL;
181
        }
182
183
        $response->writeln(sprintf(static::RESPONSE_SECRET, $rawSecret));
184
        $response->writeln(sprintf(static::COMMAND_SUCCESS, $apiClient->getId()));
185
186
        return StatusCodes::OK;
187
    }
188
189
    /**
190
     * @param string          $userId
191
     * @param string          $packedPassword
192
     * @param AdminResource[] $adminResources
193
     *
194
     * @return ApiClient
195
     * @throws \RuntimeException
196
     */
197
    protected function getApiClient(string $userId, string $packedPassword, array $adminResources): ApiClient
198
    {
199
        $description = $this->getArgumentValue(static::ARGUMENT_DESCRIPTION);
200
201
        return new ApiClient(
202
            '',
203
            $userId,
204
            $description,
205
            $packedPassword,
206
            $adminResources
207
        );
208
    }
209
210
    /**
211
     * @param string $userId
212
     *
213
     * @return AdminResource[]
214
     * @throws OrmException
215
     */
216
    protected function getAdminResources(string $userId): array
217
    {
218
        $resources = explode(',', $this->getArgumentValue(static::ARGUMENT_RESOURCES));
219
        if (!$resources) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $resources of type string[] is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
220
            return [];
221
        }
222
223
        $adminResources = [];
224
        foreach ($this->adminResourceRepo->getByUserId($userId) as $adminResource) {
225
            if (!in_array($adminResource->getIdentifier(), $resources, true)) {
226
                continue;
227
            }
228
229
            $adminResources[] = $adminResource;
230
        }
231
232
        if (count($resources) != count($adminResources)) {
233
            throw new \RuntimeException('User does not have all requested resources');
234
        }
235
236
        return $adminResources;
237
    }
238
}
239