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

Configuration::createEmailExtension()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 21
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 16
c 0
b 0
f 0
dl 0
loc 21
rs 9.7333
ccs 16
cts 16
cp 1
cc 1
nc 1
nop 2
crap 1
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 22
    public function getConfigTreeBuilder(): TreeBuilder
17
    {
18 22
        $treeBuilder = new TreeBuilder('zenstruck_schedule');
19
20 22
        $treeBuilder->getRootNode()
21 22
            ->children()
22 22
                ->scalarNode('without_overlapping_handler')
23 22
                    ->info('The LockFactory service to use')
24 22
                    ->example('lock.default.factory')
25 22
                    ->defaultNull()
26 22
                ->end()
27 22
                ->scalarNode('single_server_handler')
28 22
                    ->info('The LockFactory service to use - be sure to use a "remote store" (https://symfony.com/doc/current/components/lock.html#remote-stores)')
29 22
                    ->example('lock.redis.factory')
30 22
                    ->defaultNull()
31 22
                ->end()
32 22
                ->scalarNode('ping_handler')
33 22
                    ->info('The HttpClient service to use')
34 22
                    ->example('http_client')
35 22
                    ->defaultNull()
36 22
                ->end()
37 22
                ->scalarNode('timezone')
38 22
                    ->info('The timezone for tasks (override at task level), null for system default')
39 22
                    ->example('America/New_York')
40 22
                    ->defaultNull()
41 22
                    ->validate()
42 22
                        ->ifNotInArray(\timezone_identifiers_list())
43 22
                        ->thenInvalid('Timezone %s is not available')
44 22
                    ->end()
45 22
                ->end()
46 22
                ->arrayNode('email_handler')
47 22
                    ->canBeEnabled()
48 22
                    ->children()
49 22
                        ->scalarNode('service')
50 22
                            ->defaultValue('mailer')
51 22
                            ->cannotBeEmpty()
52 22
                            ->info('The mailer service to use')
53 22
                        ->end()
54 22
                        ->scalarNode('default_from')
55 22
                            ->info('The default "from" email address (use if no mailer default from is configured)')
56 22
                            ->defaultNull()
57 22
                        ->end()
58 22
                        ->scalarNode('default_to')
59 22
                            ->info('The default "to" email address (can be overridden by extension)')
60 22
                            ->defaultNull()
61 22
                        ->end()
62 22
                        ->scalarNode('subject_prefix')
63 22
                            ->info('The prefix to use for email subjects (use to distinguish between different application schedules)')
64 22
                            ->example('"[Acme Inc Website]"')
65 22
                            ->defaultNull()
66 22
                        ->end()
67 22
                    ->end()
68 22
                ->end()
69 22
                ->arrayNode('schedule_extensions')
70 22
                    ->addDefaultsIfNotSet()
71 22
                    ->children()
72 22
                        ->arrayNode('environments')
73 22
                            ->beforeNormalization()->castToArray()->end()
74 22
                            ->scalarPrototype()->end()
75 22
                            ->info('Set the environment(s) you only want the schedule to run in.')
76 22
                            ->example('[prod, staging]')
77 22
                        ->end()
78 22
                        ->arrayNode('on_single_server')
79 22
                            ->info('Run schedule on only one server')
80 22
                            ->canBeEnabled()
81 22
                            ->children()
82 22
                                ->integerNode('ttl')
83 22
                                    ->info('Maximum expected lock duration in seconds')
84 22
                                    ->defaultValue(SingleServerExtension::DEFAULT_TTL)
85 22
                                ->end()
86 22
                            ->end()
87 22
                        ->end()
88 22
                        ->append(self::createEmailExtension('email_on_failure', 'Send email if schedule fails'))
89 22
                        ->append(self::createPingExtension('ping_before', 'Ping a url before schedule runs'))
90 22
                        ->append(self::createPingExtension('ping_after', 'Ping a url after schedule runs'))
91 22
                        ->append(self::createPingExtension('ping_on_success', 'Ping a url if the schedule successfully ran'))
92 22
                        ->append(self::createPingExtension('ping_on_failure', 'Ping a url if the schedule failed'))
93 22
                    ->end()
94 22
                ->end()
95 22
                ->append(self::taskConfiguration())
96 22
            ->end()
97
        ;
98
99 22
        return $treeBuilder;
100
    }
101
102 22
    private static function taskConfiguration(): ArrayNodeDefinition
103
    {
104 22
        $treeBuilder = new TreeBuilder('tasks');
105 22
        $node = $treeBuilder->getRootNode();
106
107
        $node
108 22
            ->example([
109
                [
110 22
                    '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 22
            ->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 22
                ->children()
128 22
                    ->scalarNode('command')
129 22
                        ->info('The Symfony command name (with arguments) or process')
130 22
                        ->example('"my:command arg1 --option1=value" or "/bin/my-script"')
131 22
                        ->isRequired()
132 22
                        ->cannotBeEmpty()
133 22
                    ->end()
134 22
                    ->enumNode('type')
135 22
                        ->defaultValue('command')
136 22
                        ->values(['command', 'process'])
137 22
                    ->end()
138 22
                    ->scalarNode('frequency')
139 22
                        ->info('Cron string')
140 22
                        ->example('0 * * * *')
141 22
                        ->isRequired()
142 22
                        ->validate()
143
                            ->ifTrue(function ($v) {
144 6
                                return 5 !== \count(\explode(' ', $v));
145 22
                            })
146 22
                            ->thenInvalid('%s is an invalid cron expression.')
147 22
                        ->end()
148 22
                    ->end()
149 22
                    ->scalarNode('description')
150 22
                        ->info('Task description')
151 22
                        ->defaultNull()
152 22
                    ->end()
153 22
                    ->arrayNode('without_overlapping')
154 22
                        ->info('Prevent task from running if still running from previous run')
155 22
                        ->canBeEnabled()
156 22
                        ->children()
157 22
                            ->integerNode('ttl')
158 22
                                ->info('Maximum expected lock duration in seconds')
159 22
                                ->defaultValue(WithoutOverlappingExtension::DEFAULT_TTL)
160 22
                            ->end()
161 22
                        ->end()
162 22
                    ->end()
163 22
                    ->arrayNode('between')
164 22
                        ->info('Only run between given times')
165 22
                        ->canBeEnabled()
166 22
                        ->children()
167 22
                            ->scalarNode('start')
168 22
                                ->example('9:00')
169 22
                                ->isRequired()
170 22
                            ->end()
171 22
                            ->scalarNode('end')
172 22
                                ->example('17:00')
173 22
                                ->isRequired()
174 22
                            ->end()
175 22
                        ->end()
176 22
                    ->end()
177 22
                    ->arrayNode('unless_between')
178 22
                        ->info('Skip when between given times')
179 22
                        ->canBeEnabled()
180 22
                        ->children()
181 22
                            ->scalarNode('start')
182 22
                                ->example('17:00')
183 22
                                ->isRequired()
184 22
                            ->end()
185 22
                                ->scalarNode('end')
186 22
                                ->example('06:00')
187 22
                                ->isRequired()
188 22
                            ->end()
189 22
                        ->end()
190 22
                    ->end()
191 22
                    ->append(self::createPingExtension('ping_before', 'Ping a url before task runs'))
192 22
                    ->append(self::createPingExtension('ping_after', 'Ping a url after task runs'))
193 22
                    ->append(self::createPingExtension('ping_on_success', 'Ping a url if the task successfully ran'))
194 22
                    ->append(self::createPingExtension('ping_on_failure', 'Ping a url if the task failed'))
195 22
                    ->append(self::createEmailExtension('email_after', 'Send email after task runs'))
196 22
                    ->append(self::createEmailExtension('email_on_failure', 'Send email if task fails'))
197 22
                ->end()
198 22
            ->end()
199
        ;
200
201 22
        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 22
    private static function createEmailExtension(string $name, string $description): ArrayNodeDefinition
205
    {
206 22
        $treeBuilder = new TreeBuilder($name);
207 22
        $node = $treeBuilder->getRootNode();
208
209
        $node
210 22
            ->info($description)
211 22
            ->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 22
            ->children()
213 22
                ->scalarNode('to')
214 22
                    ->info('Email address to send email to (leave blank to use "zenstruck_schedule.email_handler.default_to")')
215 22
                    ->defaultNull()
216 22
                ->end()
217 22
                ->scalarNode('subject')
218 22
                    ->info('Email subject (leave blank to use extension default)')
219 22
                    ->defaultNull()
220 22
                ->end()
221 22
            ->end()
222
        ;
223
224 22
        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...
225
    }
226
227 22
    private static function createPingExtension(string $name, string $description): ArrayNodeDefinition
228
    {
229 22
        $treeBuilder = new TreeBuilder($name);
230 22
        $node = $treeBuilder->getRootNode();
231
232
        $node
233 22
            ->info($description)
234 22
            ->canBeEnabled()
235 22
            ->children()
236 22
                ->scalarNode('url')
237 22
                    ->info('The url to ping')
238 22
                    ->isRequired()
239 22
                    ->cannotBeEmpty()
240 22
                ->end()
241 22
                ->scalarNode('method')
242 22
                    ->info('The HTTP method to use')
243 22
                    ->defaultValue('GET')
244 22
                    ->cannotBeEmpty()
245 22
                ->end()
246 22
                ->arrayNode('options')
247 22
                    ->info('See HttpClientInterface::OPTIONS_DEFAULTS')
248 22
                    ->scalarPrototype()->end()
249 22
                ->end()
250 22
            ->end()
251
        ;
252
253 22
        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...
254
    }
255
}
256