Passed
Branch static-analysis (3fe576)
by Jonathan
19:22
created

AbstractReportingCloud   A

Complexity

Total Complexity 33

Size/Duplication

Total Lines 523
Duplicated Lines 0 %

Test Coverage

Coverage 97.7%

Importance

Changes 0
Metric Value
wmc 33
eloc 114
dl 0
loc 523
ccs 85
cts 87
cp 0.977
rs 9.76
c 0
b 0
f 0

21 Methods

Rating   Name   Duplication   Size   Complexity  
A setClient() 0 5 1
A setBaseUri() 0 5 1
A getDebug() 0 7 2
A getClient() 0 21 2
A getTimeout() 0 7 2
A setVersion() 0 5 1
A setDebug() 0 5 1
A setTest() 0 5 1
A getBaseUri() 0 7 2
A setApiKey() 0 5 1
A getUsername() 0 3 1
A getTest() 0 7 2
A setUsername() 0 5 1
A getApiKey() 0 3 1
A getPassword() 0 3 1
A setTimeout() 0 5 1
A setPassword() 0 5 1
A getVersion() 0 7 2
A request() 0 22 4
A getAuthorizationHeader() 0 13 4
A uri() 0 3 1
1
<?php
2
declare(strict_types=1);
3
4
/**
5
 * ReportingCloud PHP Wrapper
6
 *
7
 * PHP wrapper for ReportingCloud Web API. Authored and supported by Text Control GmbH.
8
 *
9
 * @link      https://www.reporting.cloud to learn more about ReportingCloud
10
 * @link      https://github.com/TextControl/txtextcontrol-reportingcloud-php for the canonical source repository
11
 * @license   https://raw.githubusercontent.com/TextControl/txtextcontrol-reportingcloud-php/master/LICENSE.md
12
 * @copyright © 2019 Text Control GmbH
13
 */
14
15
namespace TxTextControl\ReportingCloud;
16
17
use GuzzleHttp\Client;
18
use GuzzleHttp\Exception\TransferException;
19
use GuzzleHttp\RequestOptions;
20
use Psr\Http\Message\ResponseInterface;
21
use TxTextControl\ReportingCloud\Exception\InvalidArgumentException;
22
use TxTextControl\ReportingCloud\Exception\RuntimeException;
23
use TxTextControl\ReportingCloud\Filter\Filter;
24
25
/**
26
 * Abstract ReportingCloud
27
 *
28
 * @package TxTextControl\ReportingCloud
29
 * @author  Jonathan Maron (@JonathanMaron)
30
 */
31
abstract class AbstractReportingCloud
32
{
33
    // <editor-fold desc="Constants (default values)">
34
35
    /**
36
     * Default date/time format of backend is 'ISO 8601'
37
     *
38
     * Note, last letter is 'P' and not 'O':
39
     *
40
     * O - Difference to Greenwich time (GMT) in hours (e.g. +0200)
41
     * P - Difference to Greenwich time (GMT) with colon between hours and minutes (e.g. +02:00)
42
     *
43
     * Backend uses the 'P' variant
44
     *
45
     * @const DEFAULT_DATE_FORMAT
46
     */
47
    public const DEFAULT_DATE_FORMAT = 'Y-m-d\TH:i:sP';
48
49
    /**
50
     * Default time zone of backend
51
     *
52
     * @const DEFAULT_TIME_ZONE
53
     */
54
    public const DEFAULT_TIME_ZONE = 'UTC';
55
56
    /**
57
     * Default base URI of backend
58
     *
59
     * @const DEFAULT_BASE_URI
60
     */
61
    protected const DEFAULT_BASE_URI = 'https://api.reporting.cloud';
62
63
    /**
64
     * Default version string of backend
65
     *
66
     * @const DEFAULT_VERSION
67
     */
68
    protected const DEFAULT_VERSION = 'v1';
69
70
    /**
71
     * Default timeout of backend in seconds
72
     *
73
     * @const DEFAULT_TIMEOUT
74
     */
75
    protected const DEFAULT_TIMEOUT = 120;
76
77
    /**
78
     * Default test flag of backend
79
     *
80
     * @const DEFAULT_TEST
81
     */
82
    protected const DEFAULT_TEST = false;
83
84
    /**
85
     * Default debug flag of REST client
86
     *
87
     * @const DEFAULT_DEBUG
88
     */
89
    protected const DEFAULT_DEBUG = false;
90
91
    // </editor-fold>
92
93
    // <editor-fold desc="Constants (document dividers)">
94
95
    /**
96
     * Document divider - none
97
     */
98
    public const DOCUMENT_DIVIDER_NONE = 1;
99
100
    /**
101
     * Document divider - new paragraph
102
     */
103
    public const DOCUMENT_DIVIDER_NEW_PARAGRAPH = 2;
104
105
    /**
106
     * Document divider - new section
107
     */
108
    public const DOCUMENT_DIVIDER_NEW_SECTION = 3;
109
110
    // </editor-fold>
111
112
    // <editor-fold desc="Constants (file formats)">
113
114
    /**
115
     * Image file formats
116
     */
117
    public const FILE_FORMATS_IMAGE
118
        = [
119
            'BMP',
120
            'GIF',
121
            'JPG',
122
            'PNG',
123
        ];
124
125
    /**
126
     * Template file formats
127
     */
128
    public const FILE_FORMATS_TEMPLATE
129
        = [
130
            'DOC',
131
            'DOCX',
132
            'RTF',
133
            'TX',
134
        ];
135
136
    /**
137
     * Document file formats
138
     */
139
    public const FILE_FORMATS_DOCUMENT
140
        = [
141
            'DOC',
142
            'DOCX',
143
            'HTML',
144
            'PDF',
145
            'RTF',
146
            'TX',
147
        ];
148
149
    /**
150
     * Return file formats
151
     */
152
    public const FILE_FORMATS_RETURN
153
        = [
154
            'DOC',
155
            'DOCX',
156
            'HTML',
157
            'PDF',
158
            'PDFA',
159
            'RTF',
160
            'TX',
161
        ];
162
163
    // </editor-fold>
164
165
    // <editor-fold desc="Properties">
166
167
    /**
168
     * Backend API key
169
     *
170
     * @var string|null
171
     */
172
    private $apiKey;
173
174
    /**
175
     * Backend username
176
     *
177
     * @var string|null
178
     */
179
    private $username;
180
181
    /**
182
     * Backend password
183
     *
184
     * @var string|null
185
     */
186
    private $password;
187
188
    /**
189
     * When true, API call does not count against quota
190
     * "TEST MODE" watermark is added to document
191
     *
192
     * @var bool|null
193
     */
194
    private $test;
195
196
    /**
197
     * Backend base URI
198
     *
199
     * @var string|null
200
     */
201
    private $baseUri;
202
203
    /**
204
     * Backend version string
205
     *
206
     * @var string|null
207
     */
208
    private $version;
209
210
    /**
211
     * Backend timeout in seconds
212
     *
213
     * @var int|null
214
     */
215
    private $timeout;
216
217
    /**
218
     * Debug flag of REST client
219
     *
220
     * @var bool|null
221
     */
222
    private $debug;
223
224
    /**
225
     * REST client to backend
226
     *
227
     * @var Client|null
228
     */
229
    private $client;
230
231
    // </editor-fold>
232
233
    // <editor-fold desc="Methods">
234
235
    /**
236
     * Return the API key
237
     *
238
     * @return string|null
239
     */
240 91
    public function getApiKey(): ?string
241
    {
242 91
        return $this->apiKey;
243
    }
244
245
    /**
246
     * Set the API key
247
     *
248
     * @param string $apiKey
249
     *
250
     * @return AbstractReportingCloud
251
     */
252 208
    public function setApiKey(string $apiKey): self
253
    {
254 208
        $this->apiKey = $apiKey;
255
256 208
        return $this;
257
    }
258
259
    /**
260
     * Return the username
261
     *
262
     * @return string|null
263
     */
264 15
    public function getUsername(): ?string
265
    {
266 15
        return $this->username;
267
    }
268
269
    /**
270
     * Set the username
271
     *
272
     * @param string $username
273
     *
274
     * @return AbstractReportingCloud
275
     */
276 12
    public function setUsername(string $username): self
277
    {
278 12
        $this->username = $username;
279
280 12
        return $this;
281
    }
282
283
    /**
284
     * Return the password
285
     *
286
     * @return string|null
287
     */
288 12
    public function getPassword(): ?string
289
    {
290 12
        return $this->password;
291
    }
292
293
    /**
294
     * Set the password
295
     *
296
     * @param string $password
297
     *
298
     * @return AbstractReportingCloud
299
     */
300 12
    public function setPassword(string $password): self
301
    {
302 12
        $this->password = $password;
303
304 12
        return $this;
305
    }
306
307
    /**
308
     * Return the base URI of the backend web service
309
     *
310
     * @return string|null
311
     */
312 100
    public function getBaseUri(): ?string
313
    {
314 100
        if (null === $this->baseUri) {
315 94
            $this->setBaseUri(self::DEFAULT_BASE_URI);
316
        }
317
318 100
        return $this->baseUri;
319
    }
320
321
    /**
322
     * Set the base URI of the backend web service
323
     *
324
     * @param string $baseUri
325
     *
326
     * @return AbstractReportingCloud
327
     */
328 100
    public function setBaseUri(string $baseUri): self
329
    {
330 100
        $this->baseUri = $baseUri;
331
332 100
        return $this;
333
    }
334
335
    /**
336
     * Get the timeout (in seconds) of the backend web service
337
     *
338
     * @return int|null
339
     */
340 97
    public function getTimeout(): ?int
341
    {
342 97
        if (null === $this->timeout) {
343 91
            $this->setTimeout(self::DEFAULT_TIMEOUT);
344
        }
345
346 97
        return $this->timeout;
347
    }
348
349
    /**
350
     * Set the timeout (in seconds) of the backend web service
351
     *
352
     * @param int $timeout
353
     *
354
     * @return AbstractReportingCloud
355
     */
356 97
    public function setTimeout(int $timeout): self
357
    {
358 97
        $this->timeout = $timeout;
359
360 97
        return $this;
361
    }
362
363
    /**
364
     * Return the debug flag
365
     *
366
     * @return bool|null
367
     */
368 97
    public function getDebug(): ?bool
369
    {
370 97
        if (null === $this->debug) {
371 91
            $this->setDebug(self::DEFAULT_DEBUG);
372
        }
373
374 97
        return $this->debug;
375
    }
376
377
    /**
378
     * Set the debug flag
379
     *
380
     * @param bool $debug Debug flag
381
     *
382
     * @return AbstractReportingCloud
383
     */
384 97
    public function setDebug(bool $debug): self
385
    {
386 97
        $this->debug = $debug;
387
388 97
        return $this;
389
    }
390
391
    /**
392
     * Return the test flag
393
     *
394
     * @return bool|null
395
     */
396 88
    public function getTest(): ?bool
397
    {
398 88
        if (null === $this->test) {
399 82
            $this->setTest(self::DEFAULT_TEST);
400
        }
401
402 88
        return $this->test;
403
    }
404
405
    /**
406
     * Set the test flag
407
     *
408
     * @param bool $test
409
     *
410
     * @return AbstractReportingCloud
411
     */
412 88
    public function setTest(bool $test): self
413
    {
414 88
        $this->test = $test;
415
416 88
        return $this;
417
    }
418
419
    /**
420
     * Get the version string of the backend web service
421
     *
422
     * @return string|null
423
     */
424 94
    public function getVersion(): ?string
425
    {
426 94
        if (null === $this->version) {
427 88
            $this->version = self::DEFAULT_VERSION;
428
        }
429
430 94
        return $this->version;
431
    }
432
433
    /**
434
     * Set the version string of the backend web service
435
     *
436
     * @param string $version
437
     *
438
     * @return AbstractReportingCloud
439
     */
440 6
    public function setVersion(string $version): self
441
    {
442 6
        $this->version = $version;
443
444 6
        return $this;
445
    }
446
447
    /**
448
     * Return the REST client of the backend web service
449
     *
450
     * @return Client|null
451
     */
452 91
    public function getClient(): ?Client
453
    {
454 91
        if (!$this->client instanceof Client) {
455
456
            $headers = [
457 91
                'Authorization' => $this->getAuthorizationHeader()
458
            ];
459
460
            $options = [
461 88
                'base_uri'              => $this->getBaseUri(),
462 88
                RequestOptions::TIMEOUT => $this->getTimeout(),
463 88
                RequestOptions::DEBUG   => $this->getDebug(),
464 88
                RequestOptions::HEADERS => $headers,
465
            ];
466
467 88
            $client = new Client($options);
468
469 88
            $this->setClient($client);
470
        }
471
472 88
        return $this->client;
473
    }
474
475
    /**
476
     * Set the REST client of the backend web service
477
     *
478
     * @param Client $client
479
     *
480
     * @return AbstractReportingCloud
481
     */
482 88
    public function setClient(Client $client): self
483
    {
484 88
        $this->client = $client;
485
486 88
        return $this;
487
    }
488
489
    /**
490
     * Request the URI with options
491
     *
492
     * @param string $method  HTTP method
493
     * @param string $uri     URI
494
     * @param array  $options Options
495
     *
496
     * @return ResponseInterface
497
     * @throws RuntimeException
498
     */
499 85
    protected function request(string $method, string $uri, array $options): ResponseInterface
500
    {
501 85
        $client = $this->getClient();
502
503 82
        if (!$client instanceof Client) {
0 ignored issues
show
introduced by
$client is always a sub-type of GuzzleHttp\Client.
Loading history...
504
            $message = 'No HTTP Client has been set.';
505
            throw new RuntimeException($message);
506
        }
507
508
        try {
509 82
            $test = (bool) $this->getTest();
510 82
            if ($test) {
511 3
                $options[RequestOptions::QUERY]['test'] = Filter::filterBooleanToString($test);
512
            }
513 82
            $response = $client->request($method, $uri, $options);
514 6
        } catch (TransferException $e) {
515 6
            $message = (string) $e->getMessage();
516 6
            $code    = (int) $e->getCode();
517 6
            throw new RuntimeException($message, $code);
518
        }
519
520 79
        return $response;
521
    }
522
523
    /**
524
     * Construct URI with version number
525
     *
526
     * @param string $uri URI
527
     *
528
     * @return string
529
     */
530 85
    protected function uri(string $uri): string
531
    {
532 85
        return sprintf('/%s%s', $this->getVersion(), $uri);
533
    }
534
535
    /**
536
     * Return Authorization Header, with either API key or username and password
537
     *
538
     * @return string
539
     * @throws InvalidArgumentException
540
     */
541 91
    private function getAuthorizationHeader(): string
542
    {
543 91
        if (!empty($this->getApiKey())) {
544 85
            return sprintf('ReportingCloud-APIKey %s', $this->getApiKey());
545
        }
546
547 6
        if (!empty($this->getUsername()) && !empty($this->getPassword())) {
548 3
            $value = sprintf('%s:%s', $this->getUsername(), $this->getPassword());
549 3
            return sprintf('Basic %s', base64_encode($value));
550
        }
551
552 3
        $message = 'Either the API key, or username and password must be set for authorization';
553 3
        throw new InvalidArgumentException($message);
554
    }
555
556
    // </editor-fold>
557
}
558