Passed
Pull Request — master (#2)
by Kevin
02:04
created

Configuration::taskConfiguration()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 100
Code Lines 88

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 78
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 88
c 0
b 0
f 0
dl 0
loc 100
rs 8.2617
ccs 78
cts 78
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 23
    public function getConfigTreeBuilder(): TreeBuilder
17
    {
18 23
        $treeBuilder = new TreeBuilder('zenstruck_schedule');
19
20 23
        $treeBuilder->getRootNode()
21 23
            ->children()
22 23
                ->scalarNode('without_overlapping_handler')
23 23
                    ->info('The LockFactory service to use')
24 23
                    ->example('lock.default.factory')
25 23
                    ->defaultNull()
26 23
                ->end()
27 23
                ->scalarNode('single_server_handler')
28 23
                    ->info('The LockFactory service to use - be sure to use a "remote store" (https://symfony.com/doc/current/components/lock.html#remote-stores)')
29 23
                    ->example('lock.redis.factory')
30 23
                    ->defaultNull()
31 23
                ->end()
32 23
                ->scalarNode('ping_handler')
33 23
                    ->info('The HttpClient service to use')
34 23
                    ->example('http_client')
35 23
                    ->defaultNull()
36 23
                ->end()
37 23
                ->scalarNode('timezone')
38 23
                    ->info('The timezone for tasks (override at task level), null for system default')
39 23
                    ->example('America/New_York')
40 23
                    ->defaultNull()
41 23
                    ->validate()
42 23
                        ->ifNotInArray(\timezone_identifiers_list())
43 23
                        ->thenInvalid('Timezone %s is not available')
44 23
                    ->end()
45 23
                ->end()
46 23
                ->arrayNode('email_handler')
47 23
                    ->canBeEnabled()
48 23
                    ->children()
49 23
                        ->scalarNode('service')
50 23
                            ->defaultValue('mailer')
51 23
                            ->cannotBeEmpty()
52 23
                            ->info('The mailer service to use')
53 23
                        ->end()
54 23
                        ->scalarNode('default_from')
55 23
                            ->info('The default "from" email address (use if no mailer default from is configured)')
56 23
                            ->defaultNull()
57 23
                        ->end()
58 23
                        ->scalarNode('default_to')
59 23
                            ->info('The default "to" email address (can be overridden by extension)')
60 23
                            ->defaultNull()
61 23
                        ->end()
62 23
                        ->scalarNode('subject_prefix')
63 23
                            ->info('The prefix to use for email subjects (use to distinguish between different application schedules)')
64 23
                            ->example('"[Acme Inc Website]"')
65 23
                            ->defaultNull()
66 23
                        ->end()
67 23
                    ->end()
68 23
                ->end()
69 23
                ->arrayNode('schedule_extensions')
70 23
                    ->addDefaultsIfNotSet()
71 23
                    ->children()
72 23
                        ->arrayNode('environments')
73 23
                            ->beforeNormalization()->castToArray()->end()
74 23
                            ->scalarPrototype()->end()
75 23
                            ->info('Set the environment(s) you only want the schedule to run in.')
76 23
                            ->example('[prod, staging]')
77 23
                        ->end()
78 23
                        ->arrayNode('on_single_server')
79 23
                            ->info('Run schedule on only one server')
80 23
                            ->canBeEnabled()
81 23
                            ->children()
82 23
                                ->integerNode('ttl')
83 23
                                    ->info('Maximum expected lock duration in seconds')
84 23
                                    ->defaultValue(SingleServerExtension::DEFAULT_TTL)
85 23
                                ->end()
86 23
                            ->end()
87 23
                        ->end()
88 23
                        ->append(self::createEmailExtension('email_on_failure', 'Send email if schedule fails'))
89 23
                        ->append(self::createPingExtension('ping_before', 'Ping a url before schedule runs'))
90 23
                        ->append(self::createPingExtension('ping_after', 'Ping a url after schedule runs'))
91 23
                        ->append(self::createPingExtension('ping_on_success', 'Ping a url if the schedule successfully ran'))
92 23
                        ->append(self::createPingExtension('ping_on_failure', 'Ping a url if the schedule failed'))
93 23
                    ->end()
94 23
                ->end()
95 23
                ->append(self::taskConfiguration())
96 23
            ->end()
97
        ;
98
99 23
        return $treeBuilder;
100
    }
101
102 23
    private static function taskConfiguration(): ArrayNodeDefinition
103
    {
104 23
        $treeBuilder = new TreeBuilder('tasks');
105 23
        $node = $treeBuilder->getRootNode();
106
107
        $node
108 23
            ->example([
109
                [
110 23
                    'command' => 'send:sales-report --detailed',
111
                    'frequency' => '0 * * * *',
112
                    'description' => 'Send sales report hourly',
113
                    'without_overlapping' => '~',
114
                    'between' => [
115
                        'start' => '9:00',
116
                        'end' => '17:00',
117
                    ],
118
                    'ping_on_success' => [
119
                        'url' => 'https://example.com/hourly-report-health-check',
120
                    ],
121
                    'email_on_failure' => [
122
                        'to' => '[email protected]',
123
                    ],
124
                ],
125
            ])
126 23
            ->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

126
            ->/** @scrutinizer ignore-call */ arrayPrototype()
Loading history...
127 23
                ->children()
128 23
                    ->scalarNode('command')
129 23
                        ->info('The Symfony command name (with arguments) or process')
130 23
                        ->example('"my:command arg1 --option1=value" or "/bin/my-script"')
131 23
                        ->isRequired()
132 23
                        ->cannotBeEmpty()
133 23
                    ->end()
134 23
                    ->enumNode('type')
135 23
                        ->defaultValue('command')
136 23
                        ->values(['command', 'process'])
137 23
                    ->end()
138 23
                    ->scalarNode('frequency')
139 23
                        ->info('Cron string')
140 23
                        ->example('0 * * * *')
141 23
                        ->isRequired()
142 23
                        ->validate()
143
                            ->ifTrue(function ($v) {
144 7
                                return 5 !== \count(\explode(' ', $v));
145 23
                            })
146 23
                            ->thenInvalid('%s is an invalid cron expression.')
147 23
                        ->end()
148 23
                    ->end()
149 23
                    ->scalarNode('description')
150 23
                        ->info('Task description')
151 23
                        ->defaultNull()
152 23
                    ->end()
153 23
                    ->arrayNode('without_overlapping')
154 23
                        ->info('Prevent task from running if still running from previous run')
155 23
                        ->canBeEnabled()
156 23
                        ->children()
157 23
                            ->integerNode('ttl')
158 23
                                ->info('Maximum expected lock duration in seconds')
159 23
                                ->defaultValue(WithoutOverlappingExtension::DEFAULT_TTL)
160 23
                            ->end()
161 23
                        ->end()
162 23
                    ->end()
163 23
                    ->arrayNode('between')
164 23
                        ->info('Only run between given times')
165 23
                        ->canBeEnabled()
166 23
                        ->children()
167 23
                            ->scalarNode('start')
168 23
                                ->example('9:00')
169 23
                                ->isRequired()
170 23
                            ->end()
171 23
                            ->scalarNode('end')
172 23
                                ->example('17:00')
173 23
                                ->isRequired()
174 23
                            ->end()
175 23
                        ->end()
176 23
                    ->end()
177 23
                    ->arrayNode('unless_between')
178 23
                        ->info('Skip when between given times')
179 23
                        ->canBeEnabled()
180 23
                        ->children()
181 23
                            ->scalarNode('start')
182 23
                                ->example('17:00')
183 23
                                ->isRequired()
184 23
                            ->end()
185 23
                                ->scalarNode('end')
186 23
                                ->example('06:00')
187 23
                                ->isRequired()
188 23
                            ->end()
189 23
                        ->end()
190 23
                    ->end()
191 23
                    ->append(self::createPingExtension('ping_before', 'Ping a url before task runs'))
192 23
                    ->append(self::createPingExtension('ping_after', 'Ping a url after task runs'))
193 23
                    ->append(self::createPingExtension('ping_on_success', 'Ping a url if the task successfully ran'))
194 23
                    ->append(self::createPingExtension('ping_on_failure', 'Ping a url if the task failed'))
195 23
                    ->append(self::createEmailExtension('email_after', 'Send email after task runs'))
196 23
                    ->append(self::createEmailExtension('email_on_failure', 'Send email if task fails'))
197 23
                ->end()
198 23
            ->end()
199
        ;
200
201 23
        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...
202
    }
203
204 23
    private static function createEmailExtension(string $name, string $description): ArrayNodeDefinition
205
    {
206 23
        $treeBuilder = new TreeBuilder($name);
207 23
        $node = $treeBuilder->getRootNode();
208
209
        $node
210 23
            ->info($description)
211 23
            ->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

211
            ->/** @scrutinizer ignore-call */ canBeEnabled()
Loading history...
212 23
            ->beforeNormalization()
213 23
                ->ifString()
214
                ->then(function ($v) {
215
                    return [
216 1
                        'enabled' => true,
217 1
                        'to' => $v,
218
                        'subject' => null,
219
                    ];
220 23
                })
221 23
            ->end()
222 23
            ->children()
223 23
                ->scalarNode('to')
224 23
                    ->info('Email address to send email to (leave blank to use "zenstruck_schedule.email_handler.default_to")')
225 23
                    ->defaultNull()
226 23
                ->end()
227 23
                ->scalarNode('subject')
228 23
                    ->info('Email subject (leave blank to use extension default)')
229 23
                    ->defaultNull()
230 23
                ->end()
231 23
            ->end()
232
        ;
233
234 23
        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...
235
    }
236
237 23
    private static function createPingExtension(string $name, string $description): ArrayNodeDefinition
238
    {
239 23
        $treeBuilder = new TreeBuilder($name);
240 23
        $node = $treeBuilder->getRootNode();
241
242
        $node
243 23
            ->info($description)
244 23
            ->canBeEnabled()
245 23
            ->beforeNormalization()
246 23
                ->ifString()
247
                ->then(function ($v) {
248
                    return [
249 1
                        'enabled' => true,
250 1
                        'url' => $v,
251 1
                        'method' => 'GET',
252
                        'options' => [],
253
                    ];
254 23
                })
255 23
            ->end()
256 23
            ->children()
257 23
                ->scalarNode('url')
258 23
                    ->info('The url to ping')
259 23
                    ->isRequired()
260 23
                    ->cannotBeEmpty()
261 23
                ->end()
262 23
                ->scalarNode('method')
263 23
                    ->info('The HTTP method to use')
264 23
                    ->defaultValue('GET')
265 23
                    ->cannotBeEmpty()
266 23
                ->end()
267 23
                ->arrayNode('options')
268 23
                    ->info('See HttpClientInterface::OPTIONS_DEFAULTS')
269 23
                    ->scalarPrototype()->end()
270 23
                ->end()
271 23
            ->end()
272
        ;
273
274 23
        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...
275
    }
276
}
277