Passed
Push — master ( 06ea26...ab867b )
by Stephen
27:20 queued 12:19
created

SDK::withAuthHeaders()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
c 1
b 0
f 0
nc 1
nop 2
dl 0
loc 5
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Suitcase\Builder;
6
7
use GuzzleHttp\Client;
8
use GuzzleHttp\Psr7\Response;
9
use Suitcase\Builder\ParameterBag;
10
use GuzzleHttp\Exception\GuzzleException;
11
use Tightenco\Collect\Support\Collection;
12
use Suitcase\Builder\Exceptions\MethodNotAllowed;
13
use Suitcase\Builder\Exceptions\ResourceException;
14
use Suitcase\Builder\Exceptions\UnsupportedScheme;
15
use Suitcase\Builder\Exceptions\ResourceNotRegistered;
16
17
final class SDK
18
{
19
    protected array $resource;
20
21
    protected string $scheme;
22
23
    protected string $host;
24
25
    protected string $path;
26
27
    protected string $appends = '';
28
29
    protected array $url;
30
31
    protected string $authHeader = '';
32
33
    protected Client $client;
34
35
    protected ParameterBag $query;
36
37
    protected Collection $resources;
38
39
    protected array $allowedSchemes = [
40
        'http',
41
        'https'
42
    ];
43
44
    private function __construct(string $url)
45
    {
46
        $parts = array_merge(parse_url($url));
47
48
        $this->scheme = isset($parts['scheme']) ? $this->allowedScheme($parts['scheme']) : '';
49
        $this->host = $parts['host'] ?? '';
50
        $this->path = $parts['path'] ?? '/';
51
        $this->client = new Client();
52
        $this->query = new ParameterBag();
53
        $this->resources = new Collection();
54
    }
55
56
    public static function make(string $url): self
57
    {
58
        return new self($url);
59
    }
60
61
    public function add(string $name, array $options = []): void
62
    {
63
        $resource = [
64
            'name' => $name,
65
            'endpoint' => array_key_exists('endpoint', $options) ? $options['endpoint'] : $name,
66
            'allows' => array_key_exists('allows', $options) ? $options['allows'] : [
67
                'get', 'find', 'create', 'update', 'delete'
68
            ],
69
        ];
70
71
        $this->resources->push($resource);
72
    }
73
74
    public function getScheme(): string
75
    {
76
        return $this->scheme;
77
    }
78
79
    public function getHost(): string
80
    {
81
        return $this->host;
82
    }
83
84
    public function getPath(): string
85
    {
86
        return $this->path;
87
    }
88
89
    public function getResources(): Collection
90
    {
91
        return $this->resources;
92
    }
93
94
    public function getQuery(): ParameterBag
95
    {
96
        return $this->query;
97
    }
98
99
    public function getClient(): Client
100
    {
101
        return $this->client;
102
    }
103
104
    public function use(string $resource): self
105
    {
106
        if (! $this->resources->pluck('name')->contains($resource)) {
107
            throw new ResourceNotRegistered("No resource registered for: {$resource}");
108
        }
109
110
        $this->resource = $this->resources->where('name', $resource)->first();
111
112
        return $this;
113
    }
114
115
    public function getResource():? array
116
    {
117
        return $this->resource;
118
    }
119
120
    public function get(string $method = 'GET')
121
    {
122
        if (! $this->checkAbility('get')) {
123
            throw new MethodNotAllowed("Method get is not available on this resource");
124
        }
125
126
        return $this->call($method, $this->resource['endpoint']);
127
    }
128
129
    public function find($identifier, string $method = 'GET')
130
    {
131
        if (! $this->checkAbility('find')) {
132
            throw new MethodNotAllowed("Method find is not available on this resource");
133
        }
134
135
        return $this->call($method, $this->resource['endpoint'] . '/' . (string) $identifier);
136
    }
137
138
    public function create(array $data)
139
    {
140
        if (! $this->checkAbility('create')) {
141
            throw new MethodNotAllowed("Method create is not available on this resource");
142
        }
143
144
        return $this->call('POST', $this->resource['endpoint'], $data);
145
    }
146
147
    public function update($identifier, array $data, string $method = 'PUT')
148
    {
149
        if (! $this->checkAbility('update')) {
150
            throw new MethodNotAllowed("Method update is not available on this resource");
151
        }
152
153
        return $this->call($method, $this->resource['endpoint'] . '/' . (string) $identifier, $data);
154
    }
155
156
    public function delete($identifier, string $method = 'DELETE')
157
    {
158
        if (! $this->checkAbility('delete')) {
159
            throw new MethodNotAllowed("Method delete is not available on this resource");
160
        }
161
162
        return $this->call($method, $this->resource['endpoint'] . '/' . (string) $identifier);
163
    }
164
165
    public function append(...$appends): self
166
    {
167
        $this->appends = implode('/', $appends);
168
169
        return $this;
170
    }
171
172
    public function withAuthHeaders(string $credentials, string $type = 'Bearer'): self
173
    {
174
        $this->authHeader = "{$type} {$credentials}";
175
176
        return $this;
177
    }
178
179
    /**
180
     * @codeCoverageIgnore
181
     */
182
    protected function checkAbility(string $method)
183
    {
184
        return collect($this->resource['allows'])->contains($method);
185
    }
186
187
    /**
188
     * @codeCoverageIgnore
189
     */
190
    protected function call(string $method, string $endpoint, array $data = []): \Psr\Http\Message\ResponseInterface
191
    {
192
        $uri = $this->buildUri($endpoint);
193
194
        if ($this->authHeader !== '') {
195
            $data['headers']['Authorization'] = "{$this->authHeader}";
196
        }
197
198
        try {
199
            $response = $this->client->request(
200
                $method,
201
                $uri,
202
                $data
203
            );
204
        } catch (GuzzleException $e) {
205
            throw new ResourceException($e->getMessage());
206
        }
207
208
        return $response;
209
    }
210
211
    /**
212
     * @codeCoverageIgnore
213
     */
214
    protected function buildUri(string $endpoint): string
215
    {
216
        $uri = '';
217
218
        if ($this->getScheme() !== '') {
219
            $uri .= $this->getScheme() . '://';
220
        }
221
222
        if ($this->getHost() !== '') {
223
            $uri .= $this->getHost();
224
        }
225
226
        $uri .= rtrim($this->getPath(), '/');
227
228
        $uri .= '/' . rtrim($endpoint, '/');
229
230
        $uri .= '/' . rtrim($this->appends, '/');
231
232
        return $uri;
233
    }
234
235
    /**
236
     * @codeCoverageIgnore
237
     */
238
    protected function allowedScheme(string $scheme): string
239
    {
240
        $scheme = strtolower($scheme);
241
242
        if (! in_array($scheme, $this->allowedSchemes)) {
243
            throw new UnsupportedScheme(
244
                "The scheme `{$scheme}` isn't valid. It should be either `http` or `https`."
245
            );
246
        }
247
248
        return $scheme;
249
    }
250
}
251