Passed
Pull Request — master (#111)
by Adam
03:34 queued 01:29
created

Client::getLinkedResource()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 43
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 30
nc 2
nop 1
dl 0
loc 43
rs 9.44
c 0
b 0
f 0
1
<?php
2
3
namespace AcquiaCloudApi\Connector;
4
5
use Psr\Http\Message\ResponseInterface;
6
use GuzzleHttp\Exception\BadResponseException;
7
use AcquiaCloudApi\Exception\ApiErrorException;
8
use Psr\Http\Message\StreamInterface;
9
use AcquiaCloudApi\Exception\LinkedResourceNotImplementedException;
10
11
/**
12
 * Class Client
13
 *
14
 * @package AcquiaCloudApi\CloudApi
15
 */
16
class Client implements ClientInterface
17
{
18
    /**
19
     * @var ConnectorInterface The API connector.
20
     */
21
    protected $connector;
22
23
    /**
24
     * @var array<string, mixed> Query strings to be applied to the request.
25
     */
26
    protected $query = [];
27
28
    /**
29
     * @var array<string, mixed> Guzzle options to be applied to the request.
30
     */
31
    protected $options = [];
32
33
    /**
34
     * @var array<string, mixed> Request options from each individual API call.
35
     */
36
    private $requestOptions = [];
37
38
    /**
39
     * Client constructor.
40
     *
41
     * @param ConnectorInterface $connector
42
     */
43
    final public function __construct(ConnectorInterface $connector)
44
    {
45
        $this->connector = $connector;
46
    }
47
48
    /**
49
     * Client factory method for instantiating.
50
     *
51
     * @param ConnectorInterface $connector
52
     *
53
     * @return static
54
     */
55
    public static function factory(ConnectorInterface $connector)
56
    {
57
        $client = new static(
58
            $connector
59
        );
60
61
        return $client;
62
    }
63
64
    /**
65
     * @inheritdoc
66
     */
67
    public function getVersion(): string
68
    {
69
        return self::VERSION;
70
    }
71
72
    /**
73
     * @inheritdoc
74
     */
75
    public function modifyOptions(): array
76
    {
77
        // Combine options set globally e.g. headers with options set by individual API calls e.g. form_params.
78
        $options = $this->options + $this->requestOptions;
79
80
        // This library can be standalone or as a dependency. Dependent libraries may also set their own user agent
81
        // which will make $options['headers']['User-Agent'] an array.
82
        // We need to array_unique() the array of User-Agent headers as multiple calls may include multiple of the same header.
83
        // We also use array_unshift() to place this library's user agent first to order to have it appear at the beginning of log files.
84
        // As Guzzle joins arrays with a comma, we must implode with a space here to pass Guzzle a string.
85
        $userAgent = sprintf(
86
            "%s/%s (https://github.com/typhonius/acquia-php-sdk-v2)",
87
            'acquia-php-sdk-v2',
88
            $this->getVersion()
89
        );
90
        if (isset($options['headers']['User-Agent']) && is_array($options['headers']['User-Agent'])) {
91
            array_unshift($options['headers']['User-Agent'], $userAgent);
92
            $options['headers']['User-Agent'] = implode(' ', array_unique($options['headers']['User-Agent']));
93
        } else {
94
            $options['headers']['User-Agent'] = $userAgent;
95
        }
96
97
        $options['query'] = $this->query;
98
        if (!empty($options['query']['filter']) && is_array($options['query']['filter'])) {
99
            // Default to an OR filter to increase returned responses.
100
            $options['query']['filter'] = implode(',', $options['query']['filter']);
101
        }
102
103
        return $options;
104
    }
105
106
    /**
107
     * @inheritdoc
108
     */
109
    public function request(string $verb, string $path, array $options = [])
110
    {
111
        // Put options sent with API calls into a property so they can be accessed
112
        // and therefore tested in tests.
113
        $this->requestOptions = $options;
114
115
        // Modify the options to combine options set as part of the API call as well
116
        // as those set my tools extending this library.
117
        $modifiedOptions = $this->modifyOptions();
118
119
        $response = $this->makeRequest($verb, $path, $modifiedOptions);
120
121
        return $this->processResponse($response);
122
    }
123
124
    /**
125
     * @inheritdoc
126
     */
127
    public function makeRequest(string $verb, string $path, array $options = []): ResponseInterface
128
    {
129
        try {
130
            $response = $this->connector->sendRequest($verb, $path, $options);
131
        } catch (BadResponseException $e) {
132
            $response = $e->getResponse();
133
        }
134
135
        return $response;
136
    }
137
138
    /**
139
     * @inheritdoc
140
     */
141
    public function processResponse(ResponseInterface $response)
142
    {
143
144
        $body_json = $response->getBody();
145
        $body = json_decode($body_json);
146
        if (json_last_error() !== JSON_ERROR_NONE) {
147
            return $body_json;
148
        }
149
150
        if (property_exists($body, 'error') && property_exists($body, 'message')) {
151
            throw new ApiErrorException($body);
152
        }
153
154
        return $body;
155
    }
156
157
    /**
158
     * @inheritdoc
159
     */
160
    public function getQuery(): array
161
    {
162
        return $this->query;
163
    }
164
165
    /**
166
     * @inheritdoc
167
     */
168
    public function clearQuery(): void
169
    {
170
        $this->query = [];
171
    }
172
173
    /**
174
     * @inheritdoc
175
     */
176
    public function addQuery($name, $value): void
177
    {
178
        $this->query = array_merge_recursive($this->query, [$name => $value]);
179
    }
180
181
    /**
182
     * @inheritdoc
183
     */
184
    public function getOptions(): array
185
    {
186
        return $this->options;
187
    }
188
189
    /**
190
     * @inheritdoc
191
     */
192
    public function clearOptions(): void
193
    {
194
        $this->options = [];
195
    }
196
197
    /**
198
     * @inheritdoc
199
     */
200
    public function addOption($name, $value): void
201
    {
202
        $this->options = array_merge_recursive($this->options, [$name => $value]);
203
    }
204
205
    /**
206
     * @param array{type:string, path:string, responseClass:class-string} $link
0 ignored issues
show
Documentation Bug introduced by
The doc comment array{type:string, path:...onseClass:class-string} at position 12 could not be parsed: Unknown type name 'class-string' at position 12 in array{type:string, path:string, responseClass:class-string}.
Loading history...
207
     * @return mixed
208
     * @throws LinkedResourceNotImplementedException
209
     */
210
    public function getLinkedResource($link)
211
    {
212
        // Remove the base URI from the path as this is already added by the Connector when we call request().
213
        $path = str_replace(ConnectorInterface::BASE_URI, '', $link['path']);
214
        $type = $link['type'];
215
        $responseClass = $link['responseClass'];
216
217
        $classMap = [
218
            'alerts' => '\AcquiaCloudApi\Response\InsightAlertsResponse',
219
            'applications' => '\AcquiaCloudApi\Response\ApplicationsResponse',
220
            'backups' => '\AcquiaCloudApi\Response\BackupsResponse',
221
            'code' => '\AcquiaCloudApi\Response\BranchesResponse',
222
            'crons' => '\AcquiaCloudApi\Response\CronsResponse',
223
            'databases' => '\AcquiaCloudApi\Response\DatabasesResponse',
224
            'domains' => '\AcquiaCloudApi\Response\DomainsResponse',
225
            'environments' => '\AcquiaCloudApi\Response\EnvironmentsResponse',
226
            'ides' => '\AcquiaCloudApi\Response\IdesResponse',
227
            'insight' => '\AcquiaCloudApi\Response\InsightsResponse',
228
            'logs' => '\AcquiaCloudApi\Response\LogsResponse',
229
            'members' => '\AcquiaCloudApi\Response\MembersResponse',
230
            'metrics' => '\AcquiaCloudApi\Response\MetricsResponse',
231
            'modules' => '\AcquiaCloudApi\Response\InsightModulesResponse',
232
            'notification' => '\AcquiaCloudApi\Response\NotificationResponse',
233
            'permissions' => '\AcquiaCloudApi\Response\PermissionsResponse',
234
            'self' => $responseClass,
235
            'servers' => '\AcquiaCloudApi\Response\ServersResponse',
236
            'ssl' => '\AcquiaCloudApi\Response\SslCertificatesResponse',
237
            'teams' => '\AcquiaCloudApi\Response\TeamsResponse',
238
            'variables' => '\AcquiaCloudApi\Response\VariablesResponse',
239
        ];
240
241
        // Clear any queries attached to the client to prevent sorts etc being carried through
242
        // from the original query.
243
        // @TODO this may need to be removed if users wish to filter/sort linked resources.
244
        $this->clearQuery();
245
246
        if (isset($classMap[$type])) {
247
            return new $classMap[$type](
248
                $this->request('get', $path)
249
            );
250
        }
251
252
        throw new LinkedResourceNotImplementedException($type . ' link not implemented in this SDK. Please file an issue here: https://github.com/typhonius/acquia-php-sdk-v2/issues');
253
    }
254
}
255