Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
],
"require": {
"php": ">=8.1",
"php-http/cache-plugin": "^1.8",
"php-http/client-common": "^2.7",
"php-http/discovery": "^1.18",
"php-http/logger-plugin": "^1.3",
"psr/cache": "^3.0",
"psr/http-client": "^1.0",
"psr/http-factory": "^1.0",
Expand Down
22 changes: 10 additions & 12 deletions src/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

namespace ProgrammatorDev\OpenWeatherMap;

use ProgrammatorDev\OpenWeatherMap\Exception\ValidationException;
use ProgrammatorDev\OpenWeatherMap\HttpClient\HttpClientBuilder;
use ProgrammatorDev\OpenWeatherMap\HttpClient\Plugin\LoggerPlugin;
use ProgrammatorDev\OpenWeatherMap\Language\Language;
use ProgrammatorDev\OpenWeatherMap\MeasurementSystem\MeasurementSystem;
use ProgrammatorDev\OpenWeatherMap\Validator\BlankValidatorTrait;
Expand All @@ -24,8 +24,6 @@ public function __construct(array $options = [])
$resolver = new OptionsResolver();
$this->configureOptions($resolver);
$this->options = $resolver->resolve($options);

$this->configureAware();
}

private function configureOptions(OptionsResolver $resolver): void
Expand Down Expand Up @@ -54,20 +52,14 @@ private function configureOptions(OptionsResolver $resolver): void
$resolver->setAllowedValues('language', Language::getList());
}

private function configureAware(): void
{
if ($this->getLogger() !== null) {
$this->getHttpClientBuilder()->addPlugin(
new LoggerPlugin($this->getLogger())
);
}
}

public function getApplicationKey(): string
{
return $this->options['applicationKey'];
}

/**
* @throws ValidationException
*/
public function setApplicationKey(string $applicationKey): self
{
$this->validateBlank('applicationKey', $applicationKey);
Expand All @@ -82,6 +74,9 @@ public function getMeasurementSystem(): string
return $this->options['measurementSystem'];
}

/**
* @throws ValidationException
*/
public function setMeasurementSystem(string $measurementSystem): self
{
$this->validateChoice('measurementSystem', $measurementSystem, MeasurementSystem::getList());
Expand All @@ -96,6 +91,9 @@ public function getLanguage(): string
return $this->options['language'];
}

/**
* @throws ValidationException
*/
public function setLanguage(string $language): self
{
$this->validateChoice('language', $language, Language::getList());
Expand Down
133 changes: 55 additions & 78 deletions src/Endpoint/AbstractEndpoint.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,22 @@

namespace ProgrammatorDev\OpenWeatherMap\Endpoint;

use Http\Client\Common\HttpMethodsClient;
use Http\Client\Common\Plugin\CachePlugin;
use Http\Client\Common\Plugin\LoggerPlugin;
use Http\Client\Exception;
use ProgrammatorDev\OpenWeatherMap\Endpoint\Util\WithCacheInvalidationTrait;
use ProgrammatorDev\OpenWeatherMap\Config;
use ProgrammatorDev\OpenWeatherMap\Endpoint\Util\WithCacheTtlTrait;
use ProgrammatorDev\OpenWeatherMap\Entity\Error;
use ProgrammatorDev\OpenWeatherMap\Exception\BadRequestException;
use ProgrammatorDev\OpenWeatherMap\Exception\NotFoundException;
use ProgrammatorDev\OpenWeatherMap\Exception\TooManyRequestsException;
use ProgrammatorDev\OpenWeatherMap\Exception\UnauthorizedException;
use ProgrammatorDev\OpenWeatherMap\Exception\UnexpectedErrorException;
use ProgrammatorDev\OpenWeatherMap\HttpClient\HttpClientBuilder;
use ProgrammatorDev\OpenWeatherMap\HttpClient\Listener\LoggerCacheListener;
use ProgrammatorDev\OpenWeatherMap\HttpClient\ResponseMediator;
use ProgrammatorDev\OpenWeatherMap\OpenWeatherMap;
use Psr\Cache\CacheItemPoolInterface;
use Psr\Cache\InvalidArgumentException;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;
use Psr\Http\Message\UriInterface;
Expand All @@ -24,31 +26,30 @@
class AbstractEndpoint
{
use WithCacheTtlTrait;
use WithCacheInvalidationTrait;

private HttpMethodsClient $httpClient;
private Config $config;

private ?LoggerInterface $logger;
private HttpClientBuilder $httpClientBuilder;

private ?CacheItemPoolInterface $cache;

private bool $cacheInvalidation = false;

protected \DateInterval|int|null $cacheTtl = 60 * 10; // 10 minutes
private ?LoggerInterface $logger;

protected string $measurementSystem;

protected string $language;

protected ?int $cacheTtl = 60 * 10; // 10 minutes

public function __construct(protected OpenWeatherMap $api)
{
$config = $this->api->getConfig();
$this->config = $this->api->getConfig();

$this->httpClient = $config->getHttpClientBuilder()->getHttpClient();
$this->logger = $config->getLogger();
$this->cache = $config->getCache();
$this->measurementSystem = $config->getMeasurementSystem();
$this->language = $config->getLanguage();
$this->httpClientBuilder = $this->config->getHttpClientBuilder();
$this->cache = $this->config->getCache();
$this->logger = $this->config->getLogger();
$this->measurementSystem = $this->config->getMeasurementSystem();
$this->language = $this->config->getLanguage();
}

/**
Expand All @@ -58,7 +59,6 @@ public function __construct(protected OpenWeatherMap $api)
* @throws TooManyRequestsException
* @throws UnauthorizedException
* @throws UnexpectedErrorException
* @throws InvalidArgumentException
*/
protected function sendRequest(
string $method,
Expand All @@ -68,79 +68,61 @@ protected function sendRequest(
StreamInterface|string $body = null
): array
{
$uri = $this->buildUrl($baseUrl, $query);

// If there is a cache adapter, save responses into cache
if ($this->cache !== null) {
$cacheKey = $this->getCacheKey($uri);

// Invalidate cache to force new
if ($this->cacheInvalidation === true) {
$this->logger?->info('Cache invalidated', ['key' => $cacheKey]);

$this->cache->deleteItem($cacheKey);
}
$this->configurePlugins();

$cacheItem = $this->cache->getItem($cacheKey);

if ($cacheItem->isHit()) {
$this->logger?->info(\sprintf('Cache hit: %s %s', $method, $uri), ['key' => $cacheKey]);
}
else {
$response = ResponseMediator::toArray(
$this->handleRequest($method, $uri, $headers, $body)
);

$cacheItem->set($response);
$cacheItem->expiresAfter($this->cacheTtl);
$uri = $this->buildUrl($baseUrl, $query);
$response = $this->httpClientBuilder->getHttpClient()->send($method, $uri, $headers, $body);

$this->cache->save($cacheItem);
if (($statusCode = $response->getStatusCode()) >= 400) {
$this->handleResponseErrors($response, $statusCode);
}

$this->logger?->info('Cached response', ['ttl' => $this->cacheTtl, 'key' => $cacheKey]);
}
return ResponseMediator::toArray($response);
}

return $cacheItem->get();
private function configurePlugins(): void
{
// Plugin order is important
// CachePlugin should come first, otherwise the LoggerPlugin will log requests even if they are cached
if ($this->cache !== null) {
$this->httpClientBuilder->addPlugin(
new CachePlugin($this->cache, $this->httpClientBuilder->getStreamFactory(), [
'default_ttl' => $this->cacheTtl,
'cache_lifetime' => 0,
'cache_listeners' => ($this->logger !== null)
? [new LoggerCacheListener($this->logger)]
: []
])
);
}

return ResponseMediator::toArray(
$this->handleRequest($method, $uri, $headers, $body)
);
if ($this->logger !== null) {
$this->httpClientBuilder->addPlugin(
new LoggerPlugin($this->logger)
);
}
}

/**
* @throws Exception
* @throws NotFoundException
* @throws UnexpectedErrorException
* @throws TooManyRequestsException
* @throws BadRequestException
* @throws UnauthorizedException
* @throws BadRequestException
*/
private function handleRequest(
string $method,
string $uri,
array $headers,
StreamInterface|string $body = null
): ResponseInterface
private function handleResponseErrors(ResponseInterface $response, int $statusCode): void
{
$response = $this->httpClient->send($method, $uri, $headers, $body);
$statusCode = $response->getStatusCode();

// If API returns an error, throw exception
if ($statusCode >= 400) {
$error = new Error(
ResponseMediator::toArray($response)
);

match ($statusCode) {
400 => throw new BadRequestException($error),
401 => throw new UnauthorizedException($error),
404 => throw new NotFoundException($error),
429 => throw new TooManyRequestsException($error),
default => throw new UnexpectedErrorException($error)
};
}
$error = new Error(
ResponseMediator::toArray($response)
);

return $response;
match ($statusCode) {
400 => throw new BadRequestException($error),
401 => throw new UnauthorizedException($error),
404 => throw new NotFoundException($error),
429 => throw new TooManyRequestsException($error),
default => throw new UnexpectedErrorException($error)
};
}

private function buildUrl(UriInterface|string $baseUrl, array $query): string
Expand All @@ -156,9 +138,4 @@ private function buildUrl(UriInterface|string $baseUrl, array $query): string

return \sprintf('%s?%s', $baseUrl, http_build_query($query));
}

private function getCacheKey(string $value): string
{
return md5($value);
}
}
4 changes: 0 additions & 4 deletions src/Endpoint/AirPollutionEndpoint.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
use ProgrammatorDev\OpenWeatherMap\Validator\CoordinateValidatorTrait;
use ProgrammatorDev\OpenWeatherMap\Validator\LessThanValidatorTrait;
use ProgrammatorDev\OpenWeatherMap\Validator\RangeValidatorTrait;
use Psr\Cache\InvalidArgumentException;

class AirPollutionEndpoint extends AbstractEndpoint
{
Expand All @@ -35,7 +34,6 @@ class AirPollutionEndpoint extends AbstractEndpoint
* @throws TooManyRequestsException
* @throws UnauthorizedException
* @throws UnexpectedErrorException
* @throws InvalidArgumentException
* @throws ValidationException
*/
public function getCurrent(float $latitude, float $longitude): CurrentAirPollution
Expand All @@ -61,7 +59,6 @@ public function getCurrent(float $latitude, float $longitude): CurrentAirPolluti
* @throws TooManyRequestsException
* @throws UnauthorizedException
* @throws UnexpectedErrorException
* @throws InvalidArgumentException
* @throws ValidationException
*/
public function getForecast(float $latitude, float $longitude): AirPollutionList
Expand All @@ -87,7 +84,6 @@ public function getForecast(float $latitude, float $longitude): AirPollutionList
* @throws TooManyRequestsException
* @throws UnauthorizedException
* @throws UnexpectedErrorException
* @throws InvalidArgumentException
* @throws ValidationException
*/
public function getHistory(
Expand Down
6 changes: 1 addition & 5 deletions src/Endpoint/GeocodingEndpoint.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
use ProgrammatorDev\OpenWeatherMap\Validator\BlankValidatorTrait;
use ProgrammatorDev\OpenWeatherMap\Validator\CoordinateValidatorTrait;
use ProgrammatorDev\OpenWeatherMap\Validator\GreaterThanValidatorTrait;
use Psr\Cache\InvalidArgumentException;

class GeocodingEndpoint extends AbstractEndpoint
{
Expand All @@ -32,7 +31,7 @@ class GeocodingEndpoint extends AbstractEndpoint

private string $urlGeocodingReverse = 'https://api.openweathermap.org/geo/1.0/reverse';

protected \DateInterval|int|null $cacheTtl = 60 * 60 * 24 * 30; // 30 days
protected ?int $cacheTtl = 60 * 60 * 24 * 30; // 30 days

/**
* @return Location[]
Expand All @@ -42,7 +41,6 @@ class GeocodingEndpoint extends AbstractEndpoint
* @throws TooManyRequestsException
* @throws UnauthorizedException
* @throws UnexpectedErrorException
* @throws InvalidArgumentException
* @throws ValidationException
*/
public function getCoordinatesByLocationName(string $locationName, int $numResults = self::NUM_RESULTS): array
Expand All @@ -69,7 +67,6 @@ public function getCoordinatesByLocationName(string $locationName, int $numResul
* @throws TooManyRequestsException
* @throws UnauthorizedException
* @throws UnexpectedErrorException
* @throws InvalidArgumentException
* @throws ValidationException
*/
public function getCoordinatesByZipCode(string $zipCode, string $countryCode): ZipCodeLocation
Expand All @@ -96,7 +93,6 @@ public function getCoordinatesByZipCode(string $zipCode, string $countryCode): Z
* @throws TooManyRequestsException
* @throws UnauthorizedException
* @throws UnexpectedErrorException
* @throws InvalidArgumentException
* @throws ValidationException
*/
public function getLocationNameByCoordinates(float $latitude, float $longitude, int $numResults = self::NUM_RESULTS): array
Expand Down
4 changes: 0 additions & 4 deletions src/Endpoint/OneCallEndpoint.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
use ProgrammatorDev\OpenWeatherMap\Exception\ValidationException;
use ProgrammatorDev\OpenWeatherMap\Validator\CoordinateValidatorTrait;
use ProgrammatorDev\OpenWeatherMap\Validator\LessThanValidatorTrait;
use Psr\Cache\InvalidArgumentException;

class OneCallEndpoint extends AbstractEndpoint
{
Expand All @@ -38,7 +37,6 @@ class OneCallEndpoint extends AbstractEndpoint
* @throws TooManyRequestsException
* @throws UnauthorizedException
* @throws UnexpectedErrorException
* @throws InvalidArgumentException
* @throws ValidationException
*/
public function getWeather(float $latitude, float $longitude): OneCall
Expand Down Expand Up @@ -66,7 +64,6 @@ public function getWeather(float $latitude, float $longitude): OneCall
* @throws TooManyRequestsException
* @throws UnauthorizedException
* @throws UnexpectedErrorException
* @throws InvalidArgumentException
* @throws ValidationException
*/
public function getHistoryMoment(float $latitude, float $longitude, \DateTimeInterface $dateTime): HistoryMoment
Expand Down Expand Up @@ -96,7 +93,6 @@ public function getHistoryMoment(float $latitude, float $longitude, \DateTimeInt
* @throws TooManyRequestsException
* @throws UnauthorizedException
* @throws UnexpectedErrorException
* @throws InvalidArgumentException
* @throws ValidationException
*/
public function getHistoryDaySummary(float $latitude, float $longitude, \DateTimeInterface $dateTime): HistoryDaySummary
Expand Down
Loading