Completed
Pull Request — master (#19)
by Mischa
02:41
created

DefaultApiClient   A

Complexity

Total Complexity 14

Size/Duplication

Total Lines 140
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 3
Bugs 2 Features 1
Metric Value
wmc 14
c 3
b 2
f 1
lcom 1
cbo 8
dl 0
loc 140
rs 10

6 Methods

Rating   Name   Duplication   Size   Complexity  
B makeApiCall() 0 35 5
A createResponseObject() 0 8 2
A authenticateRequest() 0 8 1
A sendRequest() 0 8 1
A getNewToken() 0 11 2
A addDefaultHeaders() 0 10 3
1
<?php
2
3
/**
4
 * This file is part of the PHP SDK library for the Superdesk Content API.
5
 *
6
 * Copyright 2015 Sourcefabric z.u. and contributors.
7
 *
8
 * For the full copyright and license information, please see the
9
 * AUTHORS and LICENSE files distributed with this source code.
10
 *
11
 * @copyright 2015 Sourcefabric z.ú.
12
 * @license http://www.superdesk.org/license
13
 */
14
15
namespace Superdesk\ContentApiSdk\Client;
16
17
use Superdesk\ContentApiSdk\API\Request\RequestInterface;
18
use Superdesk\ContentApiSdk\API\Request\OAuthDecorator;
19
use Superdesk\ContentApiSdk\API\Response;
20
use Superdesk\ContentApiSdk\ContentApiSdk;
21
use Superdesk\ContentApiSdk\Exception\AuthenticationException;
22
use Superdesk\ContentApiSdk\Exception\AccessDeniedException;
23
use Superdesk\ContentApiSdk\Exception\ClientException;
24
use Superdesk\ContentApiSdk\Exception\ResponseException;
25
26
/**
27
 * Request client that implements all methods regarding basic request/response
28
 * handling for the Content API.
29
 */
30
class DefaultApiClient extends AbstractApiClient
31
{
32
    /**
33
     * Default request headers.
34
     *
35
     * @var array
36
     */
37
    protected $headers = array(
38
        'Accept' => 'application/json'
39
    );
40
41
    /**
42
     * {@inheritdoc}
43
     */
44
    public function makeApiCall(RequestInterface $request)
45
    {
46
        // Request tokens when none are set
47
        if ($this->authenticator->getAccessToken() === null) {
48
            $this->getNewToken($request);
49
        }
50
51
        $response = $this->sendRequest($this->authenticateRequest($request));
52
53
        if ($response['status'] === 401) {
54
55
            $this->incrementAuthenticationRetryAttempt();
56
57
            if ($this->isAuthenticationRetryLimitReached()) {
58
                throw new AccessDeniedException('Authentication retry limit reached.');
59
            }
60
61
            // Once SD-3820 is fixed, implement SWP-92 branch, it will use
62
            // the refresh token functionality, instead of request a new token
63
            // each time this method is called.
64
            $this->getNewToken($request);
65
66
            // Retry making an api call
67
            return $this->makeApiCall($request);
68
        }
69
70
        if ($response['status'] == 200) {
71
72
            $this->resetAuthenticationRetryAttempt();
73
74
            return $this->createResponseObject($response);
75
        }
76
77
        throw new ClientException(sprintf('The server returned an error with status %s.', $response['status']), $response['status']);
78
    }
79
80
    /**
81
     * Creates response object from response array.
82
     *
83
     * @param  array $response
84
     *
85
     * @return Response API Response object
86
     *
87
     * @throws ClientException Thrown when response object could not be created
88
     */
89
    private function createResponseObject(array $response)
90
    {
91
        try {
92
            return new Response($response['body'], $response['headers']);
93
        } catch (ResponseException $e) {
94
            throw new ClientException($e->getMessage(), $e->getCode(), $e);
95
        }
96
    }
97
98
    /**
99
     * Adds authentication details to request with OAuth decorator.
100
     *
101
     * @param  RequestInterface $request
102
     *
103
     * @return OAuthDecorator OAuth ready decorated Request
104
     */
105
    private function authenticateRequest(RequestInterface $request)
106
    {
107
        $authenticatedRequest = new OAuthDecorator($request);
108
        $authenticatedRequest->setAccessToken($this->authenticator->getAccessToken());
109
        $authenticatedRequest->addAuthentication();
110
111
        return $authenticatedRequest;
112
    }
113
114
    /**
115
     * Sends the actual request.
116
     *
117
     * @param  RequestInterface $request
118
     *
119
     * @return Response Response object created from raw response
120
     *
121
     * @throws ClientException Thrown when response could not be created.
122
     */
123
    private function sendRequest(RequestInterface $request)
124
    {
125
        return $this->client->makeCall(
126
            $request->getFullUrl(),
127
            $this->addDefaultHeaders($request->getHeaders()),
128
            $request->getOptions()
129
        );
130
    }
131
132
    /**
133
     * Refreshes the token via then authenticator.
134
     *
135
     * @param  RequestInterface $request
136
     *
137
     * @return void
138
     */
139
    private function getNewToken(RequestInterface $request)
140
    {
141
        try {
142
            $this->authenticator->setBaseUrl($request->getBaseUrl());
143
            $this->authenticator->getAuthenticationTokens();
144
        } catch (AuthenticationException $e) {
145
            throw new AccessDeniedException('Could not authenticate against API.', $e->getCode(), $e);
146
        }
147
148
        return;
149
    }
150
151
    /**
152
     * Adds default headers to the headers per request, only if the key
153
     * cannot not be found in the headers per request.
154
     *
155
     * @param array $headers
156
     *
157
     * @return array
158
     */
159
    private function addDefaultHeaders($headers)
160
    {
161
        foreach ($this->headers as $key => $value) {
162
            if (!isset($headers[$key])) {
163
                $headers[$key] = $value;
164
            }
165
        }
166
167
        return $headers;
168
    }
169
}
170