create wb-api parser
This commit is contained in:
commit
7a78dfc7aa
61 files changed
+11245
No files matched your search
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Wb;
|
||||
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use RuntimeException;
|
||||
|
||||
class WbApiClient
|
||||
{
|
||||
private string $baseUrl;
|
||||
private string $apiKey;
|
||||
private int $limit;
|
||||
private int $timeout;
|
||||
private int $requestSleepMs;
|
||||
private int $tooManyAttemptsSleepSeconds;
|
||||
private int $max429Retries;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->baseUrl = rtrim((string) config('wb_api.base_url'), '/');
|
||||
$this->apiKey = (string) config('wb_api.key');
|
||||
$this->limit = max(1, min((int) config('wb_api.limit', 100), 500));
|
||||
$this->timeout = (int) config('wb_api.timeout', 30);
|
||||
$this->requestSleepMs = (int) config('wb_api.request_sleep_ms', 1000);
|
||||
$this->tooManyAttemptsSleepSeconds = (int) config('wb_api.too_many_attempts_sleep_seconds', 30);
|
||||
$this->max429Retries = (int) config('wb_api.max_429_retries', 20);
|
||||
|
||||
if ($this->baseUrl === '') {
|
||||
throw new RuntimeException('WB_API_BASE_URL is empty. Check your .env file.');
|
||||
}
|
||||
|
||||
if ($this->apiKey === '') {
|
||||
throw new RuntimeException('WB_API_KEY is empty. Check your .env file.');
|
||||
}
|
||||
}
|
||||
|
||||
public function limit(): int
|
||||
{
|
||||
return $this->limit;
|
||||
}
|
||||
|
||||
public function fetchPage(string $endpoint, array $params, int $page): array
|
||||
{
|
||||
$url = $this->baseUrl . '/api/' . ltrim($endpoint, '/');
|
||||
|
||||
$query = array_filter(array_merge($params, [
|
||||
'key' => $this->apiKey,
|
||||
'limit' => $this->limit,
|
||||
'page' => $page,
|
||||
]), static fn ($value) => $value !== null && $value !== '');
|
||||
|
||||
$attempt429 = 0;
|
||||
|
||||
while (true) {
|
||||
if ($this->requestSleepMs > 0) {
|
||||
usleep($this->requestSleepMs * 1000);
|
||||
}
|
||||
|
||||
$response = Http::timeout($this->timeout)
|
||||
->acceptJson()
|
||||
->get($url, $query);
|
||||
|
||||
if ($response->status() === 429) {
|
||||
$attempt429++;
|
||||
|
||||
if ($attempt429 > $this->max429Retries) {
|
||||
throw new RuntimeException("Too many 429 responses for {$endpoint} page {$page}");
|
||||
}
|
||||
|
||||
$retryAfter = $response->header('Retry-After');
|
||||
$sleepSeconds = is_numeric($retryAfter)
|
||||
? max(1, (int) $retryAfter)
|
||||
: $this->tooManyAttemptsSleepSeconds;
|
||||
|
||||
echo "{$endpoint} page={$page} got 429, sleeping {$sleepSeconds}s, retry {$attempt429}/{$this->max429Retries}\n";
|
||||
|
||||
sleep($sleepSeconds);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$response->successful()) {
|
||||
throw new RuntimeException(sprintf(
|
||||
'API request failed: endpoint=%s page=%d http=%d body=%s',
|
||||
$endpoint,
|
||||
$page,
|
||||
$response->status(),
|
||||
mb_substr($response->body(), 0, 1000)
|
||||
));
|
||||
}
|
||||
|
||||
$json = $response->json();
|
||||
|
||||
if (!is_array($json)) {
|
||||
throw new RuntimeException("Unexpected non-array API response for {$endpoint}, page {$page}");
|
||||
}
|
||||
|
||||
$items = $json['data'] ?? $json;
|
||||
|
||||
if (!is_array($items)) {
|
||||
throw new RuntimeException("Unexpected API response shape for {$endpoint}, page {$page}");
|
||||
}
|
||||
|
||||
if ($items !== [] && array_keys($items) !== range(0, count($items) - 1)) {
|
||||
$items = [$items];
|
||||
}
|
||||
|
||||
return array_values(array_filter($items, 'is_array'));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user