LazyJsonPages::from()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 1
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Cerbero\LazyJsonPages;
6
7
use Cerbero\LazyJsonPages\Data\Config;
8
use Cerbero\LazyJsonPages\Data\Dot;
9
use Cerbero\LazyJsonPages\Paginations\AnyPagination;
10
use Cerbero\LazyJsonPages\Services\ClientFactory;
11
use Cerbero\LazyJsonPages\Sources\AnySource;
12
use Closure;
13
use GuzzleHttp\RequestOptions;
14
use Illuminate\Support\LazyCollection;
15
use Psr\Http\Message\RequestInterface as Request;
16
use Psr\Http\Message\ResponseInterface as Response;
17
use Throwable;
18
19
/**
20
 * The Lazy JSON Pages entry-point
21
 */
22
final class LazyJsonPages
23
{
24
    /**
25
     * The HTTP client factory.
26
     */
27
    private readonly ClientFactory $client;
28
29
    /**
30
     * The raw configuration of the API pagination.
31
     *
32
     * @var array<Config::OPTION_*, mixed>
0 ignored issues
show
Documentation Bug introduced by
The doc comment array<Config::OPTION_*, mixed> at position 2 could not be parsed: Expected '>' at position 2, but found 'Config'.
Loading history...
33
     */
34
    private array $config = [];
35
36
    /**
37
     * Add a global middleware.
38
     */
39 2
    public static function globalMiddleware(string $name, callable $middleware): void
40
    {
41 2
        ClientFactory::globalMiddleware($name, $middleware);
42
    }
43
44
    /**
45
     * Instantiate the class statically.
46
     */
47 50
    public static function from(mixed $source): self
48
    {
49 50
        return new self($source);
50
    }
51
52
    /**
53
     * Instantiate the class.
54
     */
55 50
    public function __construct(private readonly mixed $source)
56
    {
57 50
        $this->client = new ClientFactory();
0 ignored issues
show
Bug introduced by
The property client is declared read-only in Cerbero\LazyJsonPages\LazyJsonPages.
Loading history...
58
    }
59
60
    /**
61
     * Set the name of the page.
62
     */
63 1
    public function pageName(string $name): self
64
    {
65 1
        $this->config[Config::OPTION_PAGE_NAME] = $name;
66
67 1
        return $this;
68
    }
69
70
    /**
71
     * Set the pattern to capture the page in the URI path.
72
     */
73 4
    public function pageInPath(string $pattern = '/(\d+)(?!.*\d)/'): self
74
    {
75 4
        $this->config[Config::OPTION_PAGE_IN_PATH] = $pattern;
76
77 4
        return $this;
78
    }
79
80
    /**
81
     * Set the number of the first page.
82
     */
83 10
    public function firstPage(int $page): self
84
    {
85 10
        $this->config[Config::OPTION_FIRST_PAGE] = max(0, $page);
86
87 10
        return $this;
88
    }
89
90
    /**
91
     * Set the total number of pages.
92
     */
93 33
    public function totalPages(string $key): self
94
    {
95 33
        $this->config[Config::OPTION_TOTAL_PAGES_KEY] = $key;
96
97 33
        return $this;
98
    }
99
100
    /**
101
     * Set the total number of items.
102
     */
103 4
    public function totalItems(string $key): self
104
    {
105 4
        $this->config[Config::OPTION_TOTAL_ITEMS_KEY] = $key;
106
107 4
        return $this;
108
    }
109
110
    /**
111
     * Set the number of the last page.
112
     */
113 5
    public function lastPage(string $key): self
114
    {
115 5
        $this->config[Config::OPTION_LAST_PAGE_KEY] = $key;
116
117 5
        return $this;
118
    }
119
120
    /**
121
     * Set the cursor or next page.
122
     */
123 2
    public function cursor(string $key): self
124
    {
125 2
        $this->config[Config::OPTION_CURSOR_KEY] = $key;
126
127 2
        return $this;
128
    }
129
130
    /**
131
     * Set the offset.
132
     */
133 4
    public function offset(string $key = 'offset'): self
134
    {
135 4
        $this->config[Config::OPTION_OFFSET_KEY] = $key;
136
137 4
        return $this;
138
    }
139
140
    /**
141
     * Set the Link header pagination.
142
     */
143 4
    public function linkHeader(): self
144
    {
145 4
        $this->config[Config::OPTION_HAS_LINK_HEADER] = true;
146
147 4
        return $this;
148
    }
149
150
    /**
151
     * Set the custom pagination.
152
     */
153 5
    public function pagination(string $class): self
154
    {
155 5
        $this->config[Config::OPTION_PAGINATION] = $class;
156
157 5
        return $this;
158
    }
159
160
    /**
161
     * Throttle the requests to respect rate limiting.
162
     */
163 1
    public function throttle(int $requests, int $perSeconds = 0, int $perMinutes = 0, int $perHours = 0): self
164
    {
165 1
        $seconds = $perSeconds + $perMinutes * 60 + $perHours * 3600;
166
167 1
        if ($requests > 0 && $seconds > 0) {
168 1
            $this->config[Config::OPTION_RATE_LIMITS] ??= $this->client->rateLimits;
169
170 1
            $this->client->throttle($requests, $seconds);
171
        }
172
173 1
        return $this;
174
    }
175
176
    /**
177
     * Set the maximum number of concurrent async HTTP requests.
178
     */
179 2
    public function async(int $requests): self
180
    {
181 2
        $this->config[Config::OPTION_ASYNC] = max(1, $requests);
182
183 2
        $this->client->config(RequestOptions::STREAM, $this->config[Config::OPTION_ASYNC] === 1);
184
185 2
        return $this;
186
    }
187
188
    /**
189
     * Set the server connection timeout in seconds.
190
     */
191 1
    public function connectionTimeout(float|int $seconds): self
192
    {
193 1
        $this->client->config(RequestOptions::CONNECT_TIMEOUT, max(0, $seconds));
194
195 1
        $this->client->config(RequestOptions::READ_TIMEOUT, max(0, $seconds));
196
197 1
        return $this;
198
    }
199
200
    /**
201
     * Set an HTTP request timeout in seconds.
202
     */
203 1
    public function requestTimeout(float|int $seconds): self
204
    {
205 1
        $this->client->config(RequestOptions::TIMEOUT, max(0, $seconds));
206
207 1
        return $this;
208
    }
209
210
    /**
211
     * Set the number of attempts to fetch pages.
212
     */
213 1
    public function attempts(int $times): self
214
    {
215 1
        $this->config[Config::OPTION_ATTEMPTS] = max(1, $times);
216
217 1
        return $this;
218
    }
219
220
    /**
221
     * Set the backoff strategy.
222
     */
223 1
    public function backoff(Closure $callback): self
224
    {
225 1
        $this->config[Config::OPTION_BACKOFF] = $callback;
226
227 1
        return $this;
228
    }
229
230
    /**
231
     * Add an HTTP client middleware.
232
     */
233 2
    public function middleware(string $name, callable $middleware): self
234
    {
235 2
        $this->client->middleware($name, $middleware);
236
237 2
        return $this;
238
    }
239
240
    /**
241
     * Handle the sending request.
242
     *
243
     * @param Closure(Request $request, array<string, mixed> $config): void $callback
244
     */
245 2
    public function onRequest(Closure $callback): self
246
    {
247 2
        $this->client->onRequest($callback);
248
249 2
        return $this;
250
    }
251
252
    /**
253
     * Handle the received response.
254
     *
255
     * @param Closure(Response $response, Request $request, array<string, mixed> $config): void $callback
256
     */
257 2
    public function onResponse(Closure $callback): self
258
    {
259 2
        $this->client->onResponse($callback);
260
261 2
        return $this;
262
    }
263
264
    /**
265
     * Handle a transaction error.
266
     *
267
     * @param Closure(Throwable $e, Request $request, ?Response $response, array<string, mixed> $config): void $callback
268
     */
269 1
    public function onError(Closure $callback): self
270
    {
271 1
        $this->client->onError($callback);
272
273 1
        return $this;
274
    }
275
276
    /**
277
     * Retrieve a lazy collection yielding the paginated items.
278
     *
279
     * @return LazyCollection<int, mixed>
280
     * @throws \Cerbero\LazyJsonPages\Exceptions\UnsupportedPaginationException
281
     */
282 50
    public function collect(string $dot = '*'): LazyCollection
283
    {
284 50
        $this->config[Config::OPTION_ITEMS_POINTER] = (new Dot($dot))->toPointer();
285
286 50
        return new LazyCollection(function () {
0 ignored issues
show
Bug introduced by
function(...) { /* ... */ } of type callable is incompatible with the type Illuminate\Contracts\Support\Arrayable expected by parameter $source of Illuminate\Support\LazyCollection::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

286
        return new LazyCollection(/** @scrutinizer ignore-type */ function () {
Loading history...
287 50
            $client = $this->client->make();
288 50
            $config = new Config(...$this->config); /** @phpstan-ignore-line */
0 ignored issues
show
Bug introduced by
$this->config is expanded, but the parameter $itemsPointer of Cerbero\LazyJsonPages\Data\Config::__construct() does not expect variable arguments. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

288
            $config = new Config(/** @scrutinizer ignore-type */ ...$this->config); /** @phpstan-ignore-line */
Loading history...
289 50
            $source = (new AnySource($this->source))->setClient($client);
290
291 50
            yield from new AnyPagination($source, $client, $config);
292 50
        });
293
    }
294
}
295