Completed
Branch feature/fcm-adapter (a0bcef)
by Romain
01:52
created

FcmAdapter   B

Complexity

Total Complexity 39

Size/Duplication

Total Lines 368
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 10

Importance

Changes 0
Metric Value
wmc 39
lcom 1
cbo 10
dl 0
loc 368
rs 8.2857
c 0
b 0
f 0

19 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 2
A send() 0 4 1
A response() 0 4 1
A getTokens() 0 4 1
A setTokens() 0 7 1
A getNotification() 0 4 1
A setNotification() 0 10 2
A getDatas() 0 4 1
A setDatas() 0 13 4
A getParameters() 0 4 1
A setParameters() 0 7 1
A getPayload() 0 14 3
A _checkTokens() 0 6 3
B _checkNotification() 0 18 6
A _checkDatas() 0 6 2
A _checkParameters() 0 6 2
A _executePush() 0 10 2
A _buildMessage() 0 17 4
A _getHttpOptions() 0 12 1
1
<?php
2
namespace ker0x\Push\Adapter;
3
4
use Cake\Core\Configure;
5
use Cake\Core\InstanceConfigTrait;
6
use Cake\Http\Client;
7
use Cake\Utility\Hash;
8
use ker0x\Push\AdapterInterface;
9
use ker0x\Push\Exception\InvalidAdapterException;
10
use ker0x\Push\Exception\InvalidDataException;
11
use ker0x\Push\Exception\InvalidNotificationException;
12
use ker0x\Push\Exception\InvalidParametersException;
13
use ker0x\Push\Exception\InvalidTokenException;
14
15
class FcmAdapter implements AdapterInterface
16
{
17
18
    use InstanceConfigTrait;
19
20
    /**
21
     * Array for devices's token
22
     *
23
     * @var array
24
     */
25
    protected $tokens = [];
26
27
    /**
28
     * Array for the notification
29
     *
30
     * @var array
31
     */
32
    protected $notification = [];
33
34
    /**
35
     * Array of datas
36
     *
37
     * @var array
38
     */
39
    protected $datas = [];
40
41
    /**
42
     * Array of request parameters
43
     *
44
     * @var array
45
     */
46
    protected $parameters = [];
47
48
    /**
49
     * Array of payload
50
     *
51
     * @var array
52
     */
53
    protected $payload = [];
54
55
    /**
56
     * Response of the request
57
     *
58
     * @var \Cake\Http\Client\Response
59
     */
60
    protected $response;
61
62
    /**
63
     * Default config
64
     *
65
     * @var array
66
     */
67
    protected $_defaultConfig = [
68
        'parameters' => [
69
            'collapse_key' => null,
70
            'priority' => 'normal',
71
            'dry_run' => false,
72
            'time_to_live' => 0,
73
            'restricted_package_name' => null
74
        ],
75
        'http' => []
76
    ];
77
78
    /**
79
     * List of keys allowed to be used in notification array.
80
     *
81
     * @var array
82
     */
83
    protected $_allowedNotificationParameters = [
84
        'title',
85
        'body',
86
        'icon',
87
        'sound',
88
        'badge',
89
        'tag',
90
        'color',
91
        'click_action',
92
        'body_loc_key',
93
        'body_loc_args',
94
        'title_loc_key',
95
        'title_loc_args',
96
    ];
97
98
    /**
99
     * FcmAdapter constructor.
100
     *
101
     * @throws \ker0x\Push\Exception\InvalidAdapterException
102
     */
103
    public function __construct()
104
    {
105
        $config = Configure::read('Push.adapters.Fcm');
106
        $this->config($config);
107
108
        if ($this->config('api.key') === null) {
109
            throw new InvalidAdapterException("No API key set. Push not triggered");
110
        }
111
    }
112
113
    /**
114
     * Send the request
115
     *
116
     * @return bool
117
     */
118
    public function send()
119
    {
120
        return $this->_executePush();
121
    }
122
123
    /**
124
     * Display the response of the request
125
     *
126
     * @return array
127
     */
128
    public function response()
129
    {
130
        return $this->response->json;
131
    }
132
133
    /**
134
     * Getter for tokens
135
     *
136
     * @return array
137
     */
138
    public function getTokens(): array
139
    {
140
        return $this->tokens;
141
    }
142
143
    /**
144
     * Setter for tokens
145
     *
146
     * @param array $tokens
147
     * @return $this
148
     */
149
    public function setTokens(array $tokens)
150
    {
151
        $this->_checkTokens($tokens);
152
        $this->tokens = $tokens;
153
154
        return $this;
155
    }
156
157
    /**
158
     * Getter for notification
159
     *
160
     * @return array
161
     */
162
    public function getNotification(): array
163
    {
164
        return $this->notification;
165
    }
166
167
    /**
168
     * Setter for notification
169
     *
170
     * @param array $notification
171
     * @return $this
172
     */
173
    public function setNotification(array $notification)
174
    {
175
        $this->_checkNotification($notification);
176
        if (!isset($notification['icon'])) {
177
            $notification['icon'] = 'myicon';
178
        }
179
        $this->notification = $notification;
180
181
        return $this;
182
    }
183
184
    /**
185
     * Getter for datas
186
     *
187
     * @return array
188
     */
189
    public function getDatas(): array
190
    {
191
        return $this->datas;
192
    }
193
194
    /**
195
     * Setter for datas
196
     *
197
     * @param array $datas
198
     * @return $this
199
     */
200
    public function setDatas(array $datas)
201
    {
202
        $this->_checkDatas($datas);
203
        foreach ($datas as $key => $value) {
204
            if (is_bool($value)) {
205
                $value = ($value) ? 'true' : 'false';
206
            }
207
            $datas[$key] = (string)$value;
208
        }
209
        $this->datas = $datas;
210
211
        return $this;
212
    }
213
214
    /**
215
     * Getter for parameters
216
     *
217
     * @return array
218
     */
219
    public function getParameters(): array
220
    {
221
        return $this->parameters;
222
    }
223
224
    /**
225
     * Setter for parameters
226
     *
227
     * @param array $parameters
228
     * @return $this
229
     */
230
    public function setParameters(array $parameters)
231
    {
232
        $this->_checkParameters($parameters);
233
        $this->parameters = Hash::merge($this->config('parameters'), $parameters);
234
235
        return $this;
236
    }
237
238
    /**
239
     * Getter for payload
240
     *
241
     * @return array
242
     */
243
    public function getPayload(): array
244
    {
245
        $notification = $this->getNotification();
246
        if (!empty($notification)) {
247
            $this->payload['notification'] = $notification;
248
        }
249
250
        $datas = $this->getDatas();
251
        if (!empty($datas)) {
252
            $this->payload['datas'] = $datas;
253
        }
254
255
        return $this->payload;
256
    }
257
258
    /**
259
     * Check tokens's array
260
     *
261
     * @param array $tokens
262
     * @return void
263
     * @throws \ker0x\Push\Exception\InvalidTokenException
264
     */
265
    private function _checkTokens($tokens)
266
    {
267
        if (empty($tokens) || count($tokens) > 1000) {
268
            throw new InvalidTokenException("Array must contain at least 1 and at most 1000 tokens.");
269
        }
270
    }
271
272
    /**
273
     * Check notification's array
274
     *
275
     * @param array $notification
276
     * @return void
277
     * @throws \ker0x\Push\Exception\InvalidNotificationException
278
     */
279
    private function _checkNotification($notification)
280
    {
281
        if (empty($notification) || !isset($notification['title'])) {
282
            throw new InvalidNotificationException("Array must contain at least a key title.");
283
        }
284
285
        $notAllowedKeys = [];
286
        foreach ($notification as $key => $value) {
287
            if (!in_array($key, $this->_allowedNotificationParameters)) {
288
                $notAllowedKeys[] = $key;
289
            }
290
        }
291
292
        if (!empty($notAllowedKeys)) {
293
            $notAllowedKeys = implode(', ', $notAllowedKeys);
294
            throw new InvalidNotificationException("The following keys are not allowed: {$notAllowedKeys}");
295
        }
296
    }
297
298
    /**
299
     * Check datas's array
300
     *
301
     * @param array $datas
302
     * @return void
303
     * @throws \ker0x\Push\Exception\InvalidDataException
304
     */
305
    private function _checkDatas($datas)
306
    {
307
        if (empty($datas)) {
308
            throw new InvalidDataException("Array can not be empty.");
309
        }
310
    }
311
312
    /**
313
     * Check parameters's array
314
     *
315
     * @param array $parameters
316
     * @return void
317
     * @throws \ker0x\Push\Exception\InvalidParametersException
318
     */
319
    private function _checkParameters($parameters)
320
    {
321
        if (empty($parameters)) {
322
            throw new InvalidParametersException("Array can not be empty.");
323
        }
324
    }
325
326
    /**
327
     * Execute the push
328
     *
329
     * @return bool
330
     */
331
    private function _executePush()
332
    {
333
        $message = $this->_buildMessage();
334
        $options = $this->_getHttpOptions();
335
336
        $http = new Client();
337
        $this->response = $http->post($this->config('api.url'), $message, $options);
338
339
        return ($this->response->code === '200') ? true : false;
340
    }
341
342
    /**
343
     * Build the message
344
     *
345
     * @return string
346
     */
347
    private function _buildMessage()
348
    {
349
        $tokens = $this->getTokens();
350
        $message = (count($tokens) > 1) ? ['registration_ids' => $tokens] : ['to' => current($tokens)];
351
352
        $payload = $this->getPayload();
353
        if (!empty($payload)) {
354
            $message += $payload;
355
        }
356
357
        $parameters = $this->getParameters();
358
        if (!empty($parameters)) {
359
            $message += $parameters;
360
        }
361
362
        return json_encode($message);
363
    }
364
365
    /**
366
     * Return options for the HTTP request
367
     *
368
     * @return array $options
369
     */
370
    private function _getHttpOptions()
371
    {
372
        $options = Hash::merge($this->config('http'), [
373
            'type' => 'json',
374
            'headers' => [
375
                'Authorization' => 'key=' . $this->config('api.key'),
376
                'Content-Type' => 'application/json'
377
            ]
378
        ]);
379
380
        return $options;
381
    }
382
}
383