Skip to content

Commit

Permalink
[BUGFIX] Do not cache public json endpoint (#337)
Browse files Browse the repository at this point in the history
* [BUGFIX] Do not cache public json endpoint

* Require symfony/cache-contracts

* [TASK] Remove not needed constructor parameter and string check

* Check returned types

* Apply CS rules

* Reintroduce string check

* Type check on retrival

* Fix sqlite3 installation

* Remove string check

Co-authored-by: Simon Gilli <[email protected]>
  • Loading branch information
benjaminkott and gilbertsoft authored Jul 12, 2022
1 parent dfc6dac commit 591f337
Show file tree
Hide file tree
Showing 9 changed files with 147 additions and 63 deletions.
18 changes: 9 additions & 9 deletions .github/workflows/continuous-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ jobs:
uses: shivammathur/setup-php@v2
with:
coverage: none
extensions: ctype, iconv, json, sqlit3, tokenizer, zip, zlib
extensions: ctype, iconv, json, sqlite3, tokenizer, zip, zlib
ini-values: memory_limit=-1, error_reporting=E_ALL, display_errors=On
php-version: latest
tools: composer:2
Expand Down Expand Up @@ -115,7 +115,7 @@ jobs:
uses: shivammathur/setup-php@v2
with:
coverage: none
extensions: ctype, iconv, json, sqlit3, tokenizer, zip, zlib
extensions: ctype, iconv, json, sqlite3, tokenizer, zip, zlib
ini-values: memory_limit=-1, error_reporting=E_ALL, display_errors=On
php-version: latest
tools: composer:2
Expand Down Expand Up @@ -166,7 +166,7 @@ jobs:
uses: shivammathur/setup-php@v2
with:
coverage: none
extensions: ctype, iconv, json, sqlit3, tokenizer, zip, zlib
extensions: ctype, iconv, json, sqlite3, tokenizer, zip, zlib
ini-values: memory_limit=-1, error_reporting=E_ALL, display_errors=On
php-version: ${{ matrix.php-version }}
tools: composer:2
Expand Down Expand Up @@ -214,7 +214,7 @@ jobs:
uses: shivammathur/setup-php@v2
with:
coverage: none
extensions: ctype, iconv, json, sqlit3, tokenizer, zip, zlib
extensions: ctype, iconv, json, sqlite3, tokenizer, zip, zlib
ini-values: memory_limit=-1, error_reporting=E_ALL, display_errors=On
php-version: latest
tools: composer:2
Expand Down Expand Up @@ -254,7 +254,7 @@ jobs:
uses: shivammathur/setup-php@v2
with:
coverage: none
extensions: ctype, iconv, json, sqlit3, tokenizer, zip, zlib
extensions: ctype, iconv, json, sqlite3, tokenizer, zip, zlib
ini-values: memory_limit=-1, error_reporting=E_ALL, display_errors=On
php-version: latest
tools: composer:2
Expand Down Expand Up @@ -294,7 +294,7 @@ jobs:
uses: shivammathur/setup-php@v2
with:
coverage: none
extensions: ctype, iconv, json, sqlit3, tokenizer, zip, zlib
extensions: ctype, iconv, json, sqlite3, tokenizer, zip, zlib
ini-values: memory_limit=-1, error_reporting=E_ALL, display_errors=On
php-version: latest
tools: composer:2
Expand Down Expand Up @@ -334,7 +334,7 @@ jobs:
uses: shivammathur/setup-php@v2
with:
coverage: none
extensions: ctype, iconv, json, sqlit3, tokenizer, zip, zlib
extensions: ctype, iconv, json, sqlite3, tokenizer, zip, zlib
ini-values: memory_limit=-1, error_reporting=E_ALL, display_errors=On
php-version: latest
tools: composer:2
Expand Down Expand Up @@ -398,7 +398,7 @@ jobs:
uses: shivammathur/setup-php@v2
with:
coverage: none
extensions: ctype, iconv, json, sqlit3, tokenizer, zip, zlib
extensions: ctype, iconv, json, sqlite3, tokenizer, zip, zlib
ini-values: memory_limit=-1
php-version: ${{ matrix.php-version }}
tools: composer:2
Expand Down Expand Up @@ -487,7 +487,7 @@ jobs:
uses: shivammathur/setup-php@v2
with:
coverage: xdebug
extensions: ctype, iconv, json, sqlit3, tokenizer, zip, zlib
extensions: ctype, iconv, json, sqlite3, tokenizer, zip, zlib
ini-values: memory_limit=-1, error_reporting=E_ALL, display_errors=On
php-version: ${{ matrix.php-version }}
tools: composer:2
Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
"sensio/framework-extra-bundle": "^6.1",
"symfony/asset": "5.4.*",
"symfony/cache": "5.4.*",
"symfony/cache-contracts": "^2.5",
"symfony/console": "5.4.*",
"symfony/dependency-injection": "5.4.*",
"symfony/dotenv": "5.4.*",
Expand Down
14 changes: 7 additions & 7 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions src/Controller/Api/AbstractController.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,12 @@
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Validator\ConstraintViolationInterface;
use Symfony\Contracts\Cache\TagAwareCacheInterface;

class AbstractController extends \Symfony\Bundle\FrameworkBundle\Controller\AbstractController
{
public function __construct(
private readonly TagAwareCacheInterface $cache,
private readonly \JMS\Serializer\SerializerInterface $serializer,
private readonly \Doctrine\Persistence\ManagerRegistry $managerRegistry,
private readonly \App\Repository\MajorVersionRepository $majorVersions,
Expand All @@ -49,6 +51,11 @@ public function __construct(
) {
}

protected function getCache(): TagAwareCacheInterface
{
return $this->cache;
}

protected function getSerializer(): SerializerInterface
{
return $this->serializer;
Expand Down
6 changes: 1 addition & 5 deletions src/Controller/Api/CacheController.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
use Nelmio\ApiDocBundle\Annotation\Security as DocSecurity;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
use Swagger\Annotations as SWG;
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
Expand Down Expand Up @@ -156,10 +155,7 @@ private function getPurgeUrlsForMajorVersion(float $version): array
*/
private function deleteReleases(): void
{
$filesystemAdapter = new FilesystemAdapter();
if ($filesystemAdapter->hasItem('releases.json')) {
$filesystemAdapter->delete('releases.json');
}
$this->getCache()->invalidateTags(['releases']);
}

/**
Expand Down
19 changes: 0 additions & 19 deletions src/Controller/DefaultController.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
use App\Entity\Release;
use App\Utility\VersionUtility;
use InvalidArgumentException;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
Expand All @@ -39,8 +38,6 @@

/**
* Regular content and download pages
*
* @Cache(maxage="3600", public=true)
*/
class DefaultController extends AbstractController
{
Expand Down Expand Up @@ -82,22 +79,6 @@ public function apiDoc(): RedirectResponse
return $this->redirectToRoute('app.swagger_ui');
}

/**
* Outputs the JSON file
* /json
* Legacy end point
*/
#[Route(path: '/json', methods: ['GET'], name: 'legacy-releases-json')]
public function releaseJson(): Response
{
$content = $this->legacyDataService->getReleaseJson();
$headers = [
'Content-type' => 'application/json',
'Access-Control-Allow-Origin' => '*',
];
return new Response($content, \Symfony\Component\HttpFoundation\Response::HTTP_OK, $headers);
}

/**
* Display release notes for a version
*/
Expand Down
49 changes: 49 additions & 0 deletions src/Controller/JsonController.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

declare(strict_types=1);

/*
* This file is part of the package t3o/get.typo3.org.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* For the full copyright and license information, please read the
* LICENSE file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;

#[Route(path: '/json', methods: ['GET'], name: 'json_')]
class JsonController extends AbstractController
{
public function __construct(
private \App\Service\LegacyDataService $legacyDataService,
) {
}

// Route has set a priority to avoid conflicts with `specificversion`
#[Route(path: '', methods: ['GET'], name: 'index', priority: 1)]
public function index(): Response
{
$content = $this->legacyDataService->getReleaseJson();
$headers = [
'Content-type' => 'application/json',
'Access-Control-Allow-Origin' => '*',
];
return new Response($content, \Symfony\Component\HttpFoundation\Response::HTTP_OK, $headers);
}
}
31 changes: 8 additions & 23 deletions src/Service/LegacyDataService.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,43 +23,28 @@

namespace App\Service;

use App\Entity\MajorVersion;
use App\Repository\MajorVersionRepository;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Cache\Adapter\FilesystemAdapter;
use Symfony\Contracts\Cache\ItemInterface;
use Symfony\Contracts\Cache\TagAwareCacheInterface;

class LegacyDataService
{
public function __construct(private readonly EntityManagerInterface $entityManager)
{
public function __construct(
private readonly TagAwareCacheInterface $cache,
private readonly MajorVersionRepository $majorVersionRepository,
) {
}

public function getReleaseJson(): string
{
/*
$cache = new FilesystemAdapter();
$result = $cache->get('releases.json', function (ItemInterface $item): string {
/** @var MajorVersionRepository $majorVersions * /
$majorVersions = $this->entityManager->getRepository(MajorVersion::class);
$content = json_encode($majorVersions->findAllPreparedForJson(), JSON_THROW_ON_ERROR);
$result = $this->cache->get('releases.json', function (ItemInterface $item): string {
$item->tag(['releases']);
$content = json_encode($this->majorVersionRepository->findAllPreparedForJson(), JSON_THROW_ON_ERROR);
$content = $content != false ? $content : '';
// remove version suffix only used for version sorting
return str_replace('.0000', '', $content);
});

if (!is_string($result)) {
throw new \RuntimeException(sprintf('String expected but %s given.', gettype($result)));
}
*/

/** @var MajorVersionRepository $majorVersions */
$majorVersions = $this->entityManager->getRepository(MajorVersion::class);
$content = json_encode($majorVersions->findAllPreparedForJson(), JSON_THROW_ON_ERROR);
$content = $content != false ? $content : '';
// remove version suffix only used for version sorting
$result = str_replace('.0000', '', $content);

return $result;
}
}
65 changes: 65 additions & 0 deletions tests/Functional/Controller/Web/JsonControllerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php

/*
* This file is part of the package t3o/get.typo3.org.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* For the full copyright and license information, please read the
* LICENSE file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/

namespace App\Tests\Functional\Controller\Web;

use App\DataFixtures\MajorVersionFixtures;
use App\DataFixtures\ReleaseFixtures;
use App\DataFixtures\RequirementFixtures;
use App\Tests\Functional\AbstractCase;
use RuntimeException;
use Symfony\Component\HttpFoundation\Response;

class JsonControllerTest extends AbstractCase
{
public function setUp(): void
{
parent::setUp();
$this->addFixture(new MajorVersionFixtures());
$this->addFixture(new ReleaseFixtures());
$this->addFixture(new RequirementFixtures());
$this->executeFixtures();
}

/**
* @test
*/
public function index(): void
{
$this->client->request('GET', '/json');
$response = $this->client->getResponse();

if (($json = $response->getContent()) === false) {
throw new RuntimeException('Error no response content.', 1_657_642_832);
}

if (!\is_array($content = json_decode($json, true, 512, JSON_THROW_ON_ERROR))) {
throw new RuntimeException('Error array expected.', 1_657_642_833);
}

self::assertSame(Response::HTTP_OK, $response->getStatusCode());
self::assertArrayHasKey('10', $content);
self::assertArrayHasKey('latest_stable', $content);
self::assertArrayHasKey('latest_old_stable', $content);
self::assertArrayHasKey('latest_lts', $content);
self::assertArrayHasKey('latest_old_lts', $content);
}
}

0 comments on commit 591f337

Please sign in to comment.