Passed
Push — master ( 0002a7...9f89d6 )
by Kevin
01:45
created

EmailHandler   A

Complexity

Total Complexity 26

Size/Duplication

Total Lines 133
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 26
eloc 55
c 1
b 0
f 0
dl 0
loc 133
ccs 66
cts 66
cp 1
rs 10

10 Methods

Rating   Name   Duplication   Size   Complexity  
A sendTaskEmail() 0 17 3
A prefixSubject() 0 5 2
A getTaskOutput() 0 13 3
A onScheduleFailure() 0 4 2
A emailHeaderFor() 0 17 6
A supports() 0 3 1
A afterTask() 0 4 2
A __construct() 0 6 1
A sendScheduleEmail() 0 23 4
A onTaskFailure() 0 4 2
1
<?php
2
3
namespace Zenstruck\ScheduleBundle\Schedule\Extension\Handler;
4
5
use Symfony\Component\Mailer\MailerInterface;
6
use Symfony\Component\Mime\Address;
7
use Symfony\Component\Mime\Email;
8
use Zenstruck\ScheduleBundle\Event\AfterScheduleEvent;
9
use Zenstruck\ScheduleBundle\Event\AfterTaskEvent;
10
use Zenstruck\ScheduleBundle\Schedule\Extension;
11
use Zenstruck\ScheduleBundle\Schedule\Extension\EmailExtension;
12
use Zenstruck\ScheduleBundle\Schedule\Extension\ExtensionHandler;
13
use Zenstruck\ScheduleBundle\Schedule\Task\Result;
14
15
/**
16
 * @author Kevin Bond <[email protected]>
17
 */
18
final class EmailHandler extends ExtensionHandler
19
{
20
    private $mailer;
21
    private $defaultFrom;
22
    private $defaultTo;
23
    private $subjectPrefix;
24
25 10
    public function __construct(MailerInterface $mailer, string $defaultFrom = null, string $defaultTo = null, string $subjectPrefix = null)
26
    {
27 10
        $this->mailer = $mailer;
28 10
        $this->defaultFrom = $defaultFrom;
29 10
        $this->defaultTo = $defaultTo;
30 10
        $this->subjectPrefix = $subjectPrefix;
31 10
    }
32
33
    /**
34
     * @param EmailExtension $extension
35
     */
36 3
    public function onScheduleFailure(AfterScheduleEvent $event, Extension $extension): void
37
    {
38 3
        if ($extension->isHook(Extension::SCHEDULE_FAILURE)) {
0 ignored issues
show
Bug introduced by
The method isHook() does not exist on Zenstruck\ScheduleBundle\Schedule\Extension. It seems like you code against a sub-type of Zenstruck\ScheduleBundle\Schedule\Extension such as Zenstruck\ScheduleBundle...xtension\EmailExtension. ( Ignorable by Annotation )

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

38
        if ($extension->/** @scrutinizer ignore-call */ isHook(Extension::SCHEDULE_FAILURE)) {
Loading history...
39 3
            $this->sendScheduleEmail($event, $extension);
40
        }
41 3
    }
42
43
    /**
44
     * @param EmailExtension $extension
45
     */
46 7
    public function afterTask(AfterTaskEvent $event, Extension $extension): void
47
    {
48 7
        if ($extension->isHook(Extension::TASK_AFTER)) {
49 3
            $this->sendTaskEmail($extension, $event->getResult());
50
        }
51 7
    }
52
53
    /**
54
     * @param EmailExtension $extension
55
     */
56 4
    public function onTaskFailure(AfterTaskEvent $event, Extension $extension): void
57
    {
58 4
        if ($extension->isHook(Extension::TASK_FAILURE)) {
59 4
            $this->sendTaskEmail($extension, $event->getResult());
60
        }
61 3
    }
62
63 10
    public function supports(Extension $extension): bool
64
    {
65 10
        return $extension instanceof EmailExtension;
66
    }
67
68 3
    private function sendScheduleEmail(AfterScheduleEvent $event, EmailExtension $extension): void
69
    {
70 3
        $email = $this->emailHeaderFor($extension);
71 3
        $failureCount = \count($event->getFailures());
72 3
        $summary = \sprintf('%d task%s failed', $failureCount, $failureCount > 1 ? 's' : '');
73 3
        $text = $summary;
74
75 3
        $email->priority(Email::PRIORITY_HIGHEST);
76 3
        $this->prefixSubject($email, "[Scheduled Failed] {$summary}");
77
78 3
        foreach ($event->getFailures() as $i => $failure) {
79 3
            $task = $failure->getTask();
80 3
            $text .= \sprintf("\n\n# (Failure %d/%d) %s: %s\n\n%s", $i + 1, $failureCount, $task->getType(), $task, $failure);
81 3
            $text .= $this->getTaskOutput($failure);
82
83 3
            if ($i < $failureCount - 1) {
84 3
                $text .= "\n\n---";
85
            }
86
        }
87
88 3
        $email->text($text);
89
90 3
        $this->mailer->send($email);
91 3
    }
92
93 9
    private function getTaskOutput(Result $result): string
94
    {
95 9
        $output = '';
96
97 9
        if ($result->getOutput()) {
98 6
            $output .= "\n\n## Task Output:\n\n{$result->getOutput()}";
99
        }
100
101 9
        if ($result->isException()) {
102 3
            $output .= "\n\n## Exception:\n\n{$result->getException()}";
103
        }
104
105 9
        return $output;
106
    }
107
108 7
    private function sendTaskEmail(EmailExtension $extension, Result $result): void
109
    {
110 7
        $email = $this->emailHeaderFor($extension);
111
112 6
        $this->prefixSubject($email, \sprintf('[Scheduled Task %s] %s: %s',
113 6
            $result->isFailure() ? 'Failed' : 'Succeeded',
114 6
            $result->getTask()->getType(),
115 6
            $result->getTask()
116
        ));
117
118 6
        if ($result->isFailure()) {
119 3
            $email->priority(Email::PRIORITY_HIGHEST);
120
        }
121
122 6
        $email->text($result->getDescription().$this->getTaskOutput($result));
123
124 6
        $this->mailer->send($email);
125 6
    }
126
127 10
    private function emailHeaderFor(EmailExtension $extension): Email
128
    {
129 10
        $email = $extension->getEmail();
130
131 10
        if (null !== $this->defaultTo && empty($email->getFrom())) {
132 9
            $email->from(Address::fromString($this->defaultFrom));
0 ignored issues
show
Bug introduced by
It seems like $this->defaultFrom can also be of type null; however, parameter $string of Symfony\Component\Mime\Address::fromString() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

132
            $email->from(Address::fromString(/** @scrutinizer ignore-type */ $this->defaultFrom));
Loading history...
133
        }
134
135 10
        if (null !== $this->defaultTo && empty($email->getTo())) {
136 6
            $email->to(Address::fromString($this->defaultTo));
137
        }
138
139 10
        if (empty($email->getTo())) {
140 1
            throw new \LogicException('There is no "To" configured for the email. Either set it when adding the extension or in your configuration (config path: "zenstruck_schedule.email_handler.default_to").');
141
        }
142
143 9
        return $email;
144
    }
145
146 9
    private function prefixSubject(Email $email, string $defaultSubject): void
147
    {
148 9
        $subject = $email->getSubject() ?: $defaultSubject;
149
150 9
        $email->subject($this->subjectPrefix.$subject);
151 9
    }
152
}
153