Completed
Push — master ( 55b55f...e456b8 )
by Tomáš
40:56
created

Client   F

Complexity

Total Complexity 62

Size/Duplication

Total Lines 871
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 62
lcom 1
cbo 2
dl 0
loc 871
c 0
b 0
f 0
rs 3.169
ccs 229
cts 229
cp 1

38 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A dropPackage() 0 4 1
A dropPackages() 0 10 2
A trackPackage() 0 6 1
A addPackages() 0 19 3
B trackPackages() 0 38 6
A trackPackageLastStatus() 0 6 1
A trackPackagesLastStatus() 0 26 2
A getOverview() 0 6 1
A getLabels() 0 12 1
A getPackageInfo() 0 6 1
A getManipulationUnits() 0 12 2
A getCountries() 0 12 1
A getActivatedServices() 0 8 1
A getProofOfDelivery() 0 6 1
A getPackageInfoByCarrierId() 0 12 1
A orderShipment() 0 12 1
A getOrder() 0 8 1
A orderPickup() 0 19 1
A getServices() 0 8 2
A getB2AServices() 0 8 1
A getActivatedManipulationUnits() 0 12 2
A getBranches() 0 19 3
A getBranchesForLocation() 0 26 1
A getCodCountries() 0 12 1
A getPostCodes() 0 20 2
A checkPackages() 0 4 1
A getAdrUnits() 0 12 2
A orderB2AShipment() 0 12 1
A getProofOfDeliveries() 0 20 2
A getTransportCosts() 0 12 1
A getCountriesData() 0 8 1
A getChangelog() 0 8 1
A validateStatus() 0 6 3
A validateResponseItemHasAttribute() 0 8 3
A validateIndexes() 0 6 2
A normalizeResponseItems() 0 10 3
A encapsulateIds() 0 8 1

How to fix   Complexity   

Complex Class

Complex classes like Client often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Client, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Inspirum\Balikobot\Services;
4
5
use DateTime;
6
use Inspirum\Balikobot\Contracts\RequesterInterface;
7
use Inspirum\Balikobot\Definitions\API;
8
use Inspirum\Balikobot\Definitions\Request;
9
use Inspirum\Balikobot\Definitions\Shipper;
10
use Inspirum\Balikobot\Exceptions\BadRequestException;
11
12
class Client
13
{
14
    /**
15
     * API requester
16
     *
17
     * @var \Inspirum\Balikobot\Contracts\RequesterInterface
18
     */
19
    private $requester;
20
21
    /**
22
     * Balikobot API client
23
     *
24
     * @param \Inspirum\Balikobot\Contracts\RequesterInterface $requester
25
     */
26 278
    public function __construct(RequesterInterface $requester)
27
    {
28 278
        $this->requester = $requester;
29 278
    }
30
31
    /**
32
     * Add package(s) to the Balikobot
33
     *
34
     * @param string                     $shipper
35
     * @param array<array<string,mixed>> $packages
36
     * @param string|null                $version
37
     * @param mixed|null                 $labelsUrl
38
     *
39
     * @return array<array<string,mixed>>
40
     *
41
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
42
     */
43 25
    public function addPackages(string $shipper, array $packages, string $version = null, &$labelsUrl = null): array
44
    {
45 25
        $response = $this->requester->call($version ?: API::V1, $shipper, Request::ADD, $packages);
46
47 16
        if (isset($response['labels_url'])) {
48 5
            $labelsUrl = $response['labels_url'];
49
        }
50
51
        unset(
52 16
            $response['labels_url'],
53 16
            $response['status']
54
        );
55
56 16
        $this->validateIndexes($response, $packages);
57
58 15
        $this->validateResponseItemHasAttribute($response, 'package_id', $response);
59
60 13
        return $response;
61
    }
62
63
    /**
64
     * Drops a package from the Balikobot – the package must be not ordered
65
     *
66
     * @param string $shipper
67
     * @param int    $packageId
68
     *
69
     * @return void
70
     *
71
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
72
     */
73 2
    public function dropPackage(string $shipper, int $packageId): void
74
    {
75 2
        $this->dropPackages($shipper, [$packageId]);
76 2
    }
77
78
    /**
79
     * Drops a package from the Balikobot – the package must be not ordered
80
     *
81
     * @param string     $shipper
82
     * @param array<int> $packageIds
83
     *
84
     * @return void
85
     *
86
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
87
     */
88 7
    public function dropPackages(string $shipper, array $packageIds): void
89
    {
90 7
        $data = $this->encapsulateIds($packageIds);
91
92 7
        if (count($data) === 0) {
93 1
            return;
94
        }
95
96 6
        $this->requester->call(API::V1, $shipper, Request::DROP, $data);
97 3
    }
98
99
    /**
100
     * Tracks a package
101
     *
102
     * @param string $shipper
103
     * @param string $carrierId
104
     *
105
     * @return array<array<string,float|string>>
106
     *
107
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
108
     */
109 7
    public function trackPackage(string $shipper, string $carrierId): array
110
    {
111 7
        $response = $this->trackPackages($shipper, [$carrierId]);
112
113 4
        return $response[0];
114
    }
115
116
    /**
117
     * Tracks a packages
118
     *
119
     * @param string        $shipper
120
     * @param array<string> $carrierIds
121
     *
122
     * @return array<array<array<string,float|string>>>
123
     *
124
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
125
     */
126 21
    public function trackPackages(string $shipper, array $carrierIds): array
127
    {
128 21
        $data = $this->encapsulateIds($carrierIds);
129
130 21
        $response = $this->requester->call(API::V3, $shipper, Request::TRACK, $data, false);
131
132 17
        unset($response['status']);
133
134
        // fixes that API return only last package statuses for GLS shipper
135 17
        if ($shipper === Shipper::GLS && count($carrierIds) !== count($response)) {
136 1
            for ($i = 0; $i < count($carrierIds) - 1; $i++) {
137 1
                $response[$i] = $response[$i] ?? [];
138
            }
139 1
            sort($response);
140
        }
141
142 17
        $this->validateIndexes($response, $carrierIds);
143
144 13
        $formattedResponse = [];
145
146 13
        foreach ($response ?? [] as $i => $responseItems) {
147 13
            $this->validateResponseItemHasAttribute($responseItems, 'status_id', $response);
148
149 12
            $formattedResponse[$i] = [];
150
151 12
            foreach ($responseItems ?? [] as $responseItem) {
152 12
                $formattedResponse[$i][] = [
153 12
                    'date'          => $responseItem['date'],
154 12
                    'name'          => $responseItem['name'],
155 12
                    'status_id'     => (float) ($responseItem['status_id_v2'] ?? $responseItem['status_id']),
156 12
                    'type'          => $responseItem['type'] ?? 'event',
157 12
                    'name_internal' => $responseItem['name_balikobot'] ?? $responseItem['name'],
158
                ];
159
            }
160
        }
161
162 11
        return $formattedResponse;
163
    }
164
165
    /**
166
     * Tracks a package, get the last info
167
     *
168
     * @param string $shipper
169
     * @param string $carrierId
170
     *
171
     * @return array<string,float|string>
172
     *
173
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
174
     */
175 7
    public function trackPackageLastStatus(string $shipper, string $carrierId): array
176
    {
177 7
        $response = $this->trackPackagesLastStatus($shipper, [$carrierId]);
178
179 3
        return $response[0];
180
    }
181
182
    /**
183
     * Tracks a package, get the last info
184
     *
185
     * @param string        $shipper
186
     * @param array<string> $carrierIds
187
     *
188
     * @return array<array<string,float|string|null>>
189
     *
190
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
191
     */
192 19
    public function trackPackagesLastStatus(string $shipper, array $carrierIds): array
193
    {
194 19
        $data = $this->encapsulateIds($carrierIds);
195
196 19
        $response = $this->requester->call(API::V2, $shipper, Request::TRACK_STATUS, $data, false);
197
198 15
        unset($response['status']);
199
200 15
        $this->validateIndexes($response, $carrierIds);
201
202 12
        $formattedStatuses = [];
203
204 12
        foreach ($response as $responseItem) {
205 12
            $this->validateStatus($responseItem, $response);
206
207 11
            $formattedStatuses[] = [
208 11
                'name'          => $responseItem['status_text'],
209 11
                'name_internal' => $responseItem['status_text'],
210 11
                'type'          => 'event',
211 11
                'status_id'     => (float) $responseItem['status_id'],
212
                'date'          => null,
213
            ];
214
        }
215
216 10
        return $formattedStatuses;
217
    }
218
219
    /**
220
     * Returns packages from the front (not ordered) for given shipper
221
     *
222
     * @param string $shipper
223
     *
224
     * @return array<array<string,int|string>>
225
     *
226
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
227
     */
228 6
    public function getOverview(string $shipper): array
229
    {
230 6
        $response = $this->requester->call(API::V1, $shipper, Request::OVERVIEW, [], false);
231
232 4
        return $response;
233
    }
234
235
    /**
236
     * Gets labels
237
     *
238
     * @param string     $shipper
239
     * @param array<int> $packageIds
240
     *
241
     * @return string
242
     *
243
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
244
     */
245 7
    public function getLabels(string $shipper, array $packageIds): string
246
    {
247
        $data = [
248 7
            'package_ids' => $packageIds,
249
        ];
250
251 7
        $response = $this->requester->call(API::V1, $shipper, Request::LABELS, $data);
252
253 4
        $formattedResponse = $response['labels_url'];
254
255 4
        return $formattedResponse;
256
    }
257
258
    /**
259
     * Gets complete information about a package by its package ID
260
     *
261
     * @param string $shipper
262
     * @param int    $packageId
263
     *
264
     * @return array<string,int|string>
265
     *
266
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
267
     */
268 6
    public function getPackageInfo(string $shipper, int $packageId): array
269
    {
270 6
        $response = $this->requester->call(API::V1, $shipper, Request::PACKAGE . '/' . $packageId, [], false);
271
272 4
        return $response;
273
    }
274
275
    /**
276
     * Gets complete information about a package by its carrier ID
277
     *
278
     * @param string $shipper
279
     * @param string $carrierId
280
     *
281
     * @return array<string,int|string>
282
     *
283
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
284
     */
285 4
    public function getPackageInfoByCarrierId(string $shipper, string $carrierId): array
286
    {
287 4
        $response = $this->requester->call(
288 4
            API::V1,
289
            $shipper,
290 4
            Request::PACKAGE . '/carrier_id/' . $carrierId,
291 4
            [],
292 4
            false
293
        );
294
295 2
        return $response;
296
    }
297
298
    /**
299
     * Order shipment for packages
300
     *
301
     * @param string     $shipper
302
     * @param array<int> $packageIds
303
     *
304
     * @return array<string,int|string>
305
     *
306
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
307
     */
308 7
    public function orderShipment(string $shipper, array $packageIds): array
309
    {
310
        $data = [
311 7
            'package_ids' => $packageIds,
312
        ];
313
314 7
        $response = $this->requester->call(API::V1, $shipper, Request::ORDER, $data);
315
316 4
        unset($response['status']);
317
318 4
        return $response;
319
    }
320
321
    /**
322
     * Get order details
323
     *
324
     * @param string $shipper
325
     * @param int    $orderId
326
     *
327
     * @return array<string,int|string|array>
328
     *
329
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
330
     */
331 7
    public function getOrder(string $shipper, int $orderId): array
332
    {
333 7
        $response = $this->requester->call(API::V1, $shipper, Request::ORDER_VIEW . '/' . $orderId, [], false);
334
335 5
        unset($response['status']);
336
337 5
        return $response;
338
    }
339
340
    /**
341
     * Order pickup for packages
342
     *
343
     * @param string      $shipper
344
     * @param \DateTime   $dateFrom
345
     * @param \DateTime   $dateTo
346
     * @param float       $weight
347
     * @param int         $packageCount
348
     * @param string|null $message
349
     *
350
     * @return void
351
     *
352
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
353
     */
354 4
    public function orderPickup(
355
        string $shipper,
356
        DateTime $dateFrom,
357
        DateTime $dateTo,
358
        float $weight,
359
        int $packageCount,
360
        string $message = null
361
    ): void {
362
        $data = [
363 4
            'date'          => $dateFrom->format('Y-m-d'),
364 4
            'time_from'     => $dateFrom->format('H:s'),
365 4
            'time_to'       => $dateTo->format('H:s'),
366 4
            'weight'        => $weight,
367 4
            'package_count' => $packageCount,
368 4
            'message'       => $message,
369
        ];
370
371 4
        $this->requester->call(API::V1, $shipper, Request::ORDER_PICKUP, $data);
372 1
    }
373
374
    /**
375
     * Returns available services for the given shipper
376
     *
377
     * @param string      $shipper
378
     * @param string|null $country
379
     * @param string|null $version
380
     *
381
     * @return array<string,string>
382
     *
383
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
384
     */
385 12
    public function getServices(string $shipper, string $country = null, string $version = null): array
386
    {
387 12
        $response = $this->requester->call($version ?: API::V1, $shipper, Request::SERVICES . '/' . $country);
388
389 8
        $formattedResponse = $response['service_types'] ?? [];
390
391 8
        return $formattedResponse;
392
    }
393
394
    /**
395
     * Returns available B2A services for the given shipper
396
     *
397
     * @param string $shipper
398
     *
399
     * @return array<string,string>
400
     *
401
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
402
     */
403 8
    public function getB2AServices(string $shipper): array
404
    {
405 8
        $response = $this->requester->call(API::V1, $shipper, Request::B2A . '/' . Request::SERVICES);
406
407 5
        $formattedResponse = $response['service_types'] ?? [];
408
409 5
        return $formattedResponse;
410
    }
411
412
    /**
413
     * Returns all manipulation units for the given shipper
414
     *
415
     * @param string $shipper
416
     * @param bool   $fullData
417
     *
418
     * @return array<string,string|array>
419
     *
420
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
421
     */
422 9
    public function getManipulationUnits(string $shipper, bool $fullData = false): array
423
    {
424 9
        $response = $this->requester->call(API::V1, $shipper, Request::MANIPULATION_UNITS);
425
426 6
        $formattedResponse = $this->normalizeResponseItems(
427 6
            $response['units'] ?? [],
428 6
            'code',
429 6
            $fullData === false ? 'name' : null
430
        );
431
432 6
        return $formattedResponse;
433
    }
434
435
    /**
436
     * Returns available manipulation units for the given shipper
437
     *
438
     * @param string $shipper
439
     * @param bool   $fullData
440
     *
441
     * @return array<string,string|array>
442
     *
443
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
444
     */
445 9
    public function getActivatedManipulationUnits(string $shipper, bool $fullData = false): array
446
    {
447 9
        $response = $this->requester->call(API::V1, $shipper, Request::ACTIVATED_MANIPULATION_UNITS);
448
449 6
        $formattedResponse = $this->normalizeResponseItems(
450 6
            $response['units'] ?? [],
451 6
            'code',
452 6
            $fullData === false ? 'name' : null
453
        );
454
455 6
        return $formattedResponse;
456
    }
457
458
    /**
459
     * Returns available branches for the given shipper and its service
460
     * Full branches instead branches request
461
     *
462
     * @param string      $shipper
463
     * @param string|null $service
464
     * @param bool        $fullBranchRequest
465
     * @param string|null $country
466
     * @param string|null $version
467
     *
468
     * @return array<array<string,mixed>>
469
     *
470
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
471
     */
472 15
    public function getBranches(
473
        string $shipper,
474
        ?string $service,
475
        bool $fullBranchRequest = false,
476
        string $country = null,
477
        string $version = null
478
    ): array {
479 15
        $usedRequest = $fullBranchRequest ? Request::FULL_BRANCHES : Request::BRANCHES;
480
481 15
        $response = $this->requester->call(
482 15
            $version ?: API::V1,
483
            $shipper,
484 15
            $usedRequest . '/' . $service . '/' . $country
485
        );
486
487 12
        $formattedResponse = $response['branches'] ?? [];
488
489 12
        return $formattedResponse;
490
    }
491
492
    /**
493
     * Returns available branches for the given shipper in given location
494
     *
495
     * @param string      $shipper
496
     * @param string      $country
497
     * @param string      $city
498
     * @param string|null $postcode
499
     * @param string|null $street
500
     * @param int|null    $maxResults
501
     * @param float|null  $radius
502
     * @param string|null $type
503
     *
504
     * @return array<array<string,mixed>>
505
     *
506
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
507
     */
508 9
    public function getBranchesForLocation(
509
        string $shipper,
510
        string $country,
511
        string $city,
512
        string $postcode = null,
513
        string $street = null,
514
        int $maxResults = null,
515
        float $radius = null,
516
        string $type = null
517
    ): array {
518
        $data = [
519 9
            'country'     => $country,
520 9
            'city'        => $city,
521 9
            'zip'         => $postcode,
522 9
            'street'      => $street,
523 9
            'max_results' => $maxResults,
524 9
            'radius'      => $radius,
525 9
            'type'        => $type,
526
        ];
527
528 9
        $response = $this->requester->call(API::V1, $shipper, Request::BRANCH_LOCATOR, array_filter($data));
529
530 6
        $formattedResponse = $response['branches'] ?? [];
531
532 6
        return $formattedResponse;
533
    }
534
535
    /**
536
     * Returns list of countries where service with cash-on-delivery payment type is available in
537
     *
538
     * @param string $shipper
539
     *
540
     * @return array<array<int|string,array<string,array>>>
541
     *
542
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
543
     */
544 8
    public function getCodCountries(string $shipper): array
545
    {
546 8
        $response = $this->requester->call(API::V1, $shipper, Request::CASH_ON_DELIVERY_COUNTRIES);
547
548 5
        $formattedResponse = $this->normalizeResponseItems(
549 5
            $response['service_types'] ?? [],
550 5
            'service_type',
551 5
            'cod_countries'
552
        );
553
554 5
        return $formattedResponse;
555
    }
556
557
    /**
558
     * Returns list of countries where service is available in
559
     *
560
     * @param string $shipper
561
     *
562
     * @return array<array<int|string,string>>
563
     *
564
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
565
     */
566 8
    public function getCountries(string $shipper): array
567
    {
568 8
        $response = $this->requester->call(API::V1, $shipper, Request::COUNTRIES);
569
570 5
        $formattedResponse = $this->normalizeResponseItems(
571 5
            $response['service_types'] ?? [],
572 5
            'service_type',
573 5
            'countries'
574
        );
575
576 5
        return $formattedResponse;
577
    }
578
579
    /**
580
     * Returns available branches for the given shipper and its service
581
     *
582
     * @param string      $shipper
583
     * @param string      $service
584
     * @param string|null $country
585
     *
586
     * @return array<array<string,mixed>>
587
     *
588
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
589
     */
590 12
    public function getPostCodes(string $shipper, string $service, string $country = null): array
591
    {
592 12
        $response = $this->requester->call(API::V1, $shipper, Request::ZIP_CODES . '/' . $service . '/' . $country);
593
594 9
        $country = $response['country'] ?? $country;
595
596 9
        $formattedResponse = [];
597
598 9
        foreach ($response['zip_codes'] ?? [] as $responseItem) {
599 5
            $formattedResponse[] = [
600 5
                'postcode'     => $responseItem['zip'] ?? ($responseItem['zip_start'] ?? null),
601 5
                'postcode_end' => $responseItem['zip_end'] ?? null,
602 5
                'city'         => $responseItem['city'] ?? null,
603 5
                'country'      => $responseItem['country'] ?? $country,
604 5
                '1B'           => (bool) ($responseItem['1B'] ?? false),
605
            ];
606
        }
607
608 9
        return $formattedResponse;
609
    }
610
611
    /**
612
     * Check package(s) data
613
     *
614
     * @param string               $shipper
615
     * @param array<array<string>> $packages
616
     *
617
     * @return void
618
     *
619
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
620
     */
621 5
    public function checkPackages(string $shipper, array $packages): void
622
    {
623 5
        $this->requester->call(API::V1, $shipper, Request::CHECK, $packages);
624 2
    }
625
626
    /**
627
     * Returns available manipulation units for the given shipper
628
     *
629
     * @param string $shipper
630
     * @param bool   $fullData
631
     *
632
     * @return array<string>
633
     *
634
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
635
     */
636 9
    public function getAdrUnits(string $shipper, bool $fullData = false): array
637
    {
638 9
        $response = $this->requester->call(API::V1, $shipper, Request::ADR_UNITS);
639
640 6
        $formattedResponse = $this->normalizeResponseItems(
641 6
            $response['units'] ?? [],
642 6
            'code',
643 6
            $fullData === false ? 'name' : null
644
        );
645
646 6
        return $formattedResponse;
647
    }
648
649
    /**
650
     * Returns available activated services for the given shipper
651
     *
652
     * @param string $shipper
653
     *
654
     * @return array<string,mixed>
655
     *
656
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
657
     */
658 6
    public function getActivatedServices(string $shipper): array
659
    {
660 6
        $response = $this->requester->call(API::V1, $shipper, Request::ACTIVATED_SERVICES);
661
662 4
        unset($response['status']);
663
664 4
        return $response;
665
    }
666
667
    /**
668
     * Order shipments from place B (typically supplier / previous consignee) to place A (shipping point)
669
     *
670
     * @param string                     $shipper
671
     * @param array<array<string,mixed>> $packages
672
     *
673
     * @return array<array<string,mixed>>
674
     *
675
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
676
     */
677 11
    public function orderB2AShipment(string $shipper, array $packages): array
678
    {
679 11
        $response = $this->requester->call(API::V1, $shipper, Request::B2A, $packages);
680
681 8
        unset($response['status']);
682
683 8
        $this->validateIndexes($response, $packages);
684
685 7
        $this->validateResponseItemHasAttribute($response, 'package_id', $response);
686
687 5
        return $response;
688
    }
689
690
    /**
691
     * Get PDF link with signed consignment delivery document by the recipient
692
     *
693
     * @param string $shipper
694
     * @param string $carrierId
695
     *
696
     * @return string
697
     *
698
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
699
     */
700 7
    public function getProofOfDelivery(string $shipper, string $carrierId): string
701
    {
702 7
        $response = $this->getProofOfDeliveries($shipper, [$carrierId]);
703
704 3
        return $response[0];
705
    }
706
707
    /**
708
     * Get array of PDF links with signed consignment delivery document by the recipient
709
     *
710
     * @param string        $shipper
711
     * @param array<string> $carrierIds
712
     *
713
     * @return array<string>
714
     *
715
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
716
     */
717 19
    public function getProofOfDeliveries(string $shipper, array $carrierIds): array
718
    {
719 19
        $data = $this->encapsulateIds($carrierIds);
720
721 19
        $response = $this->requester->call(API::V1, $shipper, Request::PROOF_OF_DELIVERY, $data, false);
722
723 15
        unset($response['status']);
724
725 15
        $this->validateIndexes($response, $carrierIds);
726
727 11
        $formattedLinks = [];
728
729 11
        foreach ($response as $responseItem) {
730 11
            $this->validateStatus($responseItem, $response);
731
732 11
            $formattedLinks[] = $responseItem['file_url'];
733
        }
734
735 10
        return $formattedLinks;
736
    }
737
738
    /**
739
     * Obtain the price of carriage at consignment level
740
     *
741
     * @param string                     $shipper
742
     * @param array<array<string,mixed>> $packages
743
     *
744
     * @return array<array<string,mixed>>
745
     *
746
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
747
     */
748 10
    public function getTransportCosts(string $shipper, array $packages): array
749
    {
750 10
        $response = $this->requester->call(API::V1, $shipper, Request::TRANSPORT_COSTS, $packages);
751
752 7
        unset($response['status']);
753
754 7
        $this->validateIndexes($response, $packages);
755
756 6
        $this->validateResponseItemHasAttribute($response, 'eid', $response);
757
758 4
        return $response;
759
    }
760
761
    /**
762
     * Get information on individual countries of the world
763
     *
764
     * @return array<array<string,mixed>>
765
     *
766
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
767
     */
768 8
    public function getCountriesData(): array
769
    {
770 8
        $response = $this->requester->call(API::V1, '', Request::GET_COUNTRIES_DATA);
771
772 5
        $formattedResponse = $this->normalizeResponseItems($response['countries'] ?? [], 'iso_code', null);
773
774 5
        return $formattedResponse;
775
    }
776
777
    /**
778
     * Method for obtaining news in the Balikobot API
779
     *
780
     * @return array<string,mixed>
781
     *
782
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
783
     */
784
    public function getChangelog(): array
785
    {
786
        $response = $this->requester->call(API::V1, '', Request::CHANGELOG);
787 23
788
        unset($response['status']);
789 23
790 3
        return $response;
791
    }
792 22
793
    /**
794
     * Validate response item status
795
     *
796
     * @param array<mixed,mixed> $responseItem
797
     * @param array<mixed,mixed> $response
798
     *
799
     * @return void
800
     *
801
     * @throws \Inspirum\Balikobot\Exceptions\BadRequestException
802
     */
803
    private function validateStatus(array $responseItem, array $response): void
804
    {
805 41
        if (isset($responseItem['status']) && ((int) $responseItem['status']) !== 200) {
806
            throw new BadRequestException($response);
807 41
        }
808 41
    }
809 8
810
    /**
811
     * Validate that every response item has given attribute
812 34
     *
813
     * @param array<int,array<string,mixed>> $items
814
     * @param string                         $attribute
815
     * @param array<mixed,mixed>             $response
816
     *
817
     * @return void
818
     *
819
     * @throws \Inspirum\Balikobot\Exceptions\BadRequestException
820
     */
821
    private function validateResponseItemHasAttribute(array $items, string $attribute, array $response): void
822
    {
823
        foreach ($items as $item) {
824 78
            if (isset($item[$attribute]) === false) {
825
                throw new BadRequestException($response);
826 78
            }
827 14
        }
828
    }
829 64
830
    /**
831
     * Validate indexes
832
     *
833
     * @param array<mixed,mixed> $response
834
     * @param array<mixed,mixed> $request
835
     *
836
     * @return void
837
     *
838
     * @throws \Inspirum\Balikobot\Exceptions\BadRequestException
839
     */
840 33
    private function validateIndexes(array $response, array $request): void
841
    {
842 33
        if (array_keys($response) !== range(0, count($request) - 1)) {
843
            throw new BadRequestException($response);
844 33
        }
845 15
    }
846
847
    /**
848 33
     * Normalize response items
849
     *
850
     * @param array<array<string,string>> $items
851
     * @param string                      $keyName
852
     * @param string|null                 $valueName
853
     *
854
     * @return array<string,mixed>
855
     */
856
    private function normalizeResponseItems(array $items, string $keyName, ?string $valueName): array
857
    {
858 66
        $formattedResponse = [];
859
860 66
        foreach ($items as $item) {
861
            $formattedResponse[$item[$keyName]] = $valueName !== null ? $item[$valueName] : $item;
862 65
        }
863
864 66
        return $formattedResponse;
865
    }
866
867
    /**
868
     * Encapsulate ids
869
     *
870
     * @param array<int|string> $ids
871
     *
872
     * @return array<array<int|string>>
873
     */
874
    private function encapsulateIds(array $ids): array
875
    {
876
        return array_map(function ($carrierId) {
877
            return [
878
                'id' => $carrierId,
879
            ];
880
        }, $ids);
881
    }
882
}
883