Completed
Push — develop ( f6645d...d90408 )
by Narcotic
03:10
created

ImportCommand::importResource()   B

Complexity

Conditions 5
Paths 3

Size

Total Lines 78
Code Lines 51

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 40
CRAP Score 5.4359

Importance

Changes 10
Bugs 0 Features 0
Metric Value
c 10
b 0
f 0
dl 0
loc 78
ccs 40
cts 54
cp 0.7407
rs 8.4317
cc 5
eloc 51
nc 3
nop 8
crap 5.4359

How to fix   Long Method    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
 * import json data into graviton
4
 *
5
 * Supports importing json data from either a single file or a complete folder of files.
6
 *
7
 * The data needs to contain frontmatter to hint where the bits and pieces should go.
8
 */
9
10
namespace Graviton\ImportExport\Command;
11
12
use Graviton\ImportExport\Exception\MissingTargetException;
13
use Graviton\ImportExport\Exception\JsonParseException;
14
use Graviton\ImportExport\Exception\UnknownFileTypeException;
15
use Symfony\Component\Console\Input\InputArgument;
16
use Symfony\Component\Console\Input\InputOption;
17
use Symfony\Component\Console\Input\InputInterface;
18
use Symfony\Component\Console\Output\OutputInterface;
19
use Symfony\Component\Finder\Finder;
20
use Symfony\Component\Yaml\Parser;
21
use Symfony\Component\VarDumper\Cloner\VarCloner;
22
use Symfony\Component\VarDumper\Dumper\CliDumper as Dumper;
23
use GuzzleHttp\Client;
24
use GuzzleHttp\Promise;
25
use GuzzleHttp\Exception\RequestException;
26
use GuzzleHttp\Exception\BadResponseException;
27
use Webuni\FrontMatter\FrontMatter;
28
use Webuni\FrontMatter\Document;
29
use Psr\Http\Message\ResponseInterface;
30
31
/**
32
 * @author   List of contributors <https://github.com/libgraviton/import-export/graphs/contributors>
33
 * @license  http://opensource.org/licenses/gpl-license.php GNU Public License
34
 * @link     http://swisscom.ch
35
 */
36
class ImportCommand extends ImportCommandAbstract
37
{
38
    /**
39
     * @var Client
40
     */
41
    private $client;
42
43
    /**
44
     * @var FrontMatter
45
     */
46
    private $frontMatter;
47
48
    /**
49
     * @var Parser
50
     */
51
    private $parser;
52
53
    /**
54
     * @var VarCloner
55
     */
56
    private $cloner;
57
58
    /**
59
     * @var Dumper
60
     */
61
    private $dumper;
62
63
    /**
64
     * @param Client      $client      guzzle http client
65
     * @param Finder      $finder      symfony/finder instance
66
     * @param FrontMatter $frontMatter frontmatter parser
67
     * @param Parser      $parser      yaml/json parser
68
     * @param VarCloner   $cloner      var cloner for dumping reponses
69
     * @param Dumper      $dumper      dumper for outputing responses
70
     */
71 4
    public function __construct(
72
        Client $client,
73
        Finder $finder,
74
        FrontMatter $frontMatter,
75
        Parser $parser,
76
        VarCloner $cloner,
77
        Dumper $dumper
78
    ) {
79 4
        parent::__construct(
80
            $finder
81 4
        );
82 4
        $this->client = $client;
83 4
        $this->frontMatter = $frontMatter;
84 4
        $this->parser = $parser;
85 4
        $this->cloner = $cloner;
86 4
        $this->dumper = $dumper;
87 4
    }
88
89
    /**
90
     * Configures the current command.
91
     *
92
     * @return void
93
     */
94 4
    protected function configure()
95
    {
96 4
        $this
97 4
            ->setName('graviton:import')
98 4
            ->setDescription('Import files from a folder or file.')
99 4
            ->addOption(
100 4
                'rewrite-host',
101 4
                'r',
102 4
                InputOption::VALUE_OPTIONAL,
103 4
                'Replace the value of this option with the <host> value before importing.',
104
                'http://localhost'
105 4
            )
106 4
            ->addOption(
107 4
                'rewrite-to',
108 4
                't',
109 4
                InputOption::VALUE_OPTIONAL,
110 4
                'String to use as the replacement value for the [REWRITE-HOST] string.',
111
                '<host>'
112 4
            )
113 4
            ->addOption(
114 4
                'sync-requests',
115 4
                's',
116 4
                InputOption::VALUE_NONE,
117
                'Send requests synchronously'
118 4
            )
119 4
            ->addArgument(
120 4
                'host',
121 4
                InputArgument::REQUIRED,
122
                'Protocol and host to load data into (ie. https://graviton.nova.scapp.io)'
123 4
            )
124 4
            ->addArgument(
125 4
                'file',
126 4
                InputArgument::REQUIRED + InputArgument::IS_ARRAY,
127
                'Directories or files to load'
128 4
            );
129 4
    }
130
131
    /**
132
     * Executes the current command.
133
     *
134
     * @param Finder          $finder Finder
135
     * @param InputInterface  $input  User input on console
136
     * @param OutputInterface $output Output of the command
137
     *
138
     * @return void
139
     */
140 4
    protected function doImport(Finder $finder, InputInterface $input, OutputInterface $output)
141
    {
142 4
        $host = $input->getArgument('host');
143 4
        $rewriteHost = $input->getOption('rewrite-host');
144 4
        $rewriteTo = $input->getOption('rewrite-to');
145 4
        if ($rewriteTo === $this->getDefinition()->getOption('rewrite-to')->getDefault()) {
146 4
            $rewriteTo = $host;
147 4
        }
148 4
        $sync = $input->getOption('sync-requests');
149
150 4
        $this->importPaths($finder, $output, $host, $rewriteHost, $rewriteTo, $sync);
151 3
    }
152
153
    /**
154
     * @param Finder          $finder      finder primmed with files to import
155
     * @param OutputInterface $output      output interfac
156
     * @param string          $host        host to import into
157
     * @param string          $rewriteHost string to replace with value from $rewriteTo during loading
158
     * @param string          $rewriteTo   string to replace value from $rewriteHost with during loading
159
     * @param boolean         $sync        send requests syncronously
160
     *
161
     * @return void
162
     *
163
     * @throws MissingTargetException
164
     */
165 4
    protected function importPaths(
166
        Finder $finder,
167
        OutputInterface $output,
168
        $host,
169
        $rewriteHost,
170
        $rewriteTo,
171
        $sync = false
172
    ) {
173 4
        $promises = [];
174 4
        foreach ($finder as $file) {
175 4
            $doc = $this->frontMatter->parse($file->getContents());
176
177 4
            $output->writeln("<info>Loading data from ${file}</info>");
178
179 4
            if (!array_key_exists('target', $doc->getData())) {
180 1
                throw new MissingTargetException('Missing target in \'' . $file . '\'');
181
            }
182
183 3
            $targetUrl = sprintf('%s%s', $host, $doc->getData()['target']);
184
185 3
            $promises[] = $this->importResource(
186 3
                $targetUrl,
187 3
                (string) $file,
188 3
                $output,
189 3
                $doc,
190 3
                $host,
191 3
                $rewriteHost,
192 3
                $rewriteTo,
193
                $sync
194 3
            );
195 3
        }
196
197
        try {
198
            Promise\unwrap($promises);
199
        } catch (\GuzzleHttp\Exception\ClientException $e) {
200
            // silently ignored since we already output an error when the promise fails
201
        }
202
    }
203
204
    /**
205
     * @param string          $targetUrl   target url to import resource into
206
     * @param string          $file        path to file being loaded
207
     * @param OutputInterface $output      output of the command
208
     * @param Document        $doc         document to load
209
     * @param string          $host        host to import into
210
     * @param string          $rewriteHost string to replace with value from $host during loading
211
     * @param string          $rewriteTo   string to replace value from $rewriteHost with during loading
212
     * @param boolean         $sync        send requests syncronously
213
     *
214
     * @return Promise\Promise|null
215
     */
216
    protected function importResource(
217
        $targetUrl,
218
        $file,
219
        OutputInterface $output,
220
        Document $doc,
221
        $host,
0 ignored issues
show
Unused Code introduced by
The parameter $host is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
222
        $rewriteHost,
223
        $rewriteTo,
224
        $sync = false
225
    ) {
226 3
        $content = str_replace($rewriteHost, $rewriteTo, $doc->getContent());
227
228
        $successFunc = function (ResponseInterface $response) use ($output) {
229 2
            $output->writeln(
230 2
                '<comment>Wrote ' . $response->getHeader('Link')[0] . '</comment>'
231 2
            );
232 3
        };
233
234
        $errFunc = function (RequestException $e) use ($output, $file) {
235 1
            $output->writeln(
236 1
                '<error>' . str_pad(
237 1
                    sprintf(
238 1
                        'Failed to write <%s> from \'%s\' with message \'%s\'',
239 1
                        $e->getRequest()->getUri(),
240 1
                        $file,
241 1
                        $e->getMessage()
242 1
                    ),
243 1
                    140,
244
                    ' '
245 1
                ) . '</error>'
246 1
            );
247 1
            if ($output->getVerbosity() >= OutputInterface::VERBOSITY_VERBOSE) {
248 1
                $this->dumper->dump(
249 1
                    $this->cloner->cloneVar(
250 1
                        $this->parser->parse($e->getResponse()->getBody(), false, false, true)
251 1
                    ),
252
                    function ($line, $depth) use ($output) {
253 1
                        if ($depth > 0) {
254 1
                            $output->writeln(
255 1
                                '<error>' . str_pad(str_repeat('  ', $depth) . $line, 140, ' ') . '</error>'
256 1
                            );
257 1
                        }
258 1
                    }
259 1
                );
260 1
            }
261 3
        };
262
263 3
        if ($sync === false) {
264 3
            $promise = $this->client->requestAsync(
265 3
                'PUT',
266 3
                $targetUrl,
267
                [
268 3
                    'json' => $this->parseContent($content, $file)
269 3
                ]
270 3
            );
271 3
            $promise->then($successFunc, $errFunc);
272 3
        } else {
273
            $promise = new Promise\Promise;
274
            try {
275
                $promise->resolve(
276
                    $successFunc(
277
                        $this->client->request(
278
                            'PUT',
279
                            $targetUrl,
280
                            [
281
                                'json' => $this->parseContent($content, $file),
282
                            ]
283
                        )
284
                    )
285
                );
286
            } catch (BadResponseException $e) {
287
                $promise->resolve(
288
                    $errFunc($e)
289
                );
290
            }
291
        }
292 3
        return $promise;
293
    }
294
295
    /**
296
     * parse contents of a file depending on type
297
     *
298
     * @param string $content contents part of file
299
     * @param string $file    full path to file
300
     *
301
     * @return mixed
302
     */
303
    protected function parseContent($content, $file)
304
    {
305 3
        if (substr($file, -5) == '.json') {
306 3
            $data = json_decode($content);
307 3
            if (json_last_error() !== JSON_ERROR_NONE) {
308
                throw new JsonParseException(
309
                    sprintf(
310
                        '%s in %s',
311
                        json_last_error_msg(),
312
                        $file
313
                    )
314
                );
315
            }
316 3
        } elseif (substr($file, -4) == '.yml') {
317
            $data = $this->parser->parse($content, false, false, true);
318
        } else {
319
            throw new UnknownFileTypeException($file);
320
        }
321
322 3
        return $data;
323
    }
324
}
325