Completed
Push — master ( 72cf95...b4fc03 )
by Artem
08:38
created

MessageBuilder::messageIsValid()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 16
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 16
rs 9.2
c 0
b 0
f 0
cc 4
eloc 8
nc 4
nop 0
1
<?php
2
/*
3
 * This file is part of the FirebaseCloudMessagingBundle
4
 *
5
 * (c) Artem Henvald <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
declare(strict_types=1);
12
13
namespace Fresh\FirebaseCloudMessagingBundle\Message\Builder;
14
15
use Fresh\FirebaseCloudMessagingBundle\Message\Builder\Payload\AndroidPayloadBuilder;
16
use Fresh\FirebaseCloudMessagingBundle\Message\Builder\Payload\IosPayloadBuilder;
17
use Fresh\FirebaseCloudMessagingBundle\Message\Builder\Payload\WebPayloadBuilder;
18
use Fresh\FirebaseCloudMessagingBundle\Message\Part\Options\Options;
19
use Fresh\FirebaseCloudMessagingBundle\Message\Part\Options\OptionsInterface;
20
use Fresh\FirebaseCloudMessagingBundle\Message\Part\Options\Priority;
21
use Fresh\FirebaseCloudMessagingBundle\Message\Part\Payload\Combined\CombinedPayload;
22
use Fresh\FirebaseCloudMessagingBundle\Message\Part\Payload\CommonPayloadInterface;
23
use Fresh\FirebaseCloudMessagingBundle\Message\Part\Payload\Data\DataPayload;
24
use Fresh\FirebaseCloudMessagingBundle\Message\Part\Payload\Notification\AbstractCommonNotificationPayload;
25
use Fresh\FirebaseCloudMessagingBundle\Message\Part\Payload\Notification\AndroidNotificationPayload;
26
use Fresh\FirebaseCloudMessagingBundle\Message\Part\Payload\Notification\IosNotificationPayload;
27
use Fresh\FirebaseCloudMessagingBundle\Message\Part\Payload\Notification\WebNotificationPayload;
28
use Fresh\FirebaseCloudMessagingBundle\Message\Part\Target;
29
use Fresh\FirebaseCloudMessagingBundle\Message\Type\AbstractMessage;
30
31
/**
32
 * MessageBuilder.
33
 *
34
 * @author Artem Henvald <[email protected]>
35
 *
36
 * @todo Add validation while building message
37
 */
38
class MessageBuilder
39
{
40
    /** @var AbstractMessage */
41
    private $message;
42
43
    /** @var array */
44
    private $targetPart = [];
45
46
    /** @var array */
47
    private $optionsPart = [];
48
49
    /** @var array */
50
    private $payloadPart = [];
51
52
    /**
53
     * @param AbstractMessage $message
54
     */
55
    public function setMessage(AbstractMessage $message)
56
    {
57
        $this->message = $message;
58
    }
59
60
    /**
61
     * @throws \Exception
62
     *
63
     * @return array
64
     */
65
    public function getMessagePartsAsArray(): array
66
    {
67
        if (!$this->messageIsValid()) {
68
            throw new \RuntimeException('Message is not valid');
69
        }
70
71
        $this->buildTargetPart();
72
        $this->buildOptionsPart();
73
        $this->buildPayloadPart();
74
75
        return \array_merge($this->targetPart, $this->optionsPart, $this->payloadPart);
76
    }
77
78
    /**
79
     * @return string
80
     */
81
    public function getMessageAsJson(): string
82
    {
83
        return \json_encode($this->getMessagePartsAsArray(), true);
84
    }
85
86
    /**
87
     * Check if message is valid (has all required parts).
88
     *
89
     * Target and payload are required parts. Options can be omitted.
90
     *
91
     * @throws \RuntimeException
92
     *
93
     * @return bool
94
     */
95
    private function messageIsValid(): bool
96
    {
97
        if (!$this->message instanceof AbstractMessage) {
98
            throw new \RuntimeException(\sprintf('Message is not instance of %s', AbstractMessage::class));
99
        }
100
101
        if (!$this->message->getTarget() instanceof Target\TargetInterface) {
102
            throw new \RuntimeException(\sprintf('Message target is not instance of %s', Target\TargetInterface::class));
103
        }
104
105
        if (!$this->message->getPayload() instanceof CommonPayloadInterface) {
106
            throw new \RuntimeException(\sprintf('Message target is not instance of %s', CommonPayloadInterface::class));
107
        }
108
109
        return true;
110
    }
111
112
    /**
113
     * Build target part.
114
     */
115
    private function buildTargetPart(): void
116
    {
117
        $target = $this->message->getTarget();
118
119
        if ($target instanceof Target\ConditionTarget) {
120
            $this->targetPart = ['condition' => $target->getCondition()];
121
        } elseif ($target instanceof Target\MulticastTarget) {
122
            $this->targetPart = ['registration_ids' => $target->getRegistrationTokens()];
123
        } elseif ($target instanceof Target\SingleRecipientTarget) {
124
            $this->targetPart = ['to' => $target->getRegistrationToken()];
125
        } else {
126
            throw new \InvalidArgumentException('Unsupported target part');
127
        }
128
    }
129
130
    /**
131
     * Build options part.
132
     */
133
    private function buildOptionsPart(): void
134
    {
135
        if ($this->message instanceof AbstractMessage && $this->message->getOptions() instanceof OptionsInterface) {
136
            $options = $this->message->getOptions();
137
            $this->optionsPart = [];
138
139
            if (!empty($options->getCollapseKey())) {
140
                $this->optionsPart['collapse_key'] = (string) $options->getCollapseKey();
141
            }
142
143
            // By default, messages are sent with normal priority.
144
            // If priority is different add it to the set of options.
145
            if (Priority::NORMAL !== $options->getPriority()) {
146
                $this->optionsPart['priority'] = (string) $options->getPriority();
147
            }
148
149
            // By default `content_available` option is false. Adding it only if it was changed to true.
150
            if ($options->isContentAvailable()) {
151
                $this->optionsPart['content_available'] = true;
152
            }
153
154
            // By default TTL for message in FCM is 4 weeks, it is also the default value if you omitted the TTL option.
155
            // So if the TTL is overwritten and is not equal to the default value, then add this option.
156
            // Otherwise if TTL is still equal to default, then it is not need to send this option.
157
            if (Options::DEFAULT_TTL_IN_SECONDS !== $options->getTimeToLive()) {
158
                $this->optionsPart['time_to_live'] = $options->getTimeToLive();
159
            }
160
161
            if (!empty($options->getRestrictedPackageName())) {
162
                $this->optionsPart['restricted_package_name'] = (string) $options->getRestrictedPackageName();
163
            }
164
165
            // By default `dry_run` option is.... @todo
166
            if ($options->isDryRun()) {
167
                $this->optionsPart['dry_run'] = true;
168
            }
169
        }
170
    }
171
172
    /**
173
     * Build payload part.
174
     */
175
    private function buildPayloadPart(): void
176
    {
177
        $payload = $this->message->getPayload();
178
179
        if ($payload instanceof AbstractCommonNotificationPayload) {
180
            $this->payloadPart['notification'] = $this->buildNotificationPayloadPart($payload);
181
        } elseif ($payload instanceof DataPayload) {
182
            $this->payloadPart['data'] = $payload->getData();
183
        } elseif ($payload instanceof CombinedPayload) {
184
            $this->payloadPart = [
185
                'notification' => $this->buildNotificationPayloadPart($payload->getNotificationPayload()),
186
                'data' => $payload->getDataPayload()->getData(),
187
            ];
188
        } else {
189
            throw new \InvalidArgumentException('Unsupported payload part');
190
        }
191
    }
192
193
    /**
194
     * @param AbstractCommonNotificationPayload $payload
195
     *
196
     * @throws \Exception
197
     *
198
     * @return array
199
     */
200
    private function buildNotificationPayloadPart(AbstractCommonNotificationPayload $payload): array
201
    {
202
        if ($payload instanceof AndroidNotificationPayload) {
203
            $payloadBuilder = new AndroidPayloadBuilder($payload);
204
        } elseif ($payload instanceof IosNotificationPayload) {
205
            $payloadBuilder = new IosPayloadBuilder($payload);
206
        } elseif ($payload instanceof WebNotificationPayload) {
207
            $payloadBuilder = new WebPayloadBuilder($payload);
208
        } else {
209
            throw new \Exception('Unsupported payload part');
210
        }
211
212
        return $payloadBuilder->build()->getPayloadPart();
213
    }
214
}
215