refactor: extract Brent price sources into dedicated classes

OilPriceService no longer inlines per-provider fetch/transform/error logic.
EIA and FRED are now their own classes with a common shape; the service
just iterates and upserts the first successful result.
This commit is contained in:
Ovidiu U
2026-04-14 16:29:52 +01:00
parent a7ee9f4557
commit 1a0381265e
4 changed files with 139 additions and 114 deletions

View File

@@ -0,0 +1,58 @@
<?php
namespace App\Services\BrentPriceSources;
use App\Services\ApiLogger;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Throwable;
final class FredBrentPriceSource
{
private const string URL = 'https://api.stlouisfed.org/fred/series/observations';
public function __construct(private readonly ApiLogger $apiLogger) {}
/**
* @return array{date: string, price_usd: float}[]|null
*/
public function fetch(): ?array
{
try {
$response = $this->apiLogger->send('fred', 'GET', self::URL, fn () => Http::timeout(10)
->get(self::URL, [
'series_id' => 'DCOILBRENTEU',
'api_key' => config('services.fred.api_key'),
'sort_order' => 'desc',
'limit' => 30,
'file_type' => 'json',
]));
if (! $response->successful()) {
Log::error('FredBrentPriceSource: request failed', ['status' => $response->status()]);
return null;
}
$rows = collect($response->json('observations') ?? [])
->filter(fn (array $obs) => $obs['value'] !== '.')
->map(fn (array $obs) => [
'date' => $obs['date'],
'price_usd' => (float) $obs['value'],
])
->all();
if ($rows === []) {
Log::warning('FredBrentPriceSource: no valid observations returned');
return null;
}
return $rows;
} catch (Throwable $e) {
Log::error('FredBrentPriceSource: fetch failed', ['error' => $e->getMessage()]);
return null;
}
}
}