Passed
Push — master ( 8212b9...355016 )
by Stephen
10:44
created

SDK::buildUri()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 19
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

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