Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ELPP-2452 Added cache headers to recommendations #57

Merged
merged 6 commits into from Mar 30, 2017
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/App/DefaultController.php
Expand Up @@ -6,6 +6,7 @@
use eLife\Recommendations\Process\Hydration;
use eLife\Recommendations\Process\Rules;
use eLife\Recommendations\RecommendationsResponse;
use eLife\Recommendations\Response\PrivateResponse;
use eLife\Recommendations\RuleModel;
use eLife\Recommendations\RuleModelRepository;
use JMS\Serializer\SerializationContext;
Expand Down Expand Up @@ -111,11 +112,10 @@ public function indexAction(Request $request, string $type, string $id)

public function pingAction()
{
return new Response(
return new PrivateResponse(
'pong',
200,
[
'Cache-Control' => 'must-revalidate, no-cache, no-store, private',
'Content-Type' => 'text/plain; charset=UTF-8',
]
);
Expand Down
23 changes: 18 additions & 5 deletions src/App/Kernel.php
Expand Up @@ -31,6 +31,7 @@
use eLife\Recommendations\Process\Hydration;
use eLife\Recommendations\Process\Rules;
use eLife\Recommendations\RecommendationResultDiscriminator;
use eLife\Recommendations\Response\PrivateResponse;
use eLife\Recommendations\Rule\BidirectionalRelationship;
use eLife\Recommendations\Rule\CollectionContents;
use eLife\Recommendations\Rule\Common\MicroSdk;
Expand Down Expand Up @@ -101,7 +102,7 @@ public function __construct($config = [], $autoRun = true)
'debug' => false,
'validate' => false,
'annotation_cache' => true,
'ttl' => 3600,
'ttl' => 300,
'process_memory_limit' => 200,
'file_logs_path' => self::ROOT.'/var/logs',
'tables' => [
Expand Down Expand Up @@ -430,7 +431,7 @@ public function handleException(Throwable $e): Response
return new JsonResponse(array_filter([
'error' => $e->getMessage(),
'trace' => $this->app['config']['debug'] ? $e->getTraceAsString() : null,
]), $e->getCode());
]), $e->getCode() ? $e->getCode() : Response::HTTP_INTERNAL_SERVER_ERROR);
}
$logger->error('An unknown exception was thrown', [
'exception' => $e,
Expand All @@ -447,7 +448,7 @@ public function handleException(Throwable $e): Response
'time' => microtime(true) - $this->startTime,
] : [
'error' => trim($errorMessage),
], 500);
], Response::HTTP_INTERNAL_SERVER_ERROR);
}

public function withApp(callable $fn)
Expand Down Expand Up @@ -482,18 +483,30 @@ public function validate(Request $request, Response $response)
$json->_validationInfo = $e->getMessage();
$json->_time = microtime(true) - $this->startTime;
$response->setContent(json_encode($json));

return $response;
}
$this->get('logger')->warning('Invalid JSON provided to user', [
'exception' => $e,
'request' => $request,
'response' => $response,
]);
}

return $response;
}

public function cache(Request $request, Response $response)
{
if ($response instanceof PrivateResponse) {
return $response;
}
$response->setMaxAge($this->app['config']['ttl']);
$response->headers->addCacheControlDirective('stale-while-revalidate', $this->app['config']['ttl']);
$response->headers->addCacheControlDirective('stale-if-error', 86400);
$response->setVary('Accept');
$response->setEtag(md5($response->getContent()));
$response->setPublic();
$response->isNotModified($request);

return $response;
}
}
14 changes: 14 additions & 0 deletions src/Recommendations/Response/PrivateResponse.php
@@ -0,0 +1,14 @@
<?php

namespace eLife\Recommendations\Response;

use Symfony\Component\HttpFoundation\Response;

final class PrivateResponse extends Response
{
public function __construct($content = '', $status = 200, array $headers = array())
{
parent::__construct($content, $status, $headers);
$this->headers->set('Cache-Control', 'must-revalidate, no-cache, no-store, private');
}
}
55 changes: 55 additions & 0 deletions tests/src/Web/CacheHeadersTest.php
@@ -0,0 +1,55 @@
<?php

namespace tests\eLife\Web;

use DateTimeImmutable;

/**
* @group web
*/
class CacheHeadersTest extends WebTestCase
{
public function testETag()
{
$this->addArticlePoAWithId('1', (new DateTimeImmutable())->setDate(2017, 1, 1));
$this->addArticlePoAWithId('2', (new DateTimeImmutable())->setDate(2017, 2, 2));

$this->newClient();
$this->jsonRequest('GET', '/recommendations/research-article/1');
$response = $this->getResponse();
$this->assertEquals(200, $response->getStatusCode());
$eTag = $response->headers->get('ETag');
$this->assertEquals('max-age=300, public, stale-if-error=86400, stale-while-revalidate=300', $response->headers->get('Cache-Control'));
$this->assertEquals('Accept', $response->headers->get('Vary'));

$this->jsonRequest('GET', '/recommendations/research-article/1', [], ['If-None-Match' => $eTag]);
$response = $this->getResponse();
$this->assertEquals(304, $response->getStatusCode());
$this->assertEquals('max-age=300, public, stale-if-error=86400, stale-while-revalidate=300', $response->headers->get('Cache-Control'));
$this->assertEquals('Accept', $response->headers->get('Vary'));

$this->jsonRequest('GET', '/recommendations/research-article/1', [], ['If-None-Match' => 'NOT REAL ETAG']);
$response = $this->getResponse();
$this->assertEquals(200, $response->getStatusCode());
$this->assertEquals('max-age=300, public, stale-if-error=86400, stale-while-revalidate=300', $response->headers->get('Cache-Control'));
$this->assertEquals('Accept', $response->headers->get('Vary'));
}

public function testPing()
{
$this->newClient();
$this->jsonRequest('GET', '/ping');
$response = $this->getResponse();
$this->assertEquals(200, $response->getStatusCode());
$this->assertEquals('text/plain; charset=UTF-8', $response->headers->get('Content-Type'));
$this->assertEquals('must-revalidate, no-cache, no-store, private', $response->headers->get('Cache-Control'));
}

public function modifyConfiguration($config)
{
$config['ttl'] = 300;
$config['debug'] = false;

return $config;
}
}