Passed
Push — next ( 4b8118...208d8d )
by Bas
08:31 queued 05:26
created

ArangoClient::disconnect()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 5
ccs 1
cts 1
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace ArangoClient;
6
7
use ArangoClient\Exceptions\ArangoException;
8
use ArangoClient\Http\HttpClientConfig;
9
use ArangoClient\Http\HttpRequestOptions;
10
use ArangoClient\Statement\Statement;
11
use ArangoClient\Transactions\SupportsTransactions;
12
use GuzzleHttp\Client as GuzzleClient;
13
use GuzzleHttp\Exception\GuzzleException;
14
use GuzzleHttp\Exception\RequestException;
15
use Psr\Http\Message\ResponseInterface;
16
use Spatie\DataTransferObject\Exceptions\UnknownProperties;
17
use stdClass;
18
use Throwable;
19
use Traversable;
20
21
/**
22
 * The arangoClient handles connections to ArangoDB's HTTP REST API.
23
 *
24
 * @see https://www.arangodb.com/docs/stable/http/
25
 */
26
class ArangoClient
27
{
28
    use HandlesResponses;
29
    use HandlesJson;
30
    use HasManagers;
31
    use SupportsTransactions;
32
33
    protected GuzzleClient $httpClient;
34
35
    protected HttpClientConfig $config;
36
37
    /**
38
     * ArangoClient constructor.
39
     *
40
     * @param  array<string|numeric|null>  $config
41
     * @param  GuzzleClient|null  $httpClient
42
     *
43
     * @throws UnknownProperties
44
     */
45 117
    public function __construct(array $config = [], ?GuzzleClient $httpClient = null)
46
    {
47 117
        $this->connect($config, $httpClient);
48 117
    }
49
50 117
    /**
51
     * ArangoClient constructor.
52
     *
53
     * @param  array<string|numeric|null>  $config
54
     * @param  GuzzleClient|null  $httpClient
55
     *
56 117
     * @throws UnknownProperties
57
     */
58 117
    public function connect(array $config = [], ?GuzzleClient $httpClient = null): bool
59
    {
60
        $config['endpoint'] = $this->generateEndpoint($config);
61 117
        $this->config = new HttpClientConfig($config);
62 117
63 2
        $this->httpClient = $httpClient ?? new GuzzleClient($this->config->mapGuzzleHttpClientConfig());
64
65 117
        return true;
66 2
    }
67
68
    /**
69 117
     * We disconnect by creating a new guzzle client. The old client will remove the current connection upon destruction.
70
     *
71
     * @return bool
72
     */
73
    public function disconnect(): bool
74
    {
75
        $this->httpClient = new GuzzleClient($this->config->mapGuzzleHttpClientConfig());
76
77 117
        return true;
78
    }
79 117
80
81 117
    /**
82 117
     * @param  array<mixed>  $config
83
     */
84
    public function generateEndpoint(array $config): string
85 117
    {
86
        if (isset($config['endpoint'])) {
87 117
            return (string) $config['endpoint'];
88 2
        }
89 2
90
        $endpoint = 'http://localhost:8529';
91
        if (isset($config['host'])) {
92 117
            $endpoint = (string) $config['host'];
93 117
        }
94
95
        if (isset($config['port'])) {
96
            $endpoint .= ':' . (string) $config['port'];
97
        }
98
99
        return $endpoint;
100
    }
101
102
    /**
103
     * @param  array<mixed>|HttpRequestOptions  $options
104 117
     *
105
     * @throws ArangoException
106 117
     */
107 75
    public function request(string $method, string $uri, array|HttpRequestOptions $options = [], ?string $database = null): stdClass
108
    {
109
        $uri = $this->prependDatabaseToUri($uri, $database);
110 117
111
        if (is_array($options)) {
0 ignored issues
show
introduced by
The condition is_array($options) is always true.
Loading history...
112
            $options = $this->prepareRequestOptions($options);
113
        }
114
115
        $response = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $response is dead and can be removed.
Loading history...
116
        try {
117
            $response = $this->httpClient->request($method, $uri, $options->all());
118
        } catch (Throwable $e) {
119
            $this->handleGuzzleException($e);
120 2
        }
121
122
        if ($response !== null) {
123
            return $this->cleanupResponse($response);
124
        }
125
126 2
        return new stdClass();
127 2
    }
128
129 2
    /**
130
     * @param  array<mixed>|HttpRequestOptions  $options
131
     *
132 117
     * @throws ArangoException
133
     */
134 117
    public function rawRequest(string $method, string $uri, array|HttpRequestOptions $options = []): ResponseInterface|null
135 117
    {
136
        if (is_array($options)) {
0 ignored issues
show
introduced by
The condition is_array($options) is always true.
Loading history...
137
            $options = $this->prepareRequestOptions($options);
138 117
        }
139
140
        $response = null;
0 ignored issues
show
Unused Code introduced by
The assignment to $response is dead and can be removed.
Loading history...
141
        try {
142
            $response = $this->httpClient->request($method, $uri, $options->all());
143
        } catch (Throwable $e) {
144 2
            $this->handleGuzzleException($e);
145
        }
146 2
147 2
        return $response;
148
    }
149 2
150 2
151 2
    /**
152 2
     * @param  array<mixed>  $options
153
     *
154 2
     * @throws ArangoException
155 2
     */
156
    protected function prepareRequestOptions(array $options): HttpRequestOptions
157
    {
158 2
        if (isset($options['body'])) {
159 2
            $options['body'] = $this->jsonEncode($options['body']);
160
        }
161
162
        return new HttpRequestOptions($options);
163
    }
164 2
165 2
    /**
166 2
     * Return the response with debug information (for internal testing purposes).
167 2
     *
168
     * @param  array<mixed>  $options
169
     *
170
     * @throws GuzzleException
171
     */
172
    public function debugRequest(
173
        string $method,
174 117
        string $uri,
175
        array $options = [],
176 117
        ?string $database = null,
177 117
    ): ResponseInterface {
178 117
        $uri = $this->prependDatabaseToUri($uri, $database);
179
        $options['debug'] = true;
180 117
181
        return $this->httpClient->request($method, $uri, $options);
182
    }
183
184
    protected function prependDatabaseToUri(string $uri, ?string $database = null): string
185
    {
186
        if (!isset($database)) {
187
            $database = $this->config->database;
188 16
        }
189
190
        return '/_db/' . urlencode($database) . $uri;
191
    }
192
193 16
    /**
194
     * @throws ArangoException
195
     */
196
    protected function handleGuzzleException(Throwable $e): void
197
    {
198
        $message = $e->getMessage();
199 117
        $code = $e->getCode();
200
201 117
        if ($e instanceof RequestException && $e->hasResponse()) {
202 117
            $response = $e->getResponse();
203
            if ($response !== null) {
204 4
                $decodedResponse = $this->decodeResponse($response);
205
            }
206
            if (isset($decodedResponse->errorMessage)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $decodedResponse does not seem to be defined for all execution paths leading up to this point.
Loading history...
207 117
                $message = (string) $decodedResponse->errorMessage;
208
            }
209 117
210
            if (isset($decodedResponse->code)) {
211
                $code = (int) $decodedResponse->code;
212 8
            }
213
        }
214 8
215
        throw(
216
            new ArangoException(
217 1
                $code . ' - ' . $message,
218
                $code,
219 1
            )
220
        );
221
    }
222 1
223
    /**
224 1
     * @SuppressWarnings(PHPMD.StaticAccess)
225
     */
226
    protected function cleanupResponse(ResponseInterface $response): stdClass
227 117
    {
228
        $response = $this->decodeResponse($response);
229 117
        unset($response->error);
230
        unset($response->code);
231
232
        return $response;
233
    }
234
235
    /**
236
     * @param  array<scalar>  $bindVars
237
     * @param  array<mixed>  $options
238
     * @return Statement
239
     */
240
    public function prepare(
241
        string $query,
242
        array $bindVars = [],
243
        array $options = [],
244
    ): Traversable {
245
        return new Statement($this, $query, $bindVars, $options);
246
    }
247
248
    /**
249
     * @return mixed
250
     */
251
    public function getConfig(?string $value = null): mixed
252
    {
253
        if ($value) {
254
            return $this->config->$value;
255
        }
256
        return $this->config->toArray();
257
    }
258
259
    public function setDatabase(string $name): void
260
    {
261
        $this->config->database = $name;
262
    }
263
264
    public function getDatabase(): string
265
    {
266
        return $this->config->database;
267
    }
268
269
    public function setHttpClient(GuzzleClient $httpClient): void
270
    {
271
        $this->httpClient = $httpClient;
272
    }
273
274
    public function getHttpClient(): GuzzleClient
275
    {
276
        return $this->httpClient;
277
    }
278
279
    public function getUser(): string
280
    {
281
        return (string) $this->config->username;
282
    }
283
}
284