ApiClient::prepareRequestBody()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 14
ccs 6
cts 6
cp 1
rs 9.7998
c 0
b 0
f 0
cc 2
nc 2
nop 2
crap 2
1
<?php
2
3
namespace Botonomous\client;
4
5
use Botonomous\BotonomousException;
6
use Botonomous\ImChannel;
7
use Botonomous\Team;
8
use /* @noinspection PhpUndefinedClassInspection */
9
    GuzzleHttp\Psr7\Request;
10
use Psr\Http\Message\ResponseInterface;
11
12
/**
13
 * Class ApiClient.
14
 */
15
class ApiClient extends AbstractClient
16
{
17
    const BASE_URL = 'https://slack.com/api/';
18
    const CONTENT_TYPE = 'application/x-www-form-urlencoded';
19
    const REQUIRED_ARGUMENTS_KEY = 'required';
20
    const OPTIONAL_ARGUMENTS_KEY = 'optional';
21
    const TOKEN_ARGUMENT_KEY = 'token';
22
23
    private $arguments = [
24
        'rtm.start' => [
25
            self::REQUIRED_ARGUMENTS_KEY => [
26
                self::TOKEN_ARGUMENT_KEY,
27
            ],
28
            self::OPTIONAL_ARGUMENTS_KEY => [
29
                'simple_latest',
30
                'no_unreads',
31
                'mpim_aware',
32
            ],
33
        ],
34
        'chat.postMessage' => [
35
            self::REQUIRED_ARGUMENTS_KEY => [
36
                self::TOKEN_ARGUMENT_KEY,
37
                'channel',
38
                'text',
39
            ],
40
            self::OPTIONAL_ARGUMENTS_KEY => [
41
                'parse',
42
                'link_names',
43
                'attachments',
44
                'unfurl_links',
45
                'unfurl_media',
46
                'username',
47
                'as_user',
48
                'icon_url',
49
                'icon_emoji',
50
            ],
51
        ],
52
        'oauth.access' => [
53
            self::REQUIRED_ARGUMENTS_KEY => [
54
                'client_id',
55
                'client_secret',
56
                'code',
57
            ],
58
            self::OPTIONAL_ARGUMENTS_KEY => [
59
                'redirect_uri',
60
            ],
61
        ],
62
        'team.info' => [
63
            self::REQUIRED_ARGUMENTS_KEY => [
64
                self::TOKEN_ARGUMENT_KEY,
65
            ],
66
        ],
67
        'im.list' => [
68
            self::REQUIRED_ARGUMENTS_KEY => [
69
                self::TOKEN_ARGUMENT_KEY,
70
            ],
71
        ],
72
        'users.list' => [
73
            self::REQUIRED_ARGUMENTS_KEY => [
74
                self::TOKEN_ARGUMENT_KEY,
75
            ],
76
            self::OPTIONAL_ARGUMENTS_KEY => [
77
                'presence',
78
            ],
79
        ],
80
        'users.info' => [
81
            self::REQUIRED_ARGUMENTS_KEY => [
82
                self::TOKEN_ARGUMENT_KEY,
83
                'user',
84
            ],
85
        ],
86
    ];
87
88
    private $token;
89
90
    /**
91
     * ApiClient constructor.
92
     *
93
     * @param null $token
94
     */
95 43
    public function __construct($token = null)
96
    {
97 43
        $this->setToken($token);
98 43
    }
99
100
    /**
101
     * API CURL Call with post method.
102
     *
103
     * @param string $method
104
     * @param array  $arguments
105
     *
106
     * @throws \Exception
107
     *
108
     * @return mixed
109
     */
110 33
    public function apiCall(string $method, array $arguments = [])
111
    {
112
        try {
113 33
            $requestBody = $this->prepareRequestBody($method, $arguments);
114 30
            $response = $this->sendRequest($method, $requestBody);
115
116 28
            return $this->processResponse($response);
117 6
        } catch (\Exception $e) {
118 6
            throw $e;
119
        }
120
    }
121
122
    /**
123
     * @param $method
124
     * @param $requestBody
125
     *
126
     * @throws \Exception
127
     *
128
     * @return mixed|\Psr\Http\Message\ResponseInterface
129
     */
130 30
    private function sendRequest(string $method, $requestBody)
131
    {
132
        try {
133
            /** @noinspection PhpUndefinedClassInspection */
134 30
            $request = new Request(
135 30
                'POST',
136 30
                self::BASE_URL.$method,
137 30
                ['Content-Type' => self::CONTENT_TYPE],
138 30
                $requestBody
139
            );
140
141 30
            return $this->getClient()->send($request);
142 2
        } catch (\Exception $e) {
143 2
            throw new BotonomousException('Failed to send data to the Slack API: '.$e->getMessage());
144
        }
145
    }
146
147
    /**
148
     * @param $method
149
     * @param array $arguments
150
     *
151
     * @throws \Exception
152
     *
153
     * @return string
154
     */
155 33
    private function prepareRequestBody(string $method, array $arguments = [])
156
    {
157 33
        $arguments = array_merge($arguments, $this->getArgs());
158
159
        // check the required arguments are provided
160
        try {
161 33
            $this->validateRequiredArguments($method, $arguments);
162 3
        } catch (\Exception $e) {
163 3
            throw new BotonomousException('Missing required argument(s): '.$e->getMessage());
164
        }
165
166
        // filter unwanted arguments
167 30
        return http_build_query($this->filterArguments($method, $arguments));
168
    }
169
170
    /**
171
     * @param $response
172
     *
173
     * @throws \Exception
174
     *
175
     * @return mixed
176
     */
177 28
    private function processResponse(ResponseInterface $response)
178
    {
179 28
        $response = json_decode($response->getBody()->getContents(), true);
180
181 28
        if (!is_array($response)) {
182 1
            throw new BotonomousException('Failed to process response from the Slack API');
183
        }
184
185 27
        return $response;
186
    }
187
188
    /**
189
     * @throws \Exception
190
     *
191
     * @return array
192
     */
193 34
    public function getArgs(): array
194
    {
195
        return [
196 34
            self::TOKEN_ARGUMENT_KEY    => $this->getToken(),
197 34
            'username'                  => $this->getConfig()->get('botUsername'),
198 34
            'as_user'                   => $this->getConfig()->get('asUser'),
199 34
            'icon_url'                  => $this->getConfig()->get('iconURL'),
200
        ];
201
    }
202
203
    /**
204
     * @param array $arguments
205
     *
206
     * @throws \Exception
207
     *
208
     * @return mixed
209
     */
210 2
    public function chatPostMessage(array $arguments)
211
    {
212 2
        return $this->apiCall('chat.postMessage', $arguments);
213
    }
214
215
    /**
216
     * @param $arguments
217
     *
218
     * @throws \Exception
219
     *
220
     * @return mixed
221
     */
222 1
    public function rtmStart(array $arguments)
223
    {
224 1
        return $this->apiCall('rtm.start', $arguments);
225
    }
226
227
    /**
228
     * @throws \Exception
229
     *
230
     * @return array
231
     * @return Team
232
     */
233 4
    public function teamInfo()
234
    {
235 4
        $teamInfo = $this->apiCall('team.info');
236
237 4
        if (!isset($teamInfo['team'])) {
238 2
            return [];
239
        }
240
241 2
        return $teamInfo['team'];
242
    }
243
244
    /**
245
     * @throws \Exception
246
     *
247
     * @return \Botonomous\AbstractBaseSlack|null|void
248
     */
249 2
    public function teamInfoAsObject()
250
    {
251 2
        $teamInfo = $this->teamInfo();
252
253 2
        if (empty($teamInfo)) {
254
            /* @noinspection PhpInconsistentReturnPointsInspection */
255 1
            return;
256
        }
257
258
        // return as object
259 1
        return (new Team())->load($teamInfo);
260
    }
261
262
    /**
263
     * List all the Slack users in the team.
264
     *
265
     * @throws \Exception
266
     *
267
     * @return array
268
     */
269 2
    public function usersList(): array
270
    {
271 2
        $result = $this->apiCall('users.list');
272
273 2
        if (!isset($result['members'])) {
274 1
            return [];
275
        }
276
277 1
        return $result['members'];
278
    }
279
280
    /**
281
     * Return a user by Slack user id.
282
     *
283
     * @param $arguments
284
     *
285
     * @throws \Exception
286
     *
287
     * @return mixed
288
     */
289 10
    public function userInfo(array $arguments)
290
    {
291 10
        $result = $this->apiCall('users.info', $arguments);
292
293 10
        if (!isset($result['user'])) {
294
            /* @noinspection PhpInconsistentReturnPointsInspection */
295 4
            return;
296
        }
297
298 6
        return $result['user'];
299
    }
300
301
    /**
302
     * @throws \Exception
303
     *
304
     * @return mixed
305
     */
306 1
    public function test()
307
    {
308 1
        return $this->apiCall('api.test');
309
    }
310
311
    /**
312
     * @throws \Exception
313
     *
314
     * @return array
315
     */
316 5
    public function imList()
317
    {
318 5
        $result = $this->apiCall('im.list');
319
320 5
        if (!isset($result['ims'])) {
321 2
            return [];
322
        }
323
324 4
        return $result['ims'];
325
    }
326
327
    /**
328
     * @throws \Exception
329
     *
330
     * @return array
331
     */
332 4
    public function imListAsObject(): array
333
    {
334 4
        $imChannels = $this->imList();
335
336 4
        $imChannelObjects = [];
337 4
        if (empty($imChannels)) {
338 1
            return $imChannelObjects;
339
        }
340
341 3
        foreach ($imChannels as $imChannel) {
342 3
            $imChannelObjects[$imChannel['id']] = (new ImChannel())->load($imChannel);
343
        }
344
345 3
        return $imChannelObjects;
346
    }
347
348
    /**
349
     * @param $arguments
350
     *
351
     * @throws \Exception
352
     *
353
     * @return mixed
354
     */
355 5
    public function oauthAccess(array $arguments)
356
    {
357 5
        return $this->apiCall('oauth.access', $arguments);
358
    }
359
360
    /**
361
     * @param $method
362
     * @param $arguments
363
     *
364
     * @throws \Exception
365
     *
366
     * @return bool
367
     */
368 33
    private function validateRequiredArguments(string $method, array $arguments)
369
    {
370 33
        $validArguments = $this->getArguments($method);
371
372 33
        if (empty($validArguments[self::REQUIRED_ARGUMENTS_KEY])) {
373 3
            return true;
374
        }
375
376 30
        foreach ($validArguments[self::REQUIRED_ARGUMENTS_KEY] as $argument) {
377 30
            if ($this->getArrayUtility()->arrayKeyValueExists($argument, $arguments) !== true) {
378 30
                throw new BotonomousException("{$argument} must be provided for {$method}");
379
            }
380
        }
381
382 27
        return true;
383
    }
384
385
    /**
386
     * @param null $method
387
     *
388
     * @throws \Exception
389
     *
390
     * @return mixed
391
     */
392 35
    public function getArguments($method = null)
393
    {
394 35
        if ($method !== null) {
395 34
            if (!isset($this->arguments[$method])) {
396
                /* @noinspection PhpInconsistentReturnPointsInspection */
397 3
                return;
398
            }
399
400 31
            return $this->arguments[$method];
401
        }
402
403 1
        return $this->arguments;
404
    }
405
406
    /**
407
     * @param array $arguments
408
     */
409 1
    public function setArguments(array $arguments)
410
    {
411 1
        $this->arguments = $arguments;
412 1
    }
413
414
    /**
415
     * @param string $method
416
     * @param array  $arguments
417
     *
418
     * @throws \Exception
419
     *
420
     * @return array
421
     */
422 31
    public function filterArguments(string $method, array $arguments): array
423
    {
424 31
        $validArguments = $this->getArguments($method);
425
426 31
        if (empty($validArguments)) {
427 3
            return $arguments;
428
        }
429
430 28
        if (!isset($validArguments[self::OPTIONAL_ARGUMENTS_KEY])) {
431 19
            $validArguments[self::OPTIONAL_ARGUMENTS_KEY] = [];
432
        }
433
434 28
        $extractedArguments = array_merge(
435 28
            $validArguments[self::REQUIRED_ARGUMENTS_KEY],
436 28
            $validArguments[self::OPTIONAL_ARGUMENTS_KEY]
437
        );
438
439 28
        return $this->getArrayUtility()->filterArray($arguments, $extractedArguments);
440
    }
441
442
    /**
443
     * @throws \Exception
444
     *
445
     * @return string
446
     */
447 34
    public function getToken(): string
448
    {
449
        // fall back to config
450 34
        if (empty($this->token)) {
451 34
            $this->setToken($this->getConfig()->get('botUserToken'));
452
        }
453
454 34
        return $this->token;
455
    }
456
457
    /**
458
     * @param string $token
459
     */
460 43
    public function setToken($token)
461
    {
462 43
        $this->token = $token;
463 43
    }
464
}
465