Skip to content

Commit

Permalink
Nette FW integration, Tests
Browse files Browse the repository at this point in the history
- added compiler extension `AmpClientExtension` for Nette DIC
- added cache storage implementation `NetteCacheStorage`
- fixed errors in codebase
- added more tests
- added PHP extension `uopz` for images in Dockerfile (for testing purposes only)
- added dev dependencies on `nette/bootstrap`, `nette/caching`, `nette/di`, `psr/log` and `slope-it/clock-mock`
- added GH action for code coverage
  • Loading branch information
tg666 committed Nov 21, 2023
1 parent 6aa0f64 commit d905ce1
Show file tree
Hide file tree
Showing 36 changed files with 2,095 additions and 21 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/coding-style.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ jobs:
with:
php-version: 7.4
tools: composer:v2
extensions: uopz

- name: Install dependencies
run: composer update --no-progress --prefer-dist --prefer-stable --optimize-autoloader --quiet
Expand All @@ -42,6 +43,7 @@ jobs:
with:
php-version: 7.4
tools: composer:v2
extensions: uopz

- name: Install dependencies
run: composer update --no-progress --prefer-dist --prefer-stable --optimize-autoloader --quiet
Expand Down
40 changes: 40 additions & 0 deletions .github/workflows/coverage.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Coverage

on:
push:
branches:
- main
tags:
- v*
pull_request:
branches:
- main

jobs:
coverage:
name: Coverage
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3

- name: Install PHP
uses: shivammathur/setup-php@v2
with:
php-version: 8.1
coverage: pcov
extensions: tokenizer, uopz
tools: composer:v2

- name: Install dependencies
run: composer update --no-progress --prefer-dist --prefer-stable --optimize-autoloader

- name: Generate the coverage report
run: vendor/bin/tester -C -s --coverage ./coverage.xml --coverage-src ./src ./tests

- name: Upload the coverage report
env:
COVERALLS_REPO_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
run: |
wget https://github.com/php-coveralls/php-coveralls/releases/download/v2.5.3/php-coveralls.phar
php php-coveralls.phar --verbose --config tests/.coveralls.yml
1 change: 1 addition & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ jobs:
with:
php-version: ${{ matrix.php-versions }}
tools: composer:v2
extensions: uopz

- name: Install dependencies
run: composer update --no-progress --prefer-dist --prefer-stable --optimize-autoloader
Expand Down
12 changes: 8 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ RUN apk add --no-cache --update git
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
RUN apk add --no-cache ${PHPIZE_DEPS} \
&& pecl install pcov \
&& docker-php-ext-enable pcov
&& pecl install uopz-6.1.2 \
&& docker-php-ext-enable pcov uopz

CMD tail -f /dev/null

Expand All @@ -20,7 +21,8 @@ RUN apk add --no-cache --update git
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
RUN apk add --no-cache ${PHPIZE_DEPS} \
&& pecl install pcov \
&& docker-php-ext-enable pcov
&& pecl install uopz-7.1.1 \
&& docker-php-ext-enable pcov uopz

CMD tail -f /dev/null

Expand All @@ -33,7 +35,8 @@ RUN apk add --no-cache --update git
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
RUN apk add --no-cache ${PHPIZE_DEPS} \
&& pecl install pcov \
&& docker-php-ext-enable pcov
&& pecl install uopz-7.1.1 \
&& docker-php-ext-enable pcov uopz

CMD tail -f /dev/null

Expand All @@ -46,6 +49,7 @@ RUN apk add --no-cache --update git
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
RUN apk add --no-cache ${PHPIZE_DEPS} \
&& pecl install pcov \
&& docker-php-ext-enable pcov
&& pecl install uopz-7.1.1 \
&& docker-php-ext-enable pcov uopz

CMD tail -f /dev/null
5 changes: 5 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,15 @@
"friendsofphp/php-cs-fixer": "^3.17",
"kubawerlos/php-cs-fixer-custom-fixers": "^3.14",
"mockery/mockery": "^1.6",
"nette/bootstrap": "^3.0",
"nette/caching": "^3.1",
"nette/di": "^3.0",
"nette/tester": "^2.4",
"phpstan/phpstan": "^1.10",
"phpstan/phpstan-nette": "^1.2",
"psr/log": "^1.1",
"roave/security-advisories": "dev-latest",
"slope-it/clock-mock": "^0.4.0",
"symplify/phpstan-rules": "12.0.2.72"
},
"config": {
Expand Down
5 changes: 4 additions & 1 deletion src/AmpClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
use SixtyEightPublishers\AmpClient\Response\BannersResponse;
use SixtyEightPublishers\AmpClient\Response\Hydrator\BannersResponseHydratorHandler;
use SixtyEightPublishers\AmpClient\Response\Hydrator\ResponseHydrator;
use stdClass;
use function array_map;
use function count;
use function sprintf;
Expand Down Expand Up @@ -110,10 +111,12 @@ public function fetchBanners(BannersRequest $request): BannersResponse

foreach ($positions as $position) {
$position = $position->withResources($defaultResources);
$queryParam[$position->getCode()] = array_map(
$resources = array_map(
static fn (BannerResource $resource): array => $resource->getValues(),
$position->getResources(),
);

$queryParam[$position->getCode()] = 0 >= count($resources) ? new stdClass() : $resources;
}

$options = [
Expand Down
190 changes: 190 additions & 0 deletions src/Bridge/Nette/DI/AmpClientExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
<?php

declare(strict_types=1);

namespace SixtyEightPublishers\AmpClient\Bridge\Nette\DI;

use Nette\DI\CompilerExtension;
use Nette\DI\Definitions\ServiceDefinition;
use Nette\DI\Definitions\Statement;
use Nette\Schema\Expect;
use Nette\Schema\Schema;
use SixtyEightPublishers\AmpClient\AmpClient;
use SixtyEightPublishers\AmpClient\AmpClientInterface;
use SixtyEightPublishers\AmpClient\Bridge\Nette\DI\Config\AmpClientConfig;
use SixtyEightPublishers\AmpClient\Bridge\Nette\DI\Config\CacheConfig;
use SixtyEightPublishers\AmpClient\Bridge\Nette\DI\Config\HttpConfig;
use SixtyEightPublishers\AmpClient\Bridge\Nette\NetteCacheStorage;
use SixtyEightPublishers\AmpClient\ClientConfig;
use SixtyEightPublishers\AmpClient\Http\Cache\CacheStorageInterface;
use SixtyEightPublishers\AmpClient\Http\Cache\NoCacheStorage;
use SixtyEightPublishers\AmpClient\Http\HttpClientFactory;
use SixtyEightPublishers\AmpClient\Http\HttpClientFactoryInterface;
use SixtyEightPublishers\AmpClient\Request\ValueObject\BannerResource;
use SixtyEightPublishers\AmpClient\Response\Hydrator\BannersResponseHydratorHandler;
use SixtyEightPublishers\AmpClient\Response\Hydrator\ResponseHydrator;
use SixtyEightPublishers\AmpClient\Response\Hydrator\ResponseHydratorHandlerInterface;
use SixtyEightPublishers\AmpClient\Response\Hydrator\ResponseHydratorInterface;
use function array_values;
use function assert;
use function count;

final class AmpClientExtension extends CompilerExtension
{
public function getConfigSchema(): Schema
{
return Expect::structure([
'method' => Expect::anyOf(...ClientConfig::Methods)
->dynamic(),
'url' => Expect::string()
->required()
->dynamic(),
'channel' => Expect::string()
->required()
->dynamic(),
'version' => Expect::anyOf(...ClientConfig::Versions)
->dynamic(),
'locale' => Expect::string()
->nullable()
->dynamic(),
'default_resources' => Expect::arrayOf(
Expect::anyOf(Expect::string(), Expect::listOf('string')),
Expect::string(),
),
'origin' => Expect::string()
->nullable()
->dynamic(),
'cache' => Expect::structure([
'storage' => Expect::anyOf(Expect::string(), Expect::type(Statement::class))
->nullable()
->before(static function ($factory): Statement {
return $factory instanceof Statement ? $factory : new Statement($factory);
}),
'expiration' => Expect::anyOf(Expect::string(), Expect::int())->dynamic(),
'cache_control_header_override' => Expect::string()
->nullable(),
])->castTo(CacheConfig::class),
'http' => Expect::structure([
'guzzle_config' => Expect::array(),
])->castTo(HttpConfig::class),
])->castTo(AmpClientConfig::class);
}

public function loadConfiguration(): void
{
$builder = $this->getContainerBuilder();
$config = $this->getConfig();
assert($config instanceof AmpClientConfig);

$builder->addDefinition($this->prefix('config'))
->setAutowired(false)
->setType(ClientConfig::class)
->setCreator($this->createClientConfigCreator($config));

$cacheStorageCreator = null === $config->cache->storage
? new Statement(NoCacheStorage::class)
: new Statement(NetteCacheStorage::class, [
'storage' => $config->cache->storage,
]);

$builder->addDefinition($this->prefix('cacheStorage'))
->setAutowired(false)
->setType(CacheStorageInterface::class)
->setCreator($cacheStorageCreator);

$builder->addDefinition($this->prefix('responseHydrator'))
->setAutowired(false)
->setType(ResponseHydratorInterface::class)
->setCreator(ResponseHydrator::class);

$builder->addDefinition($this->prefix('responseHydrator.handler.bannersRequest'))
->setAutowired(false)
->setType(ResponseHydratorHandlerInterface::class)
->setCreator(BannersResponseHydratorHandler::class);

$builder->addDefinition($this->prefix('httpClientFactory'))
->setAutowired(false)
->setType(HttpClientFactoryInterface::class)
->setCreator(HttpClientFactory::class, [
'responseHydrator' => $this->prefix('@responseHydrator'),
'guzzleClientConfig' => $config->http->guzzle_config,
]);

$builder->addDefinition($this->prefix('ampClient'))
->setType(AmpClientInterface::class)
->setCreator(AmpClient::class, [
'config' => $this->prefix('@config'),
'httpClientFactory' => $this->prefix('@httpClientFactory'),
'cacheStorage' => $this->prefix('@cacheStorage'),
]);
}

public function beforeCompile(): void
{
$builder = $this->getContainerBuilder();

$responseHydratorHandlers = $builder->findByType(ResponseHydratorHandlerInterface::class);
$responseHydratorService = $builder->getDefinition($this->prefix('responseHydrator'));
assert($responseHydratorService instanceof ServiceDefinition);

$responseHydratorService->setArgument('handlers', array_values($responseHydratorHandlers));
}

private function createClientConfigCreator(AmpClientConfig $config): Statement
{
$clientConfigFactory = new Statement([ClientConfig::class, 'create'], [
'url' => $config->url,
'channel' => $config->channel,
]);

if (null !== $config->method) {
$clientConfigFactory = new Statement([$clientConfigFactory, 'withMethod'], [
'method' => $config->method,
]);
}

if (null !== $config->version) {
$clientConfigFactory = new Statement([$clientConfigFactory, 'withVersion'], [
'version' => $config->version,
]);
}

if (null !== $config->locale) {
$clientConfigFactory = new Statement([$clientConfigFactory, 'withLocale'], [
'locale' => $config->locale,
]);
}

if (0 < count($config->default_resources)) {
$defaultResources = [];

foreach ($config->default_resources as $resourceCode => $resourceValues) {
$defaultResources[] = new Statement(BannerResource::class, [$resourceCode, $resourceValues]);
}

$clientConfigFactory = new Statement([$clientConfigFactory, 'withDefaultResources'], [
'resources' => $defaultResources,
]);
}

if (null !== $config->origin) {
$clientConfigFactory = new Statement([$clientConfigFactory, 'withOrigin'], [
'origin' => $config->origin,
]);
}

if (null !== $config->cache->expiration) {
$clientConfigFactory = new Statement([$clientConfigFactory, 'withCacheExpiration'], [
'cacheExpiration' => $config->cache->expiration,
]);
}

if (null !== $config->cache->cache_control_header_override) {
$clientConfigFactory = new Statement([$clientConfigFactory, 'withCacheControlHeaderOverride'], [
'cacheControlHeaderOverride' => $config->cache->cache_control_header_override,
]);
}

return $clientConfigFactory;
}
}
29 changes: 29 additions & 0 deletions src/Bridge/Nette/DI/Config/AmpClientConfig.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

declare(strict_types=1);

namespace SixtyEightPublishers\AmpClient\Bridge\Nette\DI\Config;

use Nette\DI\Definitions\Statement;

final class AmpClientConfig
{
public ?string $method = null;

public string $url;

public string $channel;

public ?int $version = null;

public ?string $locale = null;

/** @var array<int, Statement> */
public array $default_resources = [];

public ?string $origin = null;

public CacheConfig $cache;

public HttpConfig $http;
}
17 changes: 17 additions & 0 deletions src/Bridge/Nette/DI/Config/CacheConfig.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace SixtyEightPublishers\AmpClient\Bridge\Nette\DI\Config;

use Nette\DI\Definitions\Statement;

final class CacheConfig
{
public ?Statement $storage;

/** @var string|int|null */
public $expiration = null;

public ?string $cache_control_header_override = null;
}
11 changes: 11 additions & 0 deletions src/Bridge/Nette/DI/Config/HttpConfig.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

declare(strict_types=1);

namespace SixtyEightPublishers\AmpClient\Bridge\Nette\DI\Config;

final class HttpConfig
{
/** @var array<string, mixed> */
public array $guzzle_config = [];
}
Loading

0 comments on commit d905ce1

Please sign in to comment.