Completed
Push — master ( b8ee64...bf1b53 )
by Nikola
04:04
created

FetchCommand::addErrorNotification()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 5
ccs 0
cts 2
cp 0
rs 9.4285
cc 1
eloc 3
nc 1
nop 1
crap 2
1
<?php
2
/*
3
 * This file is part of the Exchange Rate Bundle, an RunOpenCode project.
4
 *
5
 * (c) 2016 RunOpenCode
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
namespace RunOpenCode\Bundle\ExchangeRate\Command;
11
12
use RunOpenCode\Bundle\ExchangeRate\Contract\NotificationInterface;
13
use RunOpenCode\ExchangeRate\Contract\ManagerInterface;
14
use RunOpenCode\ExchangeRate\Contract\RateInterface;
15
use RunOpenCode\ExchangeRate\Contract\SourceInterface;
16
use RunOpenCode\ExchangeRate\Contract\SourcesRegistryInterface;
17
use RunOpenCode\ExchangeRate\Log\LoggerAwareTrait;
18
use Symfony\Component\Console\Command\Command;
19
use Symfony\Component\Console\Input\InputInterface;
20
use Symfony\Component\Console\Input\InputOption;
21
use Symfony\Component\Console\Output\OutputInterface;
22
use Symfony\Component\Console\Style\SymfonyStyle;
23
use Symfony\Component\Console\Style\OutputStyle;
24
use Symfony\Component\Templating\EngineInterface;
25
26
/**
27
 * Class FetchCommand
28
 *
29
 * Fetch rates from sources.
30
 *
31
 * @package RunOpenCode\Bundle\ExchangeRate\Command
32
 */
33
class FetchCommand extends Command
34
{
35
    use LoggerAwareTrait;
36
37
    /**
38
     * @var ManagerInterface
39
     */
40
    protected $manager;
41
42
    /**
43
     * @var SourcesRegistryInterface
44
     */
45
    protected $sourcesRegistry;
46
47
    /**
48
     * @var SymfonyStyle
49
     */
50
    protected $outputStyle;
51
52
    /**
53
     * @var NotificationInterface[]
54
     */
55
    protected $successNotifications;
56
57
    /**
58
     * @var NotificationInterface[]
59
     */
60
    protected $errorNotifications;
61
62
    public function __construct(ManagerInterface $manager, SourcesRegistryInterface $sourcesRegistry)
63
    {
64
        parent::__construct();
65
        $this->manager = $manager;
66
        $this->sourcesRegistry = $sourcesRegistry;
67
        $this->successNotifications = [];
68
        $this->errorNotifications = [];
69
    }
70
71
    /**
72
     * Add success notification to command notifications stack.
73
     *
74
     * @param NotificationInterface $notification Success notification to add.
75
     * @return FetchCommand $this Fluent interface.
76
     */
77
    public function addSuccessNotification(NotificationInterface $notification)
78
    {
79
        $this->successNotifications[] = $notification;
80
        return $this;
81
    }
82
83
    /**
84
     * Add error notification to command notifications stack.
85
     *
86
     * @param NotificationInterface $notification Error notification to add.
87
     * @return FetchCommand $this Fluent interface.
88
     */
89
    public function addErrorNotification(NotificationInterface $notification)
90
    {
91
        $this->errorNotifications[] = $notification;
92
        return $this;
93
    }
94
95
    /**
96
     * {@inheritdoc}
97
     */
98
    protected function configure()
99
    {
100
        $this
101
            ->setName('roc:exchange-rate:fetch')
102
            ->setDescription('Fetch exchange rates from sources.')
103
            ->addOption('date', 'd', InputOption::VALUE_OPTIONAL, 'State on which date exchange rates should be fetched.')
104
            ->addOption('source', 'src', InputOption::VALUE_OPTIONAL, 'State which sources should be contacted only, separated with comma.')
105
        ;
106
    }
107
108
    /**
109
     * {@inheritdoc}
110
     */
111
    protected function execute(InputInterface $input, OutputInterface $output)
112
    {
113
        $outputStyle = new SymfonyStyle($input, $output);
114
115
        try {
116
            $this
117
                ->cleanInputDate($input, $outputStyle)
118
                ->cleanInputSources($input, $outputStyle);
119
        } catch (\Exception $e) {
120
            return;
121
        }
122
123
        $this->displayCommandBegin($input, $outputStyle);
124
125
        try {
126
127
            $rates = $this->doFetch($input);
128
129
        } catch (\Exception $e) {
130
            $this
131
                ->displayCommandError($outputStyle)
132
                ->dispatchErrorNotifications($input->getOption('source'), $input->getOption('date'))
133
            ;
134
        }
135
136
        $this
137
            ->displayCommandSuccess($outputStyle)
138
            ->dispatchSuccessNotifications($input->getOption('source'), $input->getOption('date'), $rates)
139
        ;
140
    }
141
142
    /**
143
     * Clean date from console input.
144
     *
145
     * @param InputInterface $input Console input.
146
     * @param OutputStyle $outputStyle Output style to use.
147
     * @return FetchCommand $this Fluent interface.
148
     *
149
     * @throws \Exception
150
     */
151
    protected function cleanInputDate(InputInterface $input, OutputStyle $outputStyle)
152
    {
153
        $date = $input->getOption('date');
154
155
        if (!empty($date)) {
156
            $date = \DateTime::createFromFormat('Y-m-d', $date);
157
158
            if ($date === false) {
159
                $outputStyle->error('Invalid date format provided, expected format is "Y-m-d".');
160
                throw new \Exception;
161
            }
162
        } else {
163
            $date = new \DateTime('now');
164
        }
165
166
        $input->setOption('date', $date);
167
168
        return $this;
169
    }
170
171
    /**
172
     * Clean sources from console input.
173
     *
174
     * @param InputInterface $input Console input.
175
     * @param OutputStyle $outputStyle Output style to use.
176
     * @return FetchCommand $this Fluent interface.
177
     *
178
     * @throws \Exception
179
     */
180
    protected function cleanInputSources(InputInterface $input, OutputStyle $outputStyle)
181
    {
182
        $sources = $input->getOption('source');
183
184
        if (!empty($sources)) {
185
            $sources = array_map('trim', explode(',', $sources));
186
187
            foreach ($sources as $source) {
188
189
                if (!$this->sourcesRegistry->has($source)) {
190
191
                    $outputStyle->error(sprintf('Invalid source name "%s" provided, available sources are "%s".', $source, implode(', ', array_map(function(SourceInterface $source) {
192
                        return $source->getName();
193
                    }, $this->sourcesRegistry->all()))));
194
195
                    throw new \Exception;
196
                }
197
            }
198
        }
199
200
        $input->setOption('source', $sources);
201
202
        return $this;
203
    }
204
205
    /**
206
     * Display command begin note.
207
     *
208
     * @param InputInterface $input Console input.
209
     * @param OutputStyle $outputStyle Console style.
210
     * @return FetchCommand $this Fluent interface.
211
     */
212
    protected function displayCommandBegin(InputInterface $input, OutputStyle $outputStyle)
213
    {
214
        $outputStyle->title('Exchange rates:');
215
        $outputStyle->text(
216
            sprintf(
217
                'Fetching from %s for date %s....',
218
                ($input->getOption('source') ? sprintf('"%s"', implode('", "', $input->getOption('source'))) : 'all sources'),
219
                $input->getOption('date')->format('Y-m-d')
220
            )
221
        );
222
223
        return $this;
224
    }
225
226
    /**
227
     * Do fetch rates.
228
     *
229
     * @param InputInterface $input Console input.
230
     * @return RateInterface[] Fetched rates.
231
     * @throws \Exception
232
     */
233
    protected function doFetch(InputInterface $input)
234
    {
235
        try {
236
237
            $rates = $this->manager->fetch($input->getOption('source'), $input->getOption('date'));
238
239
            $this->getLogger()->info(sprintf('Rates fetched from %s for date %s.', $input->getOption('source') ? sprintf('"%s"', implode('", "', $input->getOption('source'))) : 'all sources', $input->getOption('date')->format('Y-m-d')));
240
241
        } catch (\Exception $e) {
242
243
            $this->getLogger()->critical('Unable to fetch rates.', array(
244
                'date' => $input->getOption('date')->format('Y-m-d'),
245
                'sources' => $input->getOption('source') ? sprintf('"%s"', implode('", "', $input->getOption('source'))) : 'All sources',
246
                'exception' => array(
247
                    'message' => $e->getMessage(),
248
                    'code' => $e->getCode(),
249
                    'file' => $e->getFile(),
250
                    'line' => $e->getLine(),
251
                    'trace' => $e->getTraceAsString()
252
                )
253
            ));
254
255
            throw $e;
256
        }
257
258
        return $rates;
259
    }
260
261
    /**
262
     * Display command success note.
263
     *
264
     * @param OutputStyle $outputStyle
265
     * @return FetchCommand $this Fluent interface.
266
     */
267
    protected function displayCommandSuccess(OutputStyle $outputStyle)
268
    {
269
        $outputStyle->success('Exchange rates successfully fetched.');
270
        return $this;
271
    }
272
273
    /**
274
     * Display command error note.
275
     *
276
     * @param OutputStyle $outputStyle
277
     * @return FetchCommand $this Fluent interface.
278
     */
279
    protected function displayCommandError(OutputStyle $outputStyle)
280
    {
281
        $outputStyle->error('Unable to fetch data from source(s). See log for details.');
282
        return $this;
283
    }
284
285
    /**
286
     * Dispatch success notifications.
287
     *
288
     * @param null|array $source Sources for which command is executed.
289
     * @param \DateTime $date Date for which rates are fetched.
290
     * @param RateInterface[] $rates Fetched rates
291
     * @return FetchCommand $this Fluent interface.
292
     */
293
    protected function dispatchSuccessNotifications($source, \DateTime $date, array $rates)
294
    {
295
        foreach ($this->successNotifications as $notification) {
296
297
            $notification->notify(array(
298
                'source' => $source,
299
                'date' => $date,
300
                'rates' => $rates
301
            ));
302
        }
303
304
        return $this;
305
    }
306
307
    /**
308
     * Dispatch error notifications.
309
     *
310
     * @param null|array $source Sources for which command is executed.
311
     * @param \DateTime $date Date for which rates are fetched.
312
     * @return FetchCommand $this Fluent interface.
313
     */
314
    protected function dispatchErrorNotifications($source, \DateTime $date)
315
    {
316
        foreach ($this->errorNotifications as $notification) {
317
318
            $notification->notify(array(
319
                'source' => $source,
320
                'date' => $date
321
            ));
322
        }
323
324
        return $this;
325
    }
326
}
327