InitCommand   A
last analyzed

Complexity

Total Complexity 15

Size/Duplication

Total Lines 158
Duplicated Lines 0 %

Importance

Changes 5
Bugs 1 Features 0
Metric Value
eloc 80
dl 0
loc 158
rs 10
c 5
b 1
f 0
wmc 15

5 Methods

Rating   Name   Duplication   Size   Complexity  
A configure() 0 13 1
A getPreparedEndpoint() 0 6 2
A getServerName() 0 22 3
A createServerId() 0 8 1
B execute() 0 76 8
1
<?php
2
3
namespace Startwind\Inventorio\Command;
4
5
use GuzzleHttp\Client;
6
use GuzzleHttp\Exception\ClientException;
7
use GuzzleHttp\RequestOptions;
8
use RuntimeException;
9
use Symfony\Component\Console\Command\Command;
10
use Symfony\Component\Console\Input\ArrayInput;
11
use Symfony\Component\Console\Input\InputArgument;
12
use Symfony\Component\Console\Input\InputInterface;
13
use Symfony\Component\Console\Input\InputOption;
14
use Symfony\Component\Console\Output\OutputInterface;
15
use Symfony\Component\Console\Style\SymfonyStyle;
16
17
class InitCommand extends InventorioCommand
18
{
19
    protected static $defaultName = 'init';
20
    protected static $defaultDescription = 'Initialize Inventorio';
21
22
    private const ENDPOINT_INIT = '/inventory/server/{serverId}';
23
24
    private const SERVER_ID_PREFIX = 'inv-srv-';
25
26
    /**
27
     * @inheritDoc
28
     */
29
    protected function configure(): void
30
    {
31
        parent::configure();
32
33
        $this->addOption('remote', null, InputOption::VALUE_REQUIRED, 'Start remote command mode');
34
        $this->addOption('smartCare', null, InputOption::VALUE_REQUIRED, 'Start remote command mode');
35
        $this->addOption('logfile', null, InputOption::VALUE_REQUIRED, 'Start logfile mode');
36
        $this->addOption('metrics', null, InputOption::VALUE_REQUIRED, 'Start metrics collection mode');
37
        $this->addOption('serverApi', null, InputOption::VALUE_REQUIRED, 'Start metrics collection mode');
38
39
        $this->addArgument('userId', InputArgument::REQUIRED, 'The inventorio user id.');
40
41
        $this->addOption('serverName', 's', InputOption::VALUE_OPTIONAL, 'The server name');
42
    }
43
44
    /**
45
     * @inheritDoc
46
     */
47
    protected function execute(InputInterface $input, OutputInterface $output): int
48
    {
49
        $this->initConfiguration($input->getOption('configFile'));
50
51
        if ($this->isInitialized()) {
52
            $output->writeln('<info>System is already initialized.</info>');
53
            return Command::SUCCESS;
54
        }
55
56
        $serverName = $this->getServerName($input, $output);
57
58
        $configFile = $this->getConfigFile();
59
        $serverId = $this->createServerId();
60
61
        $userId = $input->getArgument('userId');
62
63
        $client = new Client();
64
65
        $payload = [
66
            'userId' => $userId,
67
            'serverId' => $serverId,
68
            'serverName' => $serverName
69
        ];
70
71
        try {
72
            $response = $client->post($this->getPreparedEndpoint($serverId, $input->getOption('serverApi')), [
73
                RequestOptions::JSON => $payload
74
            ]);
75
        } catch (ClientException $exception) {
76
            $result = json_decode((string)$exception->getResponse()->getBody(), true);
77
            $output->writeln('<error>Unable to initialize: ' . $result['message'] . '</error>');
78
            return Command::FAILURE;
79
        }
80
81
        $result = json_decode((string)$response->getBody(), true);
82
83
        $config = [
84
            'serverId' => $serverId,
85
            'userId' => $userId,
86
            'remote' => false,
87
            'smartCare' => false,
88
            'metrics' => false,
89
            'commands' => $this->config->getCommands(false),
90
            'secret' => $result['data']['secret']
91
        ];
92
93
        if (!file_exists(dirname($configFile))) {
94
            mkdir(dirname($configFile), 0777, true);
95
        }
96
97
        file_put_contents($configFile, json_encode($config), JSON_PRETTY_PRINT);
98
99
        $output->writeln('<info>Server registered.</info>');
100
101
        $array = [
102
            'command' => 'config'
103
        ];
104
105
        if ($input->getOption('remote')) {
106
            $array['--remote'] = $input->getOption('remote');
107
        }
108
        if ($input->getOption('metrics')) {
109
            $array['--metrics'] = $input->getOption('metrics');
110
        }
111
        if ($input->getOption('smartCare')) {
112
            $array['--smartCare'] = $input->getOption('smartCare');
113
        }
114
        if ($input->getOption('serverApi')) {
115
            $array['--serverApi'] = $input->getOption('serverApi');
116
        }
117
118
        $newInput = new ArrayInput($array);
119
120
        $this->getApplication()->find('config')->run($newInput, $output);
121
122
        return Command::SUCCESS;
123
    }
124
125
    /**
126
     * Ask the user for the server name if not set a parameter.
127
     */
128
    private function getServerName(InputInterface $input, OutputInterface $output): string
129
    {
130
        if (!$input->getOption('serverName')) {
131
            $io = new SymfonyStyle($input, $output);
132
133
            $defaultName = gethostname();
134
135
            $serverName = $io->ask(
136
                'Please provide the name of the server (default: ' . $defaultName . ')',
137
                $defaultName,
138
                function (?string $value) {
139
                    if (strlen($value ?? '') < 3) {
140
                        throw new RuntimeException('The server name has to be at least three characters long.');
141
                    }
142
                    return $value;
143
                }
144
            );
145
        } else {
146
            $serverName = $input->getOption('serverName');
147
        }
148
149
        return $serverName;
150
    }
151
152
    /**
153
     * Return the final endpoint where the collected data should be sent to.
154
     */
155
    private function getPreparedEndpoint($serverId, $inventorioServer = null): string
156
    {
157
        if ($inventorioServer) {
158
            return str_replace('{serverId}', $serverId, $inventorioServer . self::ENDPOINT_INIT);
159
        } else {
160
            return str_replace('{serverId}', $serverId, $this->config->getInventorioServer() . self::ENDPOINT_INIT);
161
        }
162
    }
163
164
    /**
165
     * Create an unique ID for the current server
166
     */
167
    private function createServerId(): string
168
    {
169
        $data = random_bytes(16);
170
171
        $data[6] = chr(ord($data[6]) & 0x0f | 0x40);
172
        $data[8] = chr(ord($data[8]) & 0x3f | 0x80);
173
174
        return self::SERVER_ID_PREFIX . vsprintf('%s%s-%s-%s-%s-%s%s%s', /** @scrutinizer ignore-type */ str_split(bin2hex($data), 4));
175
    }
176
}
177