Completed
Push — master ( d3b32b...f42a73 )
by Tomáš
41:22
created

Client   F

Complexity

Total Complexity 61

Size/Duplication

Total Lines 855
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 61
lcom 1
cbo 2
dl 0
loc 855
ccs 222
cts 222
cp 1
rs 3.265
c 0
b 0
f 0

37 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 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 getManipulationUnits() 0 12 2
A getActivatedManipulationUnits() 0 12 2
A getBranches() 0 19 3
A getBranchesForLocation() 0 26 1
A getCodCountries() 0 12 1
A getCountries() 0 12 1
A getPostCodes() 0 20 2
A checkPackages() 0 4 1
A getAdrUnits() 0 12 2
A getActivatedServices() 0 8 1
A orderB2AShipment() 0 12 1
A getProofOfDelivery() 0 6 1
A getProofOfDeliveries() 0 20 2
A getTransportCosts() 0 12 1
A getCountriesData() 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 274
    public function __construct(RequesterInterface $requester)
27
    {
28 274
        $this->requester = $requester;
29 274
    }
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 7
    public function getPackageInfoByCarrierId(string $shipper, string $carrierId): array
286
    {
287
        $response = $this->requester->call(
288 7
            API::V1,
289
            $shipper,
290
            Request::PACKAGE . '/carrier_id/' . $carrierId,
291 7
            [],
292
            false
293 4
        );
294
295 4
        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 7
        $data = [
311
            'package_ids' => $packageIds,
312 5
        ];
313
314 5
        $response = $this->requester->call(API::V1, $shipper, Request::ORDER, $data);
315
316
        unset($response['status']);
317
318
        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 4
    public function getOrder(string $shipper, int $orderId): array
332
    {
333
        $response = $this->requester->call(API::V1, $shipper, Request::ORDER_VIEW . '/' . $orderId, [], false);
334
335
        unset($response['status']);
336
337
        return $response;
338
    }
339
340 4
    /**
341 4
     * Order pickup for packages
342 4
     *
343 4
     * @param string      $shipper
344 4
     * @param \DateTime   $dateFrom
345 4
     * @param \DateTime   $dateTo
346
     * @param float       $weight
347
     * @param int         $packageCount
348 4
     * @param string|null $message
349 1
     *
350
     * @return void
351
     *
352
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
353
     */
354
    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 12
        $data = [
363
            'date'          => $dateFrom->format('Y-m-d'),
364 12
            'time_from'     => $dateFrom->format('H:s'),
365
            'time_to'       => $dateTo->format('H:s'),
366 8
            'weight'        => $weight,
367
            'package_count' => $packageCount,
368 8
            'message'       => $message,
369
        ];
370
371
        $this->requester->call(API::V1, $shipper, Request::ORDER_PICKUP, $data);
372
    }
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 8
     *
381
     * @return array<string,string>
382 8
     *
383
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
384 5
     */
385
    public function getServices(string $shipper, string $country = null, string $version = null): array
386 5
    {
387
        $response = $this->requester->call($version ?: API::V1, $shipper, Request::SERVICES . '/' . $country);
388
389
        $formattedResponse = $response['service_types'] ?? [];
390
391
        return $formattedResponse;
392
    }
393
394
    /**
395
     * Returns available B2A services for the given shipper
396
     *
397
     * @param string $shipper
398
     *
399 9
     * @return array<string,string>
400
     *
401 9
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
402
     */
403 6
    public function getB2AServices(string $shipper): array
404 6
    {
405 6
        $response = $this->requester->call(API::V1, $shipper, Request::B2A . '/' . Request::SERVICES);
406 6
407
        $formattedResponse = $response['service_types'] ?? [];
408
409 6
        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
    public function getActivatedManipulationUnits(string $shipper, bool $fullData = false): array
446
    {
447
        $response = $this->requester->call(API::V1, $shipper, Request::ACTIVATED_MANIPULATION_UNITS);
448
449 15
        $formattedResponse = $this->normalizeResponseItems(
450
            $response['units'] ?? [],
451
            'code',
452
            $fullData === false ? 'name' : null
453
        );
454
455
        return $formattedResponse;
456 15
    }
457
458 15
    /**
459 15
     * Returns available branches for the given shipper and its service
460
     * Full branches instead branches request
461 15
     *
462
     * @param string      $shipper
463
     * @param string|null $service
464 12
     * @param bool        $fullBranchRequest
465
     * @param string|null $country
466 12
     * @param string|null $version
467
     *
468
     * @return array<array<string,mixed>>
469
     *
470
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
471
     */
472
    public function getBranches(
473
        string $shipper,
474
        ?string $service,
475
        bool $fullBranchRequest = false,
476
        string $country = null,
477
        string $version = null
478
    ): array {
479
        $usedRequest = $fullBranchRequest ? Request::FULL_BRANCHES : Request::BRANCHES;
480
481
        $response = $this->requester->call(
482
            $version ?: API::V1,
483
            $shipper,
484
            $usedRequest . '/' . $service . '/' . $country
485 9
        );
486
487
        $formattedResponse = $response['branches'] ?? [];
488
489
        return $formattedResponse;
490
    }
491
492
    /**
493
     * Returns available branches for the given shipper in given location
494
     *
495
     * @param string      $shipper
496 9
     * @param string      $country
497 9
     * @param string      $city
498 9
     * @param string|null $postcode
499 9
     * @param string|null $street
500 9
     * @param int|null    $maxResults
501 9
     * @param float|null  $radius
502 9
     * @param string|null $type
503
     *
504
     * @return array<array<string,mixed>>
505 9
     *
506
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
507 6
     */
508
    public function getBranchesForLocation(
509 6
        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
            'country'     => $country,
520
            'city'        => $city,
521 8
            'zip'         => $postcode,
522
            'street'      => $street,
523 8
            'max_results' => $maxResults,
524
            'radius'      => $radius,
525 5
            'type'        => $type,
526 5
        ];
527 5
528 5
        $response = $this->requester->call(API::V1, $shipper, Request::BRANCH_LOCATOR, array_filter($data));
529
530
        $formattedResponse = $response['branches'] ?? [];
531 5
532
        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 8
     */
544
    public function getCodCountries(string $shipper): array
545 8
    {
546
        $response = $this->requester->call(API::V1, $shipper, Request::CASH_ON_DELIVERY_COUNTRIES);
547 5
548 5
        $formattedResponse = $this->normalizeResponseItems(
549 5
            $response['service_types'] ?? [],
550 5
            'service_type',
551
            'cod_countries'
552
        );
553 5
554
        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
    public function getCountries(string $shipper): array
567 12
    {
568
        $response = $this->requester->call(API::V1, $shipper, Request::COUNTRIES);
569 12
570
        $formattedResponse = $this->normalizeResponseItems(
571 9
            $response['service_types'] ?? [],
572
            'service_type',
573 9
            'countries'
574
        );
575 9
576 5
        return $formattedResponse;
577 5
    }
578 5
579 5
    /**
580 5
     * Returns available branches for the given shipper and its service
581 5
     *
582
     * @param string      $shipper
583
     * @param string      $service
584
     * @param string|null $country
585 9
     *
586
     * @return array<array<string,mixed>>
587
     *
588
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
589
     */
590
    public function getPostCodes(string $shipper, string $service, string $country = null): array
591
    {
592
        $response = $this->requester->call(API::V1, $shipper, Request::ZIP_CODES . '/' . $service . '/' . $country);
593
594
        $country = $response['country'] ?? $country;
595
596
        $formattedResponse = [];
597
598 5
        foreach ($response['zip_codes'] ?? [] as $responseItem) {
599
            $formattedResponse[] = [
600 5
                'postcode'     => $responseItem['zip'] ?? ($responseItem['zip_start'] ?? null),
601 2
                'postcode_end' => $responseItem['zip_end'] ?? null,
602
                'city'         => $responseItem['city'] ?? null,
603
                'country'      => $responseItem['country'] ?? $country,
604
                '1B'           => (bool) ($responseItem['1B'] ?? false),
605
            ];
606
        }
607
608
        return $formattedResponse;
609
    }
610
611
    /**
612
     * Check package(s) data
613 9
     *
614
     * @param string               $shipper
615 9
     * @param array<array<string>> $packages
616
     *
617 6
     * @return void
618 6
     *
619 6
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
620 6
     */
621
    public function checkPackages(string $shipper, array $packages): void
622
    {
623 6
        $this->requester->call(API::V1, $shipper, Request::CHECK, $packages);
624
    }
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 6
     */
636
    public function getAdrUnits(string $shipper, bool $fullData = false): array
637 6
    {
638
        $response = $this->requester->call(API::V1, $shipper, Request::ADR_UNITS);
639 4
640
        $formattedResponse = $this->normalizeResponseItems(
641 4
            $response['units'] ?? [],
642
            'code',
643
            $fullData === false ? 'name' : null
644
        );
645
646
        return $formattedResponse;
647
    }
648
649
    /**
650
     * Returns available activated services for the given shipper
651
     *
652
     * @param string $shipper
653
     *
654 11
     * @return array<string,mixed>
655
     *
656 11
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
657
     */
658 8
    public function getActivatedServices(string $shipper): array
659
    {
660 8
        $response = $this->requester->call(API::V1, $shipper, Request::ACTIVATED_SERVICES);
661
662 7
        unset($response['status']);
663
664 5
        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 7
    public function orderB2AShipment(string $shipper, array $packages): array
678
    {
679 7
        $response = $this->requester->call(API::V1, $shipper, Request::B2A, $packages);
680
681 3
        unset($response['status']);
682
683
        $this->validateIndexes($response, $packages);
684
685
        $this->validateResponseItemHasAttribute($response, 'package_id', $response);
686
687
        return $response;
688
    }
689
690
    /**
691
     * Get PDF link with signed consignment delivery document by the recipient
692
     *
693
     * @param string $shipper
694 19
     * @param string $carrierId
695
     *
696 19
     * @return string
697
     *
698 19
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
699
     */
700 15
    public function getProofOfDelivery(string $shipper, string $carrierId): string
701
    {
702 15
        $response = $this->getProofOfDeliveries($shipper, [$carrierId]);
703
704 11
        return $response[0];
705
    }
706 11
707 11
    /**
708
     * Get array of PDF links with signed consignment delivery document by the recipient
709 11
     *
710
     * @param string        $shipper
711
     * @param array<string> $carrierIds
712 10
     *
713
     * @return array<string>
714
     *
715
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
716
     */
717
    public function getProofOfDeliveries(string $shipper, array $carrierIds): array
718
    {
719
        $data = $this->encapsulateIds($carrierIds);
720
721
        $response = $this->requester->call(API::V1, $shipper, Request::PROOF_OF_DELIVERY, $data, false);
722
723
        unset($response['status']);
724
725 10
        $this->validateIndexes($response, $carrierIds);
726
727 10
        $formattedLinks = [];
728
729 7
        foreach ($response as $responseItem) {
730
            $this->validateStatus($responseItem, $response);
731 7
732
            $formattedLinks[] = $responseItem['file_url'];
733 6
        }
734
735 4
        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 8
     *
746
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
747 8
     */
748
    public function getTransportCosts(string $shipper, array $packages): array
749 5
    {
750
        $response = $this->requester->call(API::V1, $shipper, Request::TRANSPORT_COSTS, $packages);
751 5
752
        unset($response['status']);
753
754
        $this->validateIndexes($response, $packages);
755
756
        $this->validateResponseItemHasAttribute($response, 'eid', $response);
757
758
        return $response;
759
    }
760
761
    /**
762
     * Ģet information on individual countries of the world
763
     *
764 23
     * @return array<array<string,mixed>>
765
     *
766 23
     * @throws \Inspirum\Balikobot\Contracts\ExceptionInterface
767 3
     */
768
    public function getCountriesData(): array
769 22
    {
770
        $response = $this->requester->call(API::V1, '', Request::GET_COUNTRIES_DATA);
771
772
        $formattedResponse = $this->normalizeResponseItems($response['countries'] ?? [], 'iso_code', null);
773
774
        return $formattedResponse;
775
    }
776
777
    /**
778
     * Validate response item status
779
     *
780
     * @param array<mixed,mixed> $responseItem
781
     * @param array<mixed,mixed> $response
782 41
     *
783
     * @return void
784 41
     *
785 41
     * @throws \Inspirum\Balikobot\Exceptions\BadRequestException
786 8
     */
787
    private function validateStatus(array $responseItem, array $response): void
788
    {
789 34
        if (isset($responseItem['status']) && ((int) $responseItem['status']) !== 200) {
790
            throw new BadRequestException($response);
791
        }
792
    }
793
794
    /**
795
     * Validate that every response item has given attribute
796
     *
797
     * @param array<int,array<string,mixed>> $items
798
     * @param string                         $attribute
799
     * @param array<mixed,mixed>             $response
800
     *
801 78
     * @return void
802
     *
803 78
     * @throws \Inspirum\Balikobot\Exceptions\BadRequestException
804 14
     */
805
    private function validateResponseItemHasAttribute(array $items, string $attribute, array $response): void
806 64
    {
807
        foreach ($items as $item) {
808
            if (isset($item[$attribute]) === false) {
809
                throw new BadRequestException($response);
810
            }
811
        }
812
    }
813
814
    /**
815
     * Validate indexes
816
     *
817 33
     * @param array<mixed,mixed> $response
818
     * @param array<mixed,mixed> $request
819 33
     *
820
     * @return void
821 33
     *
822 15
     * @throws \Inspirum\Balikobot\Exceptions\BadRequestException
823
     */
824
    private function validateIndexes(array $response, array $request): void
825 33
    {
826
        if (array_keys($response) !== range(0, count($request) - 1)) {
827
            throw new BadRequestException($response);
828
        }
829
    }
830
831
    /**
832
     * Normalize response items
833
     *
834
     * @param array<array<string,string>> $items
835 66
     * @param string                      $keyName
836
     * @param string|null                 $valueName
837 66
     *
838
     * @return array<string,mixed>
839 65
     */
840
    private function normalizeResponseItems(array $items, string $keyName, ?string $valueName): array
841 66
    {
842
        $formattedResponse = [];
843
844
        foreach ($items as $item) {
845
            $formattedResponse[$item[$keyName]] = $valueName !== null ? $item[$valueName] : $item;
846
        }
847
848
        return $formattedResponse;
849
    }
850
851
    /**
852
     * Encapsulate ids
853
     *
854
     * @param array<int|string> $ids
855
     *
856
     * @return array<array<int|string>>
857
     */
858
    private function encapsulateIds(array $ids): array
859
    {
860
        return array_map(function ($carrierId) {
861
            return [
862
                'id' => $carrierId,
863
            ];
864
        }, $ids);
865
    }
866
}
867