Issues (19)

src/AbstractReportingCloud.php (8 issues)

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

378
        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...
379 2
            $this->username = '';
0 ignored issues
show
Deprecated Code introduced by
The property TxTextControl\ReportingC...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

379
            /** @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...
380
        }
381
382 10
        return $this->username;
383
    }
384
385
    /**
386
     * Set the username
387
     *
388
     * @param string $username
389
     *
390
     * @return self
391
     * @deprecated Use $this->setApiKey(string $apiKey): self instead
392
     */
393 8
    public function setUsername(string $username): self
394
    {
395 8
        $this->username = $username;
0 ignored issues
show
Deprecated Code introduced by
The property TxTextControl\ReportingC...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

395
        /** @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...
396
397 8
        return $this;
398
    }
399
400
    /**
401
     * Return the password
402
     *
403
     * @return string
404
     * @deprecated Use $this->getApiKey() instead
405
     */
406 10
    public function getPassword(): string
407
    {
408 10
        if (!isset($this->password)) {
0 ignored issues
show
Deprecated Code introduced by
The property TxTextControl\ReportingC...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

408
        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...
409 2
            $this->password = '';
0 ignored issues
show
Deprecated Code introduced by
The property TxTextControl\ReportingC...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

409
            /** @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...
410
        }
411
412 10
        return $this->password;
413
    }
414
415
    /**
416
     * Set the password
417
     *
418
     * @param string $password
419
     *
420
     * @return self
421
     * @deprecated Use $this->setApiKey(string $apiKey): self instead
422
     */
423 8
    public function setPassword(string $password): self
424
    {
425 8
        $this->password = $password;
0 ignored issues
show
Deprecated Code introduced by
The property TxTextControl\ReportingC...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

425
        /** @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...
426
427 8
        return $this;
428
    }
429
430
    /**
431
     * Return the base URI of the backend web service
432
     *
433
     * @return string
434
     */
435 78
    public function getBaseUri(): string
436
    {
437 78
        if (!isset($this->baseUri)) {
438 74
            $baseUri = ConsoleUtils::baseUri();
439 74
            if (0 === strlen($baseUri)) {
440 74
                $baseUri = self::DEFAULT_BASE_URI;
441
            }
442 74
            Assert::assertBaseUri($baseUri);
443 74
            $this->setBaseUri($baseUri);
444
        }
445
446 78
        return $this->baseUri;
447
    }
448
449
    /**
450
     * Set the base URI of the backend web service
451
     *
452
     * @param string $baseUri
453
     *
454
     * @return self
455
     */
456 78
    public function setBaseUri(string $baseUri): self
457
    {
458 78
        $this->baseUri = $baseUri;
459
460 78
        return $this;
461
    }
462
463
    /**
464
     * Return the debug flag
465
     *
466
     * @return bool
467
     */
468 74
    public function getDebug(): bool
469
    {
470 74
        if (!isset($this->debug)) {
471 70
            $this->debug = self::DEFAULT_DEBUG;
472
        }
473
474 74
        return $this->debug;
475
    }
476
477
    /**
478
     * Set the debug flag
479
     *
480
     * @param bool $debug Debug flag
481
     *
482
     * @return self
483
     */
484 4
    public function setDebug(bool $debug): self
485
    {
486 4
        $this->debug = $debug;
487
488 4
        return $this;
489
    }
490
491
    /**
492
     * Return the test flag
493
     *
494
     * @return bool
495
     */
496 68
    public function getTest(): bool
497
    {
498 68
        if (!isset($this->test)) {
499 64
            $this->test = self::DEFAULT_TEST;
500
        }
501
502 68
        return $this->test;
503
    }
504
505
    /**
506
     * Set the test flag
507
     *
508
     * @param bool $test
509
     *
510
     * @return self
511
     */
512 6
    public function setTest(bool $test): self
513
    {
514 6
        $this->test = $test;
515
516 6
        return $this;
517
    }
518
519
    /**
520
     * Get the timeout (in seconds) of the backend web service
521
     *
522
     * @return int
523
     */
524 74
    public function getTimeout(): int
525
    {
526 74
        if (!isset($this->timeout)) {
527 70
            $this->timeout = self::DEFAULT_TIMEOUT;
528
        }
529
530 74
        return $this->timeout;
531
    }
532
533
    /**
534
     * Set the timeout (in seconds) of the backend web service
535
     *
536
     * @param int $timeout
537
     *
538
     * @return self
539
     */
540 4
    public function setTimeout(int $timeout): self
541
    {
542 4
        $this->timeout = $timeout;
543
544 4
        return $this;
545
    }
546
547
    /**
548
     * Get the version string of the backend web service
549
     *
550
     * @return string
551
     */
552 72
    public function getVersion(): string
553
    {
554 72
        if (!isset($this->version)) {
555 68
            $this->version = self::DEFAULT_VERSION;
556
        }
557
558 72
        return $this->version;
559
    }
560
561
    /**
562
     * Set the version string of the backend web service
563
     *
564
     * @param string $version
565
     *
566
     * @return self
567
     */
568 4
    public function setVersion(string $version): self
569
    {
570 4
        $this->version = $version;
571
572 4
        return $this;
573
    }
574
575
    /**
576
     * Return the REST client of the backend web service
577
     *
578
     * @return Client
579
     */
580 70
    public function getClient(): Client
581
    {
582 70
        if (!isset($this->client)) {
583
584 70
            $headers = [
585 70
                'Authorization' => $this->getAuthorizationHeader(),
586 70
            ];
587
588 68
            $options = [
589 68
                'base_uri'              => $this->getBaseUri(),
590 68
                RequestOptions::TIMEOUT => $this->getTimeout(),
591 68
                RequestOptions::DEBUG   => $this->getDebug(),
592 68
                RequestOptions::HEADERS => $headers,
593 68
            ];
594
595 68
            $client = new Client($options);
596
597 68
            $this->setClient($client);
598
        }
599
600 68
        return $this->client;
601
    }
602
603
    /**
604
     * Set the REST client of the backend web service
605
     *
606
     * @param Client $client
607
     *
608
     * @return self
609
     */
610 68
    public function setClient(Client $client): self
611
    {
612 68
        $this->client = $client;
613
614 68
        return $this;
615
    }
616
617
    /**
618
     * Request the URI with options
619
     *
620
     * @param string $method  HTTP method
621
     * @param string $uri     URI
622
     * @param array  $options Options
623
     *
624
     * @return ResponseInterface
625
     * @throws RuntimeException
626
     */
627 66
    protected function request(string $method, string $uri, array $options): ResponseInterface
628
    {
629 66
        $client = $this->getClient();
630
631
        try {
632 64
            $test = $this->getTest();
633 64
            if ($test) {
634 2
                assert(is_array($options[RequestOptions::QUERY]));
635 2
                $options[RequestOptions::QUERY]['test'] = Filter::filterBooleanToString($test);
636
            }
637 64
            $response = $client->request($method, $uri, $options);
638 4
        } catch (GuzzleException $e) {
639 4
            throw new RuntimeException($e->getMessage(), $e->getCode());
640
        }
641
642 62
        return $response;
643
    }
644
645
    /**
646
     * Construct URI with version number
647
     *
648
     * @param string $uri URI
649
     *
650
     * @return string
651
     */
652 66
    protected function uri(string $uri): string
653
    {
654 66
        return sprintf('/%s%s', $this->getVersion(), $uri);
655
    }
656
657
    /**
658
     * Return Authorization Header, with either API key or username and password
659
     *
660
     * @return string
661
     * @throws InvalidArgumentException
662
     */
663 70
    private function getAuthorizationHeader(): string
664
    {
665 70
        $apiKey = $this->getApiKey();
666
667 70
        if (strlen($apiKey) > 0) {
668 66
            return sprintf('ReportingCloud-APIKey %s', $apiKey);
669
        }
670
671 4
        $username = $this->getUsername();
0 ignored issues
show
Deprecated Code introduced by
The function TxTextControl\ReportingC...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

671
        $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...
672 4
        $password = $this->getPassword();
0 ignored issues
show
Deprecated Code introduced by
The function TxTextControl\ReportingC...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

672
        $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...
673
674 4
        if (strlen($username) > 0 && strlen($password) > 0) {
675 2
            $value = sprintf('%s:%s', $username, $password);
676 2
            return sprintf('Basic %s', base64_encode($value));
677
        }
678
679 2
        $message = 'Either the API key, or username and password must be set for authorization';
680 2
        throw new InvalidArgumentException($message);
681
    }
682
683
    // </editor-fold>
684
}
685