Compare commits

...

8 Commits

14 changed files with 247 additions and 101 deletions

View File

@ -22,7 +22,8 @@
},
"require": {
"php": "^8.3",
"symfony/finder": "^7.0"
"symfony/finder": "^7.0",
"psr/cache": "^3.0"
},
"autoload": {
"psr-4": {

View File

@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace Quintolin\Storage\Contract;
enum CacheKey: string
{
case RELEASE_NOTES = 'app.storage.release_notes';
}

View File

@ -4,47 +4,45 @@ declare(strict_types=1);
namespace Quintolin\Storage\Query;
use DateTimeImmutable;
use Psr\Cache\CacheItemPoolInterface;
use Quintolin\Storage\Contract\CacheKey;
use Quintolin\Storage\QueryResult\ReleaseNote;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Finder\SplFileInfo;
use Quintolin\Storage\Repository\ReleaseNoteRepository;
use function sprintf;
abstract readonly class AbstractReleaseNotesQuery
{
public function __construct(
private string $projectDir,
protected ReleaseNoteRepository $repository,
protected CacheItemPoolInterface $cacheItemPool,
) {}
/**
* @return ReleaseNote[]
*/
protected function fetch(null | int $maxCount): array
protected function get(null | int $maxCount): array
{
$finder = new Finder();
$finder->in(dirs: [$this->projectDir]);
$finder->files();
$finder->name(patterns: ['*.html']);
$finder->sortByName();
$finder->reverseSorting();
$cacheKey = $this->getCacheKey(maxCount: $maxCount);
$cacheItem = $this->cacheItemPool->getItem(key: $cacheKey);
$notes = [];
$total = 0;
/** @var SplFileInfo $file */
foreach ($finder as $file) {
$dateStr = $file->getBasename(suffix: '.html');
$createdAt = DateTimeImmutable::createFromFormat('!Y-m-d', $dateStr);
if ($createdAt instanceof DateTimeImmutable) {
$id = sprintf('%d000', $createdAt->getTimestamp());
$notes[] = new ReleaseNote(id: $id, createdAt: $createdAt, htmlContent: $file->getContents());
++$total;
}
if ($maxCount > 0 && $total >= $maxCount) {
break;
}
if ($cacheItem->isHit()) {
return $cacheItem->get();
}
$notes = $this->repository->fetch(maxCount: $maxCount);
$cacheItem->set(value: $notes);
$this->cacheItemPool->save($cacheItem);
return $notes;
}
private function getCacheKey(null | int $maxCount): string
{
return sprintf(
'%s_%s',
CacheKey::RELEASE_NOTES->value,
null !== $maxCount ? $maxCount : 'all',
);
}
}

View File

@ -10,7 +10,7 @@ final readonly class LatestReleaseNoteQuery extends AbstractReleaseNotesQuery
{
public function __invoke(): null | ReleaseNote
{
$notes = $this->fetch(maxCount: 1);
$notes = $this->get(maxCount: 1);
return $notes[0] ?? null;
}
}

View File

@ -13,6 +13,6 @@ final readonly class ReleaseNotesQuery extends AbstractReleaseNotesQuery
*/
public function __invoke(): array
{
return $this->fetch(maxCount: null);
return $this->get(maxCount: null);
}
}

View File

@ -0,0 +1,50 @@
<?php
declare(strict_types=1);
namespace Quintolin\Storage\Repository;
use DateTimeImmutable;
use Quintolin\Storage\QueryResult\ReleaseNote;
use Symfony\Component\Finder\Finder;
use Symfony\Component\Finder\SplFileInfo;
use function sprintf;
final readonly class ReleaseNoteRepository
{
public function __construct(
private string $projectDir,
) {}
/**
* @return ReleaseNote[]
*/
public function fetch(null | int $maxCount): array
{
$finder = new Finder();
$finder->in(dirs: [$this->projectDir]);
$finder->files();
$finder->name(patterns: ['*.html']);
$finder->sortByName();
$finder->reverseSorting();
$notes = [];
$total = 0;
/** @var SplFileInfo $file */
foreach ($finder as $file) {
$dateStr = $file->getBasename(suffix: '.html');
$createdAt = DateTimeImmutable::createFromFormat('!Y-m-d', $dateStr);
if ($createdAt instanceof DateTimeImmutable) {
$id = sprintf('%d000', $createdAt->getTimestamp());
$notes[] = new ReleaseNote(id: $id, createdAt: $createdAt, htmlContent: $file->getContents());
++$total;
}
if ($maxCount > 0 && $total >= $maxCount) {
break;
}
}
return $notes;
}
}

View File

@ -32,9 +32,9 @@
"symfony/clock": "7.0.*",
"symfony/console": "7.0.*",
"symfony/dotenv": "7.0.*",
"symfony/finder": "7.0.*",
"symfony/flex": "^2",
"symfony/framework-bundle": "7.0.*",
"symfony/process": "7.0.*",
"symfony/runtime": "7.0.*",
"symfony/twig-bundle": "7.0.*",
"symfony/yaml": "7.0.*",

127
website/composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "eafe42c282ea9b4550d687735901053f",
"content-hash": "01a78a0a3c014047539e06c2f37f5a9a",
"packages": [
{
"name": "composer/semver",
@ -383,10 +383,11 @@
"dist": {
"type": "path",
"url": "../modules/storage",
"reference": "40dd747426f7ffba283e67e03ffada0a9622ff11"
"reference": "93e9e6864db9a5e765f69cee76194292fa809ee6"
},
"require": {
"php": "^8.3",
"psr/cache": "^3.0",
"symfony/finder": "^7.0"
},
"type": "library",
@ -2538,6 +2539,67 @@
],
"time": "2023-08-16T06:22:46+00:00"
},
{
"name": "symfony/process",
"version": "v7.0.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
"reference": "acd3eb5cb02382c1cb0287ba29b2908cc6ffa83a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/acd3eb5cb02382c1cb0287ba29b2908cc6ffa83a",
"reference": "acd3eb5cb02382c1cb0287ba29b2908cc6ffa83a",
"shasum": ""
},
"require": {
"php": ">=8.2"
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\Process\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Executes commands in sub-processes",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/process/tree/v7.0.2"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2023-12-24T09:15:37+00:00"
},
{
"name": "symfony/routing",
"version": "v7.0.2",
@ -3814,67 +3876,6 @@
}
],
"time": "2023-10-31T18:23:49+00:00"
},
{
"name": "symfony/process",
"version": "v7.0.2",
"source": {
"type": "git",
"url": "https://github.com/symfony/process.git",
"reference": "acd3eb5cb02382c1cb0287ba29b2908cc6ffa83a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/process/zipball/acd3eb5cb02382c1cb0287ba29b2908cc6ffa83a",
"reference": "acd3eb5cb02382c1cb0287ba29b2908cc6ffa83a",
"shasum": ""
},
"require": {
"php": ">=8.2"
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\Process\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Executes commands in sub-processes",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/process/tree/v7.0.2"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2023-12-24T09:15:37+00:00"
}
],
"aliases": [],

View File

@ -3,16 +3,12 @@ services:
autowire: true
autoconfigure: true
Quintolin\Storage\Repository\:
resource: '../../../modules/storage/src/Repository/'
Quintolin\Storage\Query\:
resource: '../../../modules/storage/src/Query/'
Quintolin\Storage\Query\AbstractReleaseNotesQuery:
abstract: true
Quintolin\Storage\Repository\ReleaseNoteRepository:
arguments:
$projectDir: '%kernel.project_dir%/data/release-notes'
Quintolin\Storage\Query\LatestReleaseNoteQuery:
parent: Quintolin\Storage\Query\AbstractReleaseNotesQuery
Quintolin\Storage\Query\ReleaseNotesQuery:
parent: Quintolin\Storage\Query\AbstractReleaseNotesQuery

View File

@ -0,0 +1,10 @@
<?php
declare(strict_types=1);
namespace App\Contract;
enum CacheKey: string
{
case APPLICATION_VERSION = 'app.version';
}

View File

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace App\Controller;
use App\Service\VersionReader;
use Quintolin\Core\HealthCheck as CoreHealthCheck;
use Quintolin\Storage\HealthCheck as StorageHealthCheck;
use Symfony\Component\HttpFoundation\JsonResponse;
@ -16,12 +17,17 @@ use Symfony\Component\Routing\Attribute\Route;
#[Route(path: '/health', name: 'app_health', methods: [Request::METHOD_GET])]
final readonly class HealthController
{
public function __construct(
private VersionReader $versionReader,
) {}
public function __invoke(): Response
{
$coreChecker = new CoreHealthCheck();
$storageChecker = new StorageHealthCheck();
return new JsonResponse(data: [
'version' => ($this->versionReader)(),
'core' => $coreChecker(),
'storage' => $storageChecker(),
]);

View File

@ -0,0 +1,42 @@
<?php
declare(strict_types=1);
namespace App\Service;
use App\Contract\CacheKey;
use Psr\Cache\CacheItemPoolInterface;
use Symfony\Component\Process\Process;
use function trim;
final class VersionReader
{
public function __construct(
private CacheItemPoolInterface $cacheItemPool,
) {}
public function __invoke(): string
{
$cacheKey = CacheKey::APPLICATION_VERSION->value;
$cacheItem = $this->cacheItemPool->getItem(key: $cacheKey);
if ($cacheItem->isHit()) {
return $cacheItem->get();
}
$version = $this->fetch();
$cacheItem->set(value: $version);
$this->cacheItemPool->save($cacheItem);
return $version;
}
private function fetch(): string
{
$process = new Process(command: ['git', 'describe', '--always']);
$process->run();
return trim(string: $process->getOutput());
}
}

View File

@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
namespace App\Twig;
use App\Service\VersionReader;
use Override;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
final class AppVersionExtension extends AbstractExtension
{
public function __construct(
private VersionReader $versionReader,
) {}
#[Override]
public function getFunctions(): array
{
return [
new TwigFunction('app_version', [$this, 'getVersion']),
];
}
public function getVersion(): string
{
return ($this->versionReader)();
}
}

View File

@ -26,8 +26,10 @@
{%- set current_datetime_human = current_datetime|format_datetime(pattern='yyyy-MM-dd, HH:mm:ss z') -%}
<footer>
<p>
<b>Server time:</b>
<b>Server Time:</b>
<time datetime="{{ current_datetime_machine }}">{{ current_datetime_human }}</time>
<b>Game Engine version:</b>
<code>{{ app_version() }}</code>
</p>
</footer>
</body>