Passed
Pull Request — master (#2)
by Kevin
01:48
created

Configuration::taskConfiguration()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 126
Code Lines 111

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 103
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 111
c 0
b 0
f 0
dl 0
loc 126
rs 8
ccs 103
cts 103
cp 1
cc 1
nc 1
nop 0
crap 1

How to fix   Long Method   

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:

1
<?php
2
3
namespace Zenstruck\ScheduleBundle\DependencyInjection;
4
5
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
6
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
7
use Symfony\Component\Config\Definition\ConfigurationInterface;
8
use Zenstruck\ScheduleBundle\Schedule\Extension\SingleServerExtension;
9
use Zenstruck\ScheduleBundle\Schedule\Extension\WithoutOverlappingExtension;
10
11
/**
12
 * @author Kevin Bond <[email protected]>
13
 */
14
final class Configuration implements ConfigurationInterface
15
{
16 26
    public function getConfigTreeBuilder(): TreeBuilder
17
    {
18 26
        $treeBuilder = new TreeBuilder('zenstruck_schedule');
19
20 26
        $treeBuilder->getRootNode()
21 26
            ->children()
22 26
                ->scalarNode('without_overlapping_handler')
23 26
                    ->info('The LockFactory service to use')
24 26
                    ->example('lock.default.factory')
25 26
                    ->defaultNull()
26 26
                ->end()
27 26
                ->scalarNode('single_server_handler')
28 26
                    ->info('The LockFactory service to use - be sure to use a "remote store" (https://symfony.com/doc/current/components/lock.html#remote-stores)')
29 26
                    ->example('lock.redis.factory')
30 26
                    ->defaultNull()
31 26
                ->end()
32 26
                ->scalarNode('ping_handler')
33 26
                    ->info('The HttpClient service to use')
34 26
                    ->example('http_client')
35 26
                    ->defaultNull()
36 26
                ->end()
37 26
                ->scalarNode('timezone')
38 26
                    ->info('The timezone for tasks (override at task level), null for system default')
39 26
                    ->example('America/New_York')
40 26
                    ->defaultNull()
41 26
                    ->validate()
42 26
                        ->ifNotInArray(\timezone_identifiers_list())
43 26
                        ->thenInvalid('Timezone %s is not available')
44 26
                    ->end()
45 26
                ->end()
46 26
                ->arrayNode('email_handler')
47 26
                    ->canBeEnabled()
48 26
                    ->children()
49 26
                        ->scalarNode('service')
50 26
                            ->defaultValue('mailer')
51 26
                            ->cannotBeEmpty()
52 26
                            ->info('The mailer service to use')
53 26
                        ->end()
54 26
                        ->scalarNode('default_from')
55 26
                            ->info('The default "from" email address (use if no mailer default from is configured)')
56 26
                            ->defaultNull()
57 26
                        ->end()
58 26
                        ->scalarNode('default_to')
59 26
                            ->info('The default "to" email address (can be overridden by extension)')
60 26
                            ->defaultNull()
61 26
                        ->end()
62 26
                        ->scalarNode('subject_prefix')
63 26
                            ->info('The prefix to use for email subjects (use to distinguish between different application schedules)')
64 26
                            ->example('"[Acme Inc Website]"')
65 26
                            ->defaultNull()
66 26
                        ->end()
67 26
                    ->end()
68 26
                ->end()
69 26
                ->arrayNode('schedule_extensions')
70 26
                    ->addDefaultsIfNotSet()
71 26
                    ->children()
72 26
                        ->arrayNode('environments')
73 26
                            ->beforeNormalization()->castToArray()->end()
74 26
                            ->scalarPrototype()->end()
75 26
                            ->info('Set the environment(s) you only want the schedule to run in.')
76 26
                            ->example('[prod, staging]')
77 26
                        ->end()
78 26
                        ->arrayNode('on_single_server')
79 26
                            ->info('Run schedule on only one server')
80 26
                            ->canBeEnabled()
81 26
                            ->children()
82 26
                                ->integerNode('ttl')
83 26
                                    ->info('Maximum expected lock duration in seconds')
84 26
                                    ->defaultValue(SingleServerExtension::DEFAULT_TTL)
85 26
                                ->end()
86 26
                            ->end()
87 26
                        ->end()
88 26
                        ->append(self::createEmailExtension('email_on_failure', 'Send email if schedule fails'))
89 26
                        ->append(self::createPingExtension('ping_before', 'Ping a url before schedule runs'))
90 26
                        ->append(self::createPingExtension('ping_after', 'Ping a url after schedule runs'))
91 26
                        ->append(self::createPingExtension('ping_on_success', 'Ping a url if the schedule successfully ran'))
92 26
                        ->append(self::createPingExtension('ping_on_failure', 'Ping a url if the schedule failed'))
93 26
                    ->end()
94 26
                ->end()
95 26
                ->append(self::taskConfiguration())
96 26
            ->end()
97
        ;
98
99 26
        return $treeBuilder;
100
    }
101
102 26
    private static function taskConfiguration(): ArrayNodeDefinition
103
    {
104 26
        $treeBuilder = new TreeBuilder('tasks');
105 26
        $node = $treeBuilder->getRootNode();
106
107
        $node
108 26
            ->example([
109
                [
110 26
                    'command' => 'send:sales-report --detailed',
111
                    'frequency' => '0 * * * *',
112
                    'description' => 'Send sales report hourly',
113
                    'without_overlapping' => '~',
114
                    'between' => '9-17',
115
                    'ping_on_success' => 'https://example.com/hourly-report-health-check',
116
                    'email_on_failure' => '[email protected]',
117
                ],
118
            ])
119 26
            ->arrayPrototype()
0 ignored issues
show
Bug introduced by
The method arrayPrototype() does not exist on Symfony\Component\Config...\Builder\NodeDefinition. It seems like you code against a sub-type of Symfony\Component\Config...\Builder\NodeDefinition such as Symfony\Component\Config...der\ArrayNodeDefinition. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

119
            ->/** @scrutinizer ignore-call */ arrayPrototype()
Loading history...
120 26
                ->children()
121 26
                    ->arrayNode('command')
122 26
                        ->info('Defaults to CommandTask, prefix with "bash:" to create ProcessTask, pass array of commands to create CompoundTask (optionally keyed by description)')
123 26
                        ->example('"my:command arg1 --option1=value" or "bash:/bin/my-script"')
124 26
                        ->beforeNormalization()
125 26
                            ->castToArray()
126 26
                        ->end()
127 26
                        ->isRequired()
128 26
                        ->cannotBeEmpty()
129 26
                        ->scalarPrototype()->end()
130 26
                    ->end()
131 26
                    ->scalarNode('frequency')
132 26
                        ->info('Cron string')
133 26
                        ->example('0 * * * *')
134 26
                        ->isRequired()
135 26
                        ->validate()
136
                            ->ifTrue(function ($v) {
137 10
                                return 5 !== \count(\explode(' ', $v));
138 26
                            })
139 26
                            ->thenInvalid('%s is an invalid cron expression.')
140 26
                        ->end()
141 26
                    ->end()
142 26
                    ->scalarNode('description')
143 26
                        ->info('Task description')
144 26
                        ->defaultNull()
145 26
                    ->end()
146 26
                    ->scalarNode('timezone')
147 26
                        ->info('The timezone for this task, null for system default')
148 26
                        ->example('America/New_York')
149 26
                        ->defaultNull()
150 26
                        ->validate()
151 26
                            ->ifNotInArray(\timezone_identifiers_list())
152 26
                            ->thenInvalid('Timezone %s is not available')
153 26
                        ->end()
154 26
                    ->end()
155 26
                    ->arrayNode('without_overlapping')
156 26
                        ->info('Prevent task from running if still running from previous run')
157 26
                        ->canBeEnabled()
158 26
                        ->children()
159 26
                            ->integerNode('ttl')
160 26
                                ->info('Maximum expected lock duration in seconds')
161 26
                                ->defaultValue(WithoutOverlappingExtension::DEFAULT_TTL)
162 26
                            ->end()
163 26
                        ->end()
164 26
                    ->end()
165 26
                    ->arrayNode('between')
166 26
                        ->info('Only run between given times (alternatively enable by passing a range, ie "9:00-17:00"')
167 26
                        ->canBeEnabled()
168 26
                        ->beforeNormalization()
169 26
                            ->ifString()
170
                            ->then(function ($v) {
171 1
                                [$start, $end] = \explode('-', $v);
172
173
                                return [
174 1
                                    'enabled' => true,
175 1
                                    'start' => $start,
176 1
                                    'end' => $end,
177
                                ];
178 26
                            })
179 26
                        ->end()
180 26
                        ->children()
181 26
                            ->scalarNode('start')
182 26
                                ->example('9:00')
183 26
                                ->isRequired()
184 26
                            ->end()
185 26
                            ->scalarNode('end')
186 26
                                ->example('17:00')
187 26
                                ->isRequired()
188 26
                            ->end()
189 26
                        ->end()
190 26
                    ->end()
191 26
                    ->arrayNode('unless_between')
192 26
                        ->info('Skip when between given times (alternatively enable by passing a range, ie "17:00-06:00"')
193 26
                        ->canBeEnabled()
194 26
                        ->beforeNormalization()
195 26
                            ->ifString()
196
                            ->then(function ($v) {
197 1
                                [$start, $end] = \explode('-', $v);
198
199
                                return [
200 1
                                    'enabled' => true,
201 1
                                    'start' => $start,
202 1
                                    'end' => $end,
203
                                ];
204 26
                            })
205 26
                        ->end()
206 26
                        ->children()
207 26
                            ->scalarNode('start')
208 26
                                ->example('17:00')
209 26
                                ->isRequired()
210 26
                            ->end()
211 26
                                ->scalarNode('end')
212 26
                                ->example('06:00')
213 26
                                ->isRequired()
214 26
                            ->end()
215 26
                        ->end()
216 26
                    ->end()
217 26
                    ->append(self::createPingExtension('ping_before', 'Ping a url before task runs'))
218 26
                    ->append(self::createPingExtension('ping_after', 'Ping a url after task runs'))
219 26
                    ->append(self::createPingExtension('ping_on_success', 'Ping a url if the task successfully ran'))
220 26
                    ->append(self::createPingExtension('ping_on_failure', 'Ping a url if the task failed'))
221 26
                    ->append(self::createEmailExtension('email_after', 'Send email after task runs'))
222 26
                    ->append(self::createEmailExtension('email_on_failure', 'Send email if task fails'))
223 26
                ->end()
224 26
            ->end()
225
        ;
226
227 26
        return $node;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $node returns the type Symfony\Component\Config...\Builder\NodeDefinition which includes types incompatible with the type-hinted return Symfony\Component\Config...der\ArrayNodeDefinition.
Loading history...
228
    }
229
230 26
    private static function createEmailExtension(string $name, string $description): ArrayNodeDefinition
231
    {
232 26
        $treeBuilder = new TreeBuilder($name);
233 26
        $node = $treeBuilder->getRootNode();
234
235
        $node
236 26
            ->info($description.' (alternatively enable by passing a "to" email)')
237 26
            ->canBeEnabled()
0 ignored issues
show
Bug introduced by
The method canBeEnabled() does not exist on Symfony\Component\Config...\Builder\NodeDefinition. It seems like you code against a sub-type of Symfony\Component\Config...\Builder\NodeDefinition such as Symfony\Component\Config...der\ArrayNodeDefinition. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

237
            ->/** @scrutinizer ignore-call */ canBeEnabled()
Loading history...
238 26
            ->beforeNormalization()
239 26
                ->ifString()
240
                ->then(function ($v) {
241
                    return [
242 1
                        'enabled' => true,
243 1
                        'to' => $v,
244
                        'subject' => null,
245
                    ];
246 26
                })
247 26
            ->end()
248 26
            ->children()
249 26
                ->scalarNode('to')
250 26
                    ->info('Email address to send email to (leave blank to use "zenstruck_schedule.email_handler.default_to")')
251 26
                    ->defaultNull()
252 26
                ->end()
253 26
                ->scalarNode('subject')
254 26
                    ->info('Email subject (leave blank to use extension default)')
255 26
                    ->defaultNull()
256 26
                ->end()
257 26
            ->end()
258
        ;
259
260 26
        return $node;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $node returns the type Symfony\Component\Config...\Builder\NodeDefinition which includes types incompatible with the type-hinted return Symfony\Component\Config...der\ArrayNodeDefinition.
Loading history...
261
    }
262
263 26
    private static function createPingExtension(string $name, string $description): ArrayNodeDefinition
264
    {
265 26
        $treeBuilder = new TreeBuilder($name);
266 26
        $node = $treeBuilder->getRootNode();
267
268
        $node
269 26
            ->info($description.' (alternatively enable by passing a url)')
270 26
            ->canBeEnabled()
271 26
            ->beforeNormalization()
272 26
                ->ifString()
273
                ->then(function ($v) {
274
                    return [
275 1
                        'enabled' => true,
276 1
                        'url' => $v,
277 1
                        'method' => 'GET',
278
                        'options' => [],
279
                    ];
280 26
                })
281 26
            ->end()
282 26
            ->children()
283 26
                ->scalarNode('url')
284 26
                    ->info('The url to ping')
285 26
                    ->isRequired()
286 26
                    ->cannotBeEmpty()
287 26
                ->end()
288 26
                ->scalarNode('method')
289 26
                    ->info('The HTTP method to use')
290 26
                    ->defaultValue('GET')
291 26
                    ->cannotBeEmpty()
292 26
                ->end()
293 26
                ->arrayNode('options')
294 26
                    ->info('See HttpClientInterface::OPTIONS_DEFAULTS')
295 26
                    ->scalarPrototype()->end()
296 26
                ->end()
297 26
            ->end()
298
        ;
299
300 26
        return $node;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $node returns the type Symfony\Component\Config...\Builder\NodeDefinition which includes types incompatible with the type-hinted return Symfony\Component\Config...der\ArrayNodeDefinition.
Loading history...
301
    }
302
}
303