| Code Coverage | ||||||||||
| Classes and Traits | Functions and Methods | Lines | ||||||||
| Total |  | 0.00% | 0 / 1 |  | 50.00% | 2 / 4 | CRAP |  | 83.87% | 26 / 31 | 
| SampleDataSource |  | 0.00% | 0 / 1 |  | 50.00% | 2 / 4 | 10.42 |  | 83.87% | 26 / 31 | 
| __construct |  | 100.00% | 1 / 1 | 3 |  | 100.00% | 13 / 13 | |||
| getQuotes |  | 100.00% | 1 / 1 | 1 |  | 100.00% | 1 / 1 | |||
| transformResponseInQuotesCollection |  | 0.00% | 0 / 1 | 3.21 |  | 71.43% | 5 / 7 | |||
| fetchQuotesFromSource |  | 0.00% | 0 / 1 | 3.24 |  | 70.00% | 7 / 10 | |||
| <?php | |
| declare(strict_types=1); | |
| namespace App\Quote\DataSource; | |
| use App\Quote\Collection\QuoteCollection; | |
| use App\Quote\Collection\QuoteCollectionInterface; | |
| use App\Quote\DataSource\Exception\DataSourceException; | |
| use App\Quote\DataSource\Exception\DataSourceIsNotReadableException; | |
| use Psr\Log\LoggerInterface; | |
| use Symfony\Component\Cache\Adapter\AdapterInterface; | |
| use Symfony\Component\HttpClient\Exception\ClientException; | |
| use Symfony\Component\HttpFoundation\Request; | |
| use Symfony\Component\HttpFoundation\Response; | |
| use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; | |
| use Symfony\Contracts\HttpClient\HttpClientInterface; | |
| use Symfony\Contracts\HttpClient\ResponseInterface; | |
| use Webmozart\Assert\Assert; | |
| final class SampleDataSource implements DataSourceInterface | |
| { | |
| private const KEY = 'SampleDataSource'; | |
| private HttpClientInterface $httpClient; | |
| private QuoteCollectionInterface $quotes; | |
| private AdapterInterface $cache; | |
| private LoggerInterface $logger; | |
| private string $url; | |
| public function __construct( | |
| HttpClientInterface $httpClient, | |
| AdapterInterface $cache, | |
| LoggerInterface $logger, | |
| string $dataSourceUrl | |
| ) { | |
| $this->httpClient = $httpClient; | |
| $this->cache = $cache; | |
| $this->url = $dataSourceUrl; | |
| $this->logger = $logger; | |
| $cachedItem = $this->cache->getItem(self::KEY); | |
| if ($cachedItem->get() instanceof QuoteCollectionInterface) { | |
| $this->quotes = $cachedItem->get(); | |
| $this->logger->info('Data fetched from cache', ['DataSource' => self::KEY]); | |
| } | |
| if (false === $cachedItem->isHit()) { | |
| $this->fetchQuotesFromSource(); | |
| $cachedItem->set($this->quotes); | |
| $this->cache->save($cachedItem); | |
| } | |
| } | |
| public function getQuotes(): QuoteCollectionInterface | |
| { | |
| return $this->quotes; | |
| } | |
| private function transformResponseInQuotesCollection(ResponseInterface $rawDataResponse): array | |
| { | |
| if ($rawDataResponse->getStatusCode() !== Response::HTTP_OK) { | |
| return []; | |
| } | |
| $data = json_decode($rawDataResponse->getContent(), true, 512, JSON_THROW_ON_ERROR); | |
| if (empty($data)) { | |
| return []; | |
| } | |
| Assert::keyExists($data, 'quotes'); | |
| return $data['quotes']; | |
| } | |
| private function fetchQuotesFromSource(): void | |
| { | |
| try { | |
| $rawDataResponse = $this->httpClient->request(Request::METHOD_GET, $this->url); | |
| } catch (TransportExceptionInterface | ClientException $e) { | |
| throw new DataSourceException( | |
| sprintf('Could not fetch quotes from %s data source', __CLASS__) | |
| ); | |
| } | |
| if ($rawDataResponse->getStatusCode() === Response::HTTP_NOT_FOUND) { | |
| throw new DataSourceIsNotReadableException( | |
| sprintf('The url %s provided is not readable or reachable', $this->url) | |
| ); | |
| } | |
| $this->logger->info('Raw data fetched from text collection quotes api', ['DataSource' => self::KEY]); | |
| $this->quotes = QuoteCollection::fromArray($this->transformResponseInQuotesCollection($rawDataResponse)); | |
| } | |
| } |