Passed
Pull Request — master (#2)
by Kevin
03:06 queued 01:22
created

Configuration::taskConfiguration()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 117
Code Lines 102

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 94
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 102
c 0
b 0
f 0
dl 0
loc 117
rs 8
ccs 94
cts 94
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
                    ->arrayNode('without_overlapping')
147 26
                        ->info('Prevent task from running if still running from previous run')
148 26
                        ->canBeEnabled()
149 26
                        ->children()
150 26
                            ->integerNode('ttl')
151 26
                                ->info('Maximum expected lock duration in seconds')
152 26
                                ->defaultValue(WithoutOverlappingExtension::DEFAULT_TTL)
153 26
                            ->end()
154 26
                        ->end()
155 26
                    ->end()
156 26
                    ->arrayNode('between')
157 26
                        ->info('Only run between given times (alternatively enable by passing a range, ie "9:00-17:00"')
158 26
                        ->canBeEnabled()
159 26
                        ->beforeNormalization()
160 26
                            ->ifString()
161
                            ->then(function ($v) {
162 1
                                [$start, $end] = \explode('-', $v);
163
164
                                return [
165 1
                                    'enabled' => true,
166 1
                                    'start' => $start,
167 1
                                    'end' => $end,
168
                                ];
169 26
                            })
170 26
                        ->end()
171 26
                        ->children()
172 26
                            ->scalarNode('start')
173 26
                                ->example('9:00')
174 26
                                ->isRequired()
175 26
                            ->end()
176 26
                            ->scalarNode('end')
177 26
                                ->example('17:00')
178 26
                                ->isRequired()
179 26
                            ->end()
180 26
                        ->end()
181 26
                    ->end()
182 26
                    ->arrayNode('unless_between')
183 26
                        ->info('Skip when between given times (alternatively enable by passing a range, ie "17:00-06:00"')
184 26
                        ->canBeEnabled()
185 26
                        ->beforeNormalization()
186 26
                            ->ifString()
187
                            ->then(function ($v) {
188 1
                                [$start, $end] = \explode('-', $v);
189
190
                                return [
191 1
                                    'enabled' => true,
192 1
                                    'start' => $start,
193 1
                                    'end' => $end,
194
                                ];
195 26
                            })
196 26
                        ->end()
197 26
                        ->children()
198 26
                            ->scalarNode('start')
199 26
                                ->example('17:00')
200 26
                                ->isRequired()
201 26
                            ->end()
202 26
                                ->scalarNode('end')
203 26
                                ->example('06:00')
204 26
                                ->isRequired()
205 26
                            ->end()
206 26
                        ->end()
207 26
                    ->end()
208 26
                    ->append(self::createPingExtension('ping_before', 'Ping a url before task runs'))
209 26
                    ->append(self::createPingExtension('ping_after', 'Ping a url after task runs'))
210 26
                    ->append(self::createPingExtension('ping_on_success', 'Ping a url if the task successfully ran'))
211 26
                    ->append(self::createPingExtension('ping_on_failure', 'Ping a url if the task failed'))
212 26
                    ->append(self::createEmailExtension('email_after', 'Send email after task runs'))
213 26
                    ->append(self::createEmailExtension('email_on_failure', 'Send email if task fails'))
214 26
                ->end()
215 26
            ->end()
216
        ;
217
218 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...
219
    }
220
221 26
    private static function createEmailExtension(string $name, string $description): ArrayNodeDefinition
222
    {
223 26
        $treeBuilder = new TreeBuilder($name);
224 26
        $node = $treeBuilder->getRootNode();
225
226
        $node
227 26
            ->info($description.' (alternatively enable by passing a "to" email)')
228 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

228
            ->/** @scrutinizer ignore-call */ canBeEnabled()
Loading history...
229 26
            ->beforeNormalization()
230 26
                ->ifString()
231
                ->then(function ($v) {
232
                    return [
233 1
                        'enabled' => true,
234 1
                        'to' => $v,
235
                        'subject' => null,
236
                    ];
237 26
                })
238 26
            ->end()
239 26
            ->children()
240 26
                ->scalarNode('to')
241 26
                    ->info('Email address to send email to (leave blank to use "zenstruck_schedule.email_handler.default_to")')
242 26
                    ->defaultNull()
243 26
                ->end()
244 26
                ->scalarNode('subject')
245 26
                    ->info('Email subject (leave blank to use extension default)')
246 26
                    ->defaultNull()
247 26
                ->end()
248 26
            ->end()
249
        ;
250
251 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...
252
    }
253
254 26
    private static function createPingExtension(string $name, string $description): ArrayNodeDefinition
255
    {
256 26
        $treeBuilder = new TreeBuilder($name);
257 26
        $node = $treeBuilder->getRootNode();
258
259
        $node
260 26
            ->info($description.' (alternatively enable by passing a url)')
261 26
            ->canBeEnabled()
262 26
            ->beforeNormalization()
263 26
                ->ifString()
264
                ->then(function ($v) {
265
                    return [
266 1
                        'enabled' => true,
267 1
                        'url' => $v,
268 1
                        'method' => 'GET',
269
                        'options' => [],
270
                    ];
271 26
                })
272 26
            ->end()
273 26
            ->children()
274 26
                ->scalarNode('url')
275 26
                    ->info('The url to ping')
276 26
                    ->isRequired()
277 26
                    ->cannotBeEmpty()
278 26
                ->end()
279 26
                ->scalarNode('method')
280 26
                    ->info('The HTTP method to use')
281 26
                    ->defaultValue('GET')
282 26
                    ->cannotBeEmpty()
283 26
                ->end()
284 26
                ->arrayNode('options')
285 26
                    ->info('See HttpClientInterface::OPTIONS_DEFAULTS')
286 26
                    ->scalarPrototype()->end()
287 26
                ->end()
288 26
            ->end()
289
        ;
290
291 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...
292
    }
293
}
294