Completed
Push — master ( 2b3b62...a74c29 )
by Taosikai
14:49
created

DHLECommerceTracker   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 149
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 10

Importance

Changes 0
Metric Value
wmc 20
lcom 1
cbo 10
dl 0
loc 149
rs 10
c 0
b 0
f 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 2
A setAccessToken() 0 4 2
B track() 0 25 4
B getAccessToken() 0 22 5
A getCredential() 0 4 1
A sendRequest() 0 9 2
B buildShipment() 0 23 4
1
<?php
2
/**
3
 * Slince shipment tracker library
4
 * @author Tao <[email protected]>
5
 */
6
namespace Slince\ShipmentTracking\DHLECommerce;
7
8
use GuzzleHttp\Client as HttpClient;
9
use GuzzleHttp\Exception\GuzzleException;
10
use GuzzleHttp\Psr7\Request;
11
use Psr\Http\Message\RequestInterface;
12
use Slince\ShipmentTracking\DHLECommerce\Exception\InvalidAccessTokenException;
13
use Slince\ShipmentTracking\Foundation\Shipment;
14
use Slince\ShipmentTracking\Foundation\ShipmentEvent;
15
use Slince\ShipmentTracking\Foundation\HttpAwareTracker;
16
use Slince\ShipmentTracking\Foundation\Exception\TrackException;
17
18
class DHLECommerceTracker extends HttpAwareTracker
19
{
20
    /**
21
     * @var string
22
     */
23
    const ACCESS_TOKEN_ENDPOINT = 'https://api.dhlecommerce.dhl.com/rest/v1/OAuth/AccessToken';
24
25
    /**
26
     * @var string
27
     */
28
    const TRACKING_ENDPOINT = 'https://api.dhlecommerce.dhl.com/rest/v2/Tracking';
29
30
    /**
31
     * @var Credential
32
     */
33
    protected $credential;
34
35
    /**
36
     * @var AccessToken
37
     */
38
    protected $accessToken;
39
40
    public function __construct($clientId, $password, HttpClient $httpClient = null)
41
    {
42
        $httpClient && $this->setHttpClient($httpClient);
43
        $this->credential = new Credential($clientId, $password);
44
    }
45
46
    /**
47
     * Sets the access token for the tracker
48
     * @param string|AccessToken $accessToken
49
     */
50
    public function setAccessToken($accessToken)
51
    {
52
        $this->accessToken = $accessToken instanceof AccessToken ? $accessToken : new AccessToken($accessToken);
53
    }
54
55
    /**
56
     * {@inheritdoc}
57
     */
58
    public function track($trackingNumber)
59
    {
60
        $parameters = [
61
            'trackItemRequest' => [
62
                'token' => $this->getAccessToken()->getToken(),
63
                'messageLanguage' => 'en',
64
                'messageVersion' => '1.1',
65
                'trackingReferenceNumber' => [$trackingNumber]
66
            ]
67
        ];
68
        $request = new Request('POST', static::TRACKING_ENDPOINT);
69
        $json = $this->sendRequest($request, [
70
            'json' => $parameters
71
        ]);
72
        if ($json['trackItemResponse']['responseCode'] != 0 &&
73
            stripos($json['trackItemResponse']['responseText'], 'Invalid access token') !== false
74
        ) {
75
            throw new InvalidAccessTokenException(sprintf('The access token "%s" is invalid, please refresh for the new one', $this->accessToken));
76
        }
77
        if ($json['trackItemResponse']['responseCode'] != 0) {
78
            throw new TrackException(sprintf('Bad response with code "%d"', $json['trackItemResponse']['responseCode']));
79
        }
80
        return static::buildShipment($json);
81
82
    }
83
84
    /**
85
     * Gets the access token
86
     * @param boolean $refresh
87
     * @throws TrackException
88
     * @return AccessToken
89
     */
90
    public function getAccessToken($refresh = false)
91
    {
92
        if ($this->accessToken && !$refresh) {
93
            return $this->accessToken;
94
        }
95
        $request = new Request('GET', static::ACCESS_TOKEN_ENDPOINT);
96
        $json = $this->sendRequest($request, [
97
            'query' => array_merge($this->credential->toArray(), [
98
                'returnFormat' => 'json'
99
            ])
100
        ]);
101
        if (!isset($json['accessTokenResponse']['responseStatus']) || $json['accessTokenResponse']['responseStatus']['code'] != '100000') {
102
            throw new TrackException(sprintf('Get access token error with message details "%s"',
103
                $json['accessTokenResponse']['responseStatus']['messageDetails']
104
            ));
105
        }
106
        return $this->accessToken = new AccessToken(
107
            $json['accessTokenResponse']['token'],
108
            $json['accessTokenResponse']['token_type'],
109
            $json['accessTokenResponse']['expires_in_seconds']
110
        );
111
    }
112
113
    /**
114
     * @return Credential
115
     */
116
    public function getCredential()
117
    {
118
        return $this->credential;
119
    }
120
121
    /**
122
     * @param RequestInterface $request
123
     * @param array $options
124
     * @throws TrackException
125
     * @return array
126
     * @codeCoverageIgnore
127
     */
128
    protected function sendRequest(RequestInterface $request, $options = [])
129
    {
130
        try {
131
            $response = $this->getHttpClient()->send($request, $options);
132
        } catch (GuzzleException $exception) {
133
            throw new TrackException($exception->getMessage());
134
        }
135
        return \GuzzleHttp\json_decode((string)$response->getBody(), true);
136
    }
137
138
    /**
139
     * @param array $json
140
     * @throws TrackException
141
     * @return Shipment
142
     */
143
    protected static function buildShipment($json)
144
    {
145
        $json = reset($json['trackItemResponse']['items']);
146
        if (!$json) {
147
            throw new TrackException(sprintf('Bad response'));
148
        }
149
        $events = array_map(function($item) {
150
            return ShipmentEvent::fromArray([
151
                'location' => $item['address']['city'],
152
                'description' => $item['description'],
153
                'date' => $item['timestamp'],
154
                'status' => $item['status']
155
            ]);
156
        }, $json['events']);
157
        $shipment = new Shipment($events);
158
        $isDelivered = ($lastEvent = end($events)) ? $lastEvent->getStatus() == 71093 : null;
159
        $shipment->setIsDelivered($isDelivered)
0 ignored issues
show
Bug introduced by
It seems like $isDelivered defined by ($lastEvent = end($event...tatus() == 71093 : null on line 158 can also be of type null; however, Slince\ShipmentTracking\...pment::setIsDelivered() does only seem to accept boolean, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
160
            ->setDestination($json['destination']['countryCode']);
161
        if ($firstEvent = reset($events)) {
162
            $shipment->setDeliveredAt($firstEvent->getDate());
163
        }
164
        return $shipment;
165
    }
166
}