Passed
Pull Request — master (#99)
by Dante
16:17
created

BaseClient::defaultConfigName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 3
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 6
rs 10
1
<?php
2
declare(strict_types=1);
3
4
/**
5
 * BEdita, API-first content management framework
6
 * Copyright 2025 ChannelWeb Srl, Chialab Srl
7
 *
8
 * This file is part of BEdita: you can redistribute it and/or modify
9
 * it under the terms of the GNU Lesser General Public License as published
10
 * by the Free Software Foundation, either version 3 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * See LICENSE.LGPL or <http://gnu.org/licenses/lgpl-3.0.html> for more details.
14
 */
15
16
namespace BEdita\WebTools\Http;
17
18
use Cake\Core\App;
19
use Cake\Core\Configure;
20
use Cake\Core\InstanceConfigTrait;
21
use Cake\Http\Client;
22
use Cake\Http\Client\Response;
23
use Cake\Log\LogTrait;
24
use Cake\Validation\Validator;
25
26
/**
27
 * Base class for clients.
28
 */
29
abstract class BaseClient
30
{
31
    use InstanceConfigTrait;
32
    use LogTrait;
33
34
    /**
35
     * Default configuration.
36
     *
37
     * @var array
38
     */
39
    protected $_defaultConfig = [
40
        'auth' => null,
41
        'logLevel' => 'error',
42
        'url' => null,
43
    ];
44
45
    /**
46
     * The HTTP client.
47
     *
48
     * @var \Cake\Http\Client
49
     */
50
    protected $client;
51
52
    /**
53
     * Constructor. Initialize HTTP client.
54
     *
55
     * @param array $config The configuration
56
     */
57
    public function __construct(array $config = [])
58
    {
59
        $config += (array)Configure::read($this->defaultConfigName(), []);
60
        $this->setConfig($config);
61
        $this->validateConf($this->getValidator());
62
        $this->createClient();
63
    }
64
65
    /**
66
     * Get default config name.
67
     * It's the name of the client class without `Client` suffix.
68
     *
69
     * @return string
70
     */
71
    protected function defaultConfigName(): string
72
    {
73
        $shortName = App::shortName(static::class, 'Http', 'Client');
74
        [$plugin, $name] = pluginSplit($shortName);
75
76
        return $name;
77
    }
78
79
    /**
80
     * Return the Validator object
81
     *
82
     * @return \Cake\Validation\Validator
83
     */
84
    protected function getValidator(): Validator
85
    {
86
        $validator = new Validator();
87
88
        return $validator
89
            ->requirePresence('url')
90
            ->notEmptyString('url');
91
    }
92
93
    /**
94
     * Validate configuration data.
95
     *
96
     * @param \Cake\Validation\Validator $validator The validator object
97
     * @return void
98
     */
99
    protected function validateConf(Validator $validator): void
100
    {
101
        $errors = $validator->validate($this->getConfig());
102
        if (!empty($errors)) {
103
            throw new \InvalidArgumentException(sprintf('%s client config not valid: %s', static::class, json_encode($errors)));
104
        }
105
    }
106
107
    /**
108
     * Create JSON HTTP client.
109
     *
110
     * @return void
111
     */
112
    protected function createClient(): void
113
    {
114
        $parsedUrl = parse_url($this->getConfig('url'));
115
        $options = [
116
            'host' => $parsedUrl['host'],
117
            'scheme' => $parsedUrl['scheme'],
118
            'path' => $parsedUrl['path'] ?? '',
119
            'headers' => [
120
                'Accept' => 'application/json',
121
                'Content-Type' => 'application/json',
122
            ],
123
        ] + $this->getConfig();
124
        $this->client = new Client($options);
125
    }
126
127
    /**
128
     * Get the correct relative url.
129
     *
130
     * @param string $url The relative url
131
     * @return string
132
     */
133
    protected function getUrl($url): string
134
    {
135
        if (strpos($url, 'https://') === 0) {
136
            return $url;
137
        }
138
        $base = trim($this->client->getConfig('path'), '/');
139
        $url = trim($url, '/');
140
141
        return sprintf('%s/%s', $base, $url);
142
    }
143
144
    /**
145
     * Log API call.
146
     *
147
     * @param \Cake\Http\Client\Response $response The API response
148
     * @param string $payload The json payload
149
     * @return void
150
     */
151
    protected function logCall(Response $response, string $payload = ''): void
152
    {
153
        $level = $this->getConfig('logLevel') ?? 'error';
154
        if (!in_array($level, ['error', 'debug', 'verbose'])) {
155
            return;
156
        }
157
        $result = $response->isOk() ? '' : 'error';
158
        if ($level === 'error' && empty($result)) {
159
            return;
160
        }
161
        $level = $result === 'error' ? 'error' : $level;
162
163
        $this->log(
164
            sprintf(
165
                '%s API %s with status %s: %s - Payload: %s',
166
                $result,
167
                $this->defaultConfigName(),
168
                $response->getStatusCode(),
169
                (string)$response->getBody(),
170
                $payload
171
            ),
172
            $level
173
        );
174
    }
175
176
    /**
177
     * Get http client
178
     *
179
     * @return \Cake\Http\Client The client
180
     */
181
    public function getHttpClient(): Client
182
    {
183
        return $this->client;
184
    }
185
186
    /**
187
     * Get request.
188
     *
189
     * @param string $url The request url
190
     * @param array $data The query data
191
     * @param array $options Request options
192
     * @return \Cake\Http\Client\Response
193
     */
194
    public function get(string $url, array $data = [], array $options = []): Response
195
    {
196
        $response = $this->client->get($this->getUrl($url), $data, $options);
197
        $this->logCall($response, json_encode($data));
198
199
        return $response;
200
    }
201
202
    /**
203
     * Post request.
204
     *
205
     * @param string $url The request url
206
     * @param array $data The post data
207
     * @param array $options Request options
208
     * @return \Cake\Http\Client\Response
209
     */
210
    public function post(string $url, array $data = [], array $options = []): Response
211
    {
212
        $data = json_encode($data);
213
        $response = $this->client->post($this->getUrl($url), $data, $options);
214
        $this->logCall($response, $data);
215
216
        return $response;
217
    }
218
219
    /**
220
     * Patch request.
221
     *
222
     * @param string $url The request url
223
     * @param array $data The post data
224
     * @param array $options Request options
225
     * @return \Cake\Http\Client\Response
226
     */
227
    public function patch(string $url, array $data = [], array $options = []): Response
228
    {
229
        $data = json_encode($data);
230
        $response = $this->client->patch($this->getUrl($url), $data, $options);
231
        $this->logCall($response, $data);
232
233
        return $response;
234
    }
235
236
    /**
237
     * Put request.
238
     *
239
     * @param string $url The request url
240
     * @param array $data The post data
241
     * @param array $options Request options
242
     * @return \Cake\Http\Client\Response
243
     */
244
    public function put(string $url, array $data = [], array $options = []): Response
245
    {
246
        $data = json_encode($data);
247
        $response = $this->client->put($this->getUrl($url), $data, $options);
248
        $this->logCall($response, $data);
249
250
        return $response;
251
    }
252
253
    /**
254
     * Delete request.
255
     *
256
     * @param string $url The request url
257
     * @param array $data The post data
258
     * @param array $options Request options
259
     * @return \Cake\Http\Client\Response
260
     */
261
    public function delete(string $url, array $data = [], array $options = []): Response
262
    {
263
        $data = json_encode($data);
264
        $response = $this->client->delete($this->getUrl($url), $data, $options);
265
        $this->logCall($response, $data);
266
267
        return $response;
268
    }
269
}
270