AbstractReportingCloud::getClient()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 21
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 11
c 1
b 0
f 0
dl 0
loc 21
ccs 14
cts 14
cp 1
rs 9.9
cc 2
nc 2
nop 0
crap 2
1
<?php
2
declare(strict_types=1);
3
4
/**
5
 * ReportingCloud PHP SDK
6
 *
7
 * PHP SDK 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://tinyurl.com/vmbbh6kd for the canonical source repository
11
 * @license   https://tinyurl.com/3pc9am89
12
 * @copyright © 2023 Text Control GmbH
13
 */
14
15
namespace TextControl\ReportingCloud;
16
17
use GuzzleHttp\Client;
18
use GuzzleHttp\Exception\GuzzleException;
19
use GuzzleHttp\RequestOptions;
20
use Psr\Http\Message\ResponseInterface;
21
use TextControl\ReportingCloud\Assert\Assert;
22
use TextControl\ReportingCloud\Exception\InvalidArgumentException;
23
use TextControl\ReportingCloud\Exception\RuntimeException;
24
use TextControl\ReportingCloud\Filter\Filter;
25
use TextControl\ReportingCloud\Stdlib\ConsoleUtils;
26
27
/**
28
 * Abstract ReportingCloud
29
 */
30
abstract class AbstractReportingCloud
31
{
32
    // <editor-fold desc="Constants (default values)">
33
    /**
34
     * Default date/time format of backend is 'ISO 8601'
35
     *
36
     * Note, last letter is 'P' and not 'O':
37
     *
38
     * O - Difference to Greenwich time (GMT) in hours (e.g. +0200)
39
     * P - Difference to Greenwich time (GMT) with colon between hours and minutes (e.g. +02:00)
40
     *
41
     * Backend uses the 'P' variant
42
     *
43
     * @const DEFAULT_DATE_FORMAT
44
     * @var string
45
     */
46
    final public const DEFAULT_DATE_FORMAT = 'Y-m-d\TH:i:sP';
47
48
    /**
49
     * Default time zone of backend
50
     *
51
     * @const DEFAULT_TIME_ZONE
52
     * @var string
53
     */
54
    final public const DEFAULT_TIME_ZONE = 'UTC';
55
56
    /**
57
     * Default base URI of backend
58
     *
59
     * @const DEFAULT_BASE_URI
60
     * @var string
61
     */
62
    final public const DEFAULT_BASE_URI = 'https://api.reporting.cloud';
63
64
    // </editor-fold>
65
    // <editor-fold desc="Constants (document dividers)">
66
    /**
67
     * Document divider - none
68
     * @var int
69
     */
70
    final public const DOCUMENT_DIVIDER_NONE = 1;
71
72
    /**
73
     * Document divider - new paragraph
74
     * @var int
75
     */
76
    final public const DOCUMENT_DIVIDER_NEW_PARAGRAPH = 2;
77
78
    /**
79
     * Document divider - new section
80
     * @var int
81
     */
82
    final public const DOCUMENT_DIVIDER_NEW_SECTION = 3;
83
84
    // </editor-fold>
85
    // <editor-fold desc="Constants (file formats)">
86
    /**
87
     * DOC file format
88
     * @var string
89
     */
90
    final public const FILE_FORMAT_DOC = 'DOC';
91
92
    /**
93
     * DOCX file format
94
     * @var string
95
     */
96
    final public const FILE_FORMAT_DOCX = 'DOCX';
97
98
    /**
99
     * HTML file format
100
     * @var string
101
     */
102
    final public const FILE_FORMAT_HTML = 'HTML';
103
104
    /**
105
     * PDF file format
106
     * @var string
107
     */
108
    final public const FILE_FORMAT_PDF = 'PDF';
109
110
    /**
111
     * PDF/A file format
112
     * @var string
113
     */
114
    final public const FILE_FORMAT_PDFA = 'PDFA';
115
116
    /**
117
     * RTF file format
118
     * @var string
119
     */
120
    final public const FILE_FORMAT_RTF = 'RTF';
121
122
    /**
123
     * TX (Text Control) file format
124
     * @var string
125
     */
126
    final public const FILE_FORMAT_TX = 'TX';
127
128
    /**
129
     * Pure text file format
130
     * @var string
131
     */
132
    final public const FILE_FORMAT_TXT = 'TXT';
133
134
    /**
135
     * Bitmap file format
136
     * @var string
137
     */
138
    final public const FILE_FORMAT_BMP = 'BMP';
139
140
    /**
141
     * GIF file format
142
     * @var string
143
     */
144
    final public const FILE_FORMAT_GIF = 'GIF';
145
146
    /**
147
     * JPEG file format
148
     * @var string
149
     */
150
    final public const FILE_FORMAT_JPG = 'JPG';
151
152
    /**
153
     * PNG file format
154
     * @var string
155
     */
156
    final public const FILE_FORMAT_PNG = 'PNG';
157
158
    /**
159
     * XLSX file format
160
     * @var string
161
     */
162
    final public const FILE_FORMAT_XLSX = 'XLSX';
163
164
    // </editor-fold>
165
    // <editor-fold desc="Constants (tracked changes)">
166
    /**
167
     * InsertedText tracked change
168
     * @var int
169
     */
170
    final public const TRACKED_CHANGE_INSERTED_TEXT = 4096;
171
172
    /**
173
     * DeletedText tracked change
174
     * @var int
175
     */
176
    final public const TRACKED_CHANGE_DELETED_TEXT = 8192;
177
178
    // </editor-fold>
179
    // <editor-fold desc="Constants (highlight mode)">
180
    /**
181
     * Never highlight mode
182
     * @var int
183
     */
184
    final public const HIGHLIGHT_MODE_NEVER = 1;
185
186
    /**
187
     * Activated highlight mode
188
     * @var int
189
     */
190
    final public const HIGHLIGHT_MODE_ACTIVATED = 2;
191
192
    /**
193
     * Always highlight mode
194
     * @var int
195
     */
196
    final public const HIGHLIGHT_MODE_ALWAYS = 3;
197
198
    // </editor-fold>
199
    // <editor-fold desc="Constants (file format collections)">
200
    /**
201
     * Image file formats
202
     * @var string[]
203
     */
204
    final public const FILE_FORMATS_IMAGE
205
        = [self::FILE_FORMAT_BMP, self::FILE_FORMAT_GIF, self::FILE_FORMAT_JPG, self::FILE_FORMAT_PNG];
206
207
    /**
208
     * Template file formats
209
     * @var string[]
210
     */
211
    final public const FILE_FORMATS_TEMPLATE
212
        = [self::FILE_FORMAT_DOC, self::FILE_FORMAT_DOCX, self::FILE_FORMAT_RTF, self::FILE_FORMAT_TX];
213
214
    /**
215
     * Document file formats
216
     * @var string[]
217
     */
218
    final public const FILE_FORMATS_DOCUMENT
219
        = [
220
            self::FILE_FORMAT_DOC,
221
            self::FILE_FORMAT_DOCX,
222
            self::FILE_FORMAT_HTML,
223
            self::FILE_FORMAT_PDF,
224
            self::FILE_FORMAT_RTF,
225
            self::FILE_FORMAT_TX,
226
        ];
227
228
    /**
229
     * Return file formats
230
     * @var string[]
231
     */
232
    final public const FILE_FORMATS_RETURN
233
        = [
234
            self::FILE_FORMAT_DOC,
235
            self::FILE_FORMAT_DOCX,
236
            self::FILE_FORMAT_HTML,
237
            self::FILE_FORMAT_PDF,
238
            self::FILE_FORMAT_PDFA,
239
            self::FILE_FORMAT_RTF,
240
            self::FILE_FORMAT_TX,
241
            self::FILE_FORMAT_TXT,
242
        ];
243
244
    /**
245
     * Default debug flag of REST client
246
     *
247
     * @const DEFAULT_DEBUG
248
     * @var bool
249
     */
250
    protected const DEFAULT_DEBUG = false;
251
252
    /**
253
     * Default test flag of backend
254
     *
255
     * @const DEFAULT_TEST
256
     * @var bool
257
     */
258
    protected const DEFAULT_TEST = false;
259
260
    /**
261
     * Default timeout of backend in seconds
262
     *
263
     * @const DEFAULT_TIMEOUT
264
     * @var int
265
     */
266
    protected const DEFAULT_TIMEOUT = 120;
267
268
    /**
269
     * Default version string of backend
270
     *
271
     * @const DEFAULT_VERSION
272
     * @var string
273
     */
274
    protected const DEFAULT_VERSION = 'v1';
275
276
    // </editor-fold>
277
    // <editor-fold desc="Properties">
278
    /**
279
     * Backend API key
280
     */
281
    private string $apiKey;
282
283
    /**
284
     * Backend username
285
     *
286
     * @deprecated Use $this->apiKey instead
287
     */
288
    private string $username;
289
290
    /**
291
     * Backend password
292
     *
293
     * @deprecated Use $this->apiKey instead
294
     */
295
    private string $password;
296
297
    /**
298
     * Backend base URI
299
     */
300
    private string $baseUri;
301
302
    /**
303
     * Debug flag of REST client
304
     */
305
    private bool $debug = false;
306
307
    /**
308
     * When true, API call does not count against quota
309
     * "TEST MODE" watermark is added to document
310
     */
311
    private bool $test = false;
312
313
    /**
314
     * Backend timeout in seconds
315
     */
316
    private int $timeout;
317
318
    /**
319
     * Backend version string
320
     */
321
    private string $version;
322
323
    /**
324
     * REST client to backend
325
     */
326
    private Client $client;
327
328
    // </editor-fold>
329
    // <editor-fold desc="Methods">
330
    /**
331
     * Return the API key
332
     */
333 66
    public function getApiKey(): string
334
    {
335 66
        if (!isset($this->apiKey)) {
336
            $this->apiKey = '';
337
        }
338
339 66
        return $this->apiKey;
340
    }
341
342
    /**
343
     * Set the API key
344
     */
345 180
    public function setApiKey(string $apiKey): self
346
    {
347 180
        $this->apiKey = $apiKey;
348
349 180
        return $this;
350
    }
351
352
    /**
353
     * Return the username
354
     *
355
     * @deprecated Use $this->getApiKey(): string instead
356
     */
357 8
    public function getUsername(): string
358
    {
359 8
        if (!isset($this->username)) {
0 ignored issues
show
Deprecated Code introduced by
The property TextControl\ReportingClo...portingCloud::$username has been deprecated: Use $this->apiKey instead ( Ignorable by Annotation )

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

359
        if (!isset(/** @scrutinizer ignore-deprecated */ $this->username)) {

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
360 2
            $this->username = '';
0 ignored issues
show
Deprecated Code introduced by
The property TextControl\ReportingClo...portingCloud::$username has been deprecated: Use $this->apiKey instead ( Ignorable by Annotation )

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

360
            /** @scrutinizer ignore-deprecated */ $this->username = '';

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
361
        }
362
363 8
        return $this->username;
364
    }
365
366
    /**
367
     * Set the username
368
     *
369
     * @deprecated Use $this->setApiKey(string $apiKey): self instead
370
     */
371 6
    public function setUsername(string $username): self
372
    {
373 6
        $this->username = $username;
0 ignored issues
show
Deprecated Code introduced by
The property TextControl\ReportingClo...portingCloud::$username has been deprecated: Use $this->apiKey instead ( Ignorable by Annotation )

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

373
        /** @scrutinizer ignore-deprecated */ $this->username = $username;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
374
375 6
        return $this;
376
    }
377
378
    /**
379
     * Return the password
380
     *
381
     * @deprecated Use $this->getApiKey() instead
382
     */
383 8
    public function getPassword(): string
384
    {
385 8
        if (!isset($this->password)) {
0 ignored issues
show
Deprecated Code introduced by
The property TextControl\ReportingClo...portingCloud::$password has been deprecated: Use $this->apiKey instead ( Ignorable by Annotation )

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

385
        if (!isset(/** @scrutinizer ignore-deprecated */ $this->password)) {

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
386 2
            $this->password = '';
0 ignored issues
show
Deprecated Code introduced by
The property TextControl\ReportingClo...portingCloud::$password has been deprecated: Use $this->apiKey instead ( Ignorable by Annotation )

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

386
            /** @scrutinizer ignore-deprecated */ $this->password = '';

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
387
        }
388
389 8
        return $this->password;
390
    }
391
392
    /**
393
     * Set the password
394
     *
395
     * @deprecated Use $this->setApiKey(string $apiKey): self instead
396
     */
397 6
    public function setPassword(string $password): self
398
    {
399 6
        $this->password = $password;
0 ignored issues
show
Deprecated Code introduced by
The property TextControl\ReportingClo...portingCloud::$password has been deprecated: Use $this->apiKey instead ( Ignorable by Annotation )

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

399
        /** @scrutinizer ignore-deprecated */ $this->password = $password;

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
400
401 6
        return $this;
402
    }
403
404
    /**
405
     * Return the base URI of the backend web service
406
     */
407 74
    public function getBaseUri(): string
408
    {
409 74
        if (!isset($this->baseUri)) {
410 70
            $baseUri = ConsoleUtils::baseUri();
411 70
            if ('' === $baseUri) {
412 70
                $baseUri = self::DEFAULT_BASE_URI;
413
            }
414 70
            Assert::assertBaseUri($baseUri);
415 70
            $this->setBaseUri($baseUri);
416
        }
417
418 74
        return $this->baseUri;
419
    }
420
421
    /**
422
     * Set the base URI of the backend web service
423
     */
424 74
    public function setBaseUri(string $baseUri): self
425
    {
426 74
        $this->baseUri = $baseUri;
427
428 74
        return $this;
429
    }
430
431
    /**
432
     * Return the debug flag
433
     */
434 70
    public function getDebug(): bool
435
    {
436 70
        if (!isset($this->debug)) {
437
            $this->debug = self::DEFAULT_DEBUG;
438
        }
439
440 70
        return $this->debug;
441
    }
442
443
    /**
444
     * Set the debug flag
445
     *
446
     * @param bool $debug Debug flag
447
     */
448 4
    public function setDebug(bool $debug): self
449
    {
450 4
        $this->debug = $debug;
451
452 4
        return $this;
453
    }
454
455
    /**
456
     * Return the test flag
457
     */
458 68
    public function getTest(): bool
459
    {
460 68
        if (!isset($this->test)) {
461
            $this->test = self::DEFAULT_TEST;
462
        }
463
464 68
        return $this->test;
465
    }
466
467
    /**
468
     * Set the test flag
469
     */
470 6
    public function setTest(bool $test): self
471
    {
472 6
        $this->test = $test;
473
474 6
        return $this;
475
    }
476
477
    /**
478
     * Get the timeout (in seconds) of the backend web service
479
     */
480 70
    public function getTimeout(): int
481
    {
482 70
        if (!isset($this->timeout)) {
483 66
            $this->timeout = self::DEFAULT_TIMEOUT;
484
        }
485
486 70
        return $this->timeout;
487
    }
488
489
    /**
490
     * Set the timeout (in seconds) of the backend web service
491
     */
492 4
    public function setTimeout(int $timeout): self
493
    {
494 4
        $this->timeout = $timeout;
495
496 4
        return $this;
497
    }
498
499
    /**
500
     * Get the version string of the backend web service
501
     */
502 72
    public function getVersion(): string
503
    {
504 72
        if (!isset($this->version)) {
505 68
            $this->version = self::DEFAULT_VERSION;
506
        }
507
508 72
        return $this->version;
509
    }
510
511
    /**
512
     * Set the version string of the backend web service
513
     */
514 4
    public function setVersion(string $version): self
515
    {
516 4
        $this->version = $version;
517
518 4
        return $this;
519
    }
520
521
    /**
522
     * Return the REST client of the backend web service
523
     */
524 66
    public function getClient(): Client
525
    {
526 66
        if (!isset($this->client)) {
527
528 66
            $headers = [
529 66
                'Authorization' => $this->getAuthorizationHeader(),
530 66
            ];
531
532 64
            $options = [
533 64
                'base_uri'              => $this->getBaseUri(),
534 64
                RequestOptions::TIMEOUT => $this->getTimeout(),
535 64
                RequestOptions::DEBUG   => $this->getDebug(),
536 64
                RequestOptions::HEADERS => $headers,
537 64
            ];
538
539 64
            $client = new Client($options);
540
541 64
            $this->setClient($client);
542
        }
543
544 64
        return $this->client;
545
    }
546
547
    /**
548
     * Set the REST client of the backend web service
549
     */
550 64
    public function setClient(Client $client): self
551
    {
552 64
        $this->client = $client;
553
554 64
        return $this;
555
    }
556
557
    /**
558
     * Request the URI with options
559
     *
560
     * @param string $method HTTP method
561
     * @param string $uri URI
562
     * @param array  $options Options
563
     */
564 66
    protected function request(string $method, string $uri, array $options): ResponseInterface
565
    {
566 66
        $client = $this->getClient();
567
568
        try {
569 64
            $test = $this->getTest();
570 64
            if ($test) {
571 2
                assert(is_array($options[RequestOptions::QUERY]));
572 2
                $options[RequestOptions::QUERY]['test'] = Filter::filterBooleanToString($test);
573
            }
574 64
            $response = $client->request($method, $uri, $options);
575 4
        } catch (GuzzleException $guzzleException) {
576 4
            throw new RuntimeException($guzzleException->getMessage(), $guzzleException->getCode());
577
        }
578
579 62
        return $response;
580
    }
581
582
    /**
583
     * Construct URI with version number
584
     *
585
     * @param string $uri URI
586
     */
587 66
    protected function uri(string $uri): string
588
    {
589 66
        return sprintf('/%s%s', $this->getVersion(), $uri);
590
    }
591
592
    /**
593
     * Return Authorization Header, with either API key or username and password
594
     */
595 66
    private function getAuthorizationHeader(): string
596
    {
597 66
        $apiKey = $this->getApiKey();
598
599 66
        if (0 < strlen($apiKey)) {
600 64
            return sprintf('ReportingCloud-APIKey %s', $apiKey);
601
        }
602
603 2
        $username = $this->getUsername();
0 ignored issues
show
Deprecated Code introduced by
The function TextControl\ReportingClo...ingCloud::getUsername() has been deprecated: Use $this->getApiKey(): string instead ( Ignorable by Annotation )

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

603
        $username = /** @scrutinizer ignore-deprecated */ $this->getUsername();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
604 2
        $password = $this->getPassword();
0 ignored issues
show
Deprecated Code introduced by
The function TextControl\ReportingClo...ingCloud::getPassword() has been deprecated: Use $this->getApiKey() instead ( Ignorable by Annotation )

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

604
        $password = /** @scrutinizer ignore-deprecated */ $this->getPassword();

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
605
606 2
        if (0 < strlen($username) && 0 < strlen($password)) {
607
            $value = sprintf('%s:%s', $username, $password);
608
609
            return sprintf('Basic %s', base64_encode($value));
610
        }
611
612 2
        $message = 'Either the API key, or username and password must be set for authorization';
613 2
        throw new InvalidArgumentException($message);
614
    }
615
616
    // </editor-fold>
617
}
618