Skip to content

Commit

Permalink
Merge pull request #278 from Verivox/provide_response_cache
Browse files Browse the repository at this point in the history
Provide response cache
  • Loading branch information
weisswurstkanone authored Mar 3, 2021
2 parents 5e24051 + 0988cbd commit bbf0c3f
Show file tree
Hide file tree
Showing 9 changed files with 588 additions and 2 deletions.
59 changes: 59 additions & 0 deletions doc/10_GraphQL/10_Events.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ All Datahub events are defined as a constant on component classes:
- [Mutation](https://github.com/pimcore/data-hub/blob/master/src/Event/GraphQL/MutationEvents.php)
- [Executor](https://github.com/pimcore/data-hub/blob/master/src/Event/GraphQL/ExecutorEvents.php)
- [Listing](https://github.com/pimcore/data-hub/blob/master/src/Event/GraphQL/ListingEvents.php)
- [Ouput cache](https://github.com/pimcore/data-hub/blob/master/src/Event/GraphQL/OutputCacheEvents.php)

## Event Subscriber examples

Expand Down Expand Up @@ -218,3 +219,61 @@ class GraphQlSubscriber implements EventSubscriberInterface
}

```

#### Example 5: Add custom conditions to enable/disable output (responses) cache per request

- `OutputCacheEvents::PRE_LOAD`: is triggered before trying to load an entry from cache, if cache is enabled. You can disable the cache for this request by setting `$event->setUseCache(false)`. If you disable the cache, the entry won't be loaded nor saved
- `OutputCacheEvents::PRE_SAVE`: if cache is enabled, it's triggered before saving an entry into the cache. You can use it to modify the response before it gets saved.

```php
<?php

namespace AppBundle\EventListener;
use Pimcore\Bundle\DataHubBundle\Event\GraphQL\Model\OutputCachePreLoadEvent;
use Pimcore\Bundle\DataHubBundle\Event\GraphQL\Model\OutputCachePreSaveEvent;
use Pimcore\Bundle\DataHubBundle\Event\GraphQL\OutputCacheEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class GraphqlListener implements EventSubscriberInterface
{
/**
* @inheritDoc
*/
public static function getSubscribedEvents()
{
return [
OutputCacheEvents::PRE_LOAD => 'onPreLoadCache',
OutputCacheEvents::PRE_SAVE => 'onPreSaveCache'
];
}

/**
* @param OutputCachePreLoadEvent $event
*/
public function onPreLoadCache(OutputCachePreLoadEvent $event)
{
$uri = $event->getRequest()->getMethod();

if(str_contains($uri, "my-special-endpoint"))
{
$event->setUseCache(false);
}
}

/**
* @param OutputCachePreSaveEvent $event
*/
public function onPreSaveCache(OutputCachePreSaveEvent $event)
{
$uri = $event->getRequest()->getMethod();

if(str_contains($uri, "my-awesome-endpoint"))
{
$response = $event->getResponse();
// modify the response as you want it to be saved...

$event->setResponse($response);
}
}
}
```
23 changes: 22 additions & 1 deletion doc/10_GraphQL/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,25 @@ for an endpoint in an iframe within Pimcore or as an additional browser tab.

## Events
It is possible to customize default behavior of graphQL endpoint with event listeners. For details
see [Events Documentation](./10_Events.md).
see [Events Documentation](./10_Events.md).


## Output cache
It is possible to keep a cache of the responses delivered by the endpoint, using the same default cache backend configured for Pimcore (Doctrine, Redis,...). This is specially useful to speed up the endpoint replies when it produces complex responses with many dependencies.

The cache can be enabled and configured with a configuration entry like this in your `config.yml` file:
```yml
#### DATAHUB OUTPUT CACHE
pimcore_data_hub:
graphql:
output_cache_enabled: true # Enables/disables the output (responses) cache
output_cache_lifetime: 20 # If enabled, for how many seconds each response will be cached
```
By default the cache is disabled but if it is enabled and you don't specify a value for `output_cache_lifetime`, its default value is set to 30 seconds.

### Disable Output Cache for a Single Request (only in DEBUG MODE)
Just add the parameter `?pimcore_outputfilters_disabled=true` to the URL. This works in a similar way as the [Pimcore's Full Page Cache](https://pimcore.com/docs/pimcore/current/Development_Documentation/Development_Tools_and_Details/Cache/Full_Page_Cache.html).

### Customize the Cache Behaviour
It is possible to customize some behavior of output cache with event listeners. For details
see [Events Documentation](./10_Events.md).
19 changes: 18 additions & 1 deletion src/Controller/WebserviceController.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
use Pimcore\Bundle\DataHubBundle\GraphQL\Query\QueryType;
use Pimcore\Bundle\DataHubBundle\GraphQL\Service;
use Pimcore\Bundle\DataHubBundle\Service\CheckConsumerPermissionsService;
use Pimcore\Bundle\DataHubBundle\Service\OutputCacheService;
use Pimcore\Bundle\DataHubBundle\PimcoreDataHubBundle;
use Pimcore\Cache\Runtime;
use Pimcore\Controller\FrontendController;
Expand All @@ -51,13 +52,19 @@ class WebserviceController extends FrontendController
*/
private $permissionsService;

/**
* @var OutputCacheService
*/
private $cacheService;

/**
* @param EventDispatcherInterface $eventDispatcher
*/
public function __construct(EventDispatcherInterface $eventDispatcher, CheckConsumerPermissionsService $permissionsService)
public function __construct(EventDispatcherInterface $eventDispatcher, CheckConsumerPermissionsService $permissionsService, OutputCacheService $cacheService)
{
$this->eventDispatcher = $eventDispatcher;
$this->permissionsService = $permissionsService;
$this->cacheService = $cacheService;
}

/**
Expand All @@ -83,6 +90,13 @@ public function webonyxAction(Service $service, LocaleServiceInterface $localeSe
throw new AccessDeniedHttpException('Permission denied, apikey not valid');
}

if($response = $this->cacheService->load($request)) {
Logger::debug("Loading response from cache");
return $response;
}

Logger::debug("Cache entry not found");

// context info, will be passed on to all resolver function
$context = ['clientname' => $clientname, 'configuration' => $configuration];

Expand Down Expand Up @@ -191,6 +205,9 @@ public function webonyxAction(Service $service, LocaleServiceInterface $localeSe
$response->headers->set('Access-Control-Allow-Credentials', 'true');
$response->headers->set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
$response->headers->set('Access-Control-Allow-Headers', 'Origin, Content-Type, X-Auth-Token');

$this->cacheService->save($request, $response);

return $response;
}
}
2 changes: 2 additions & 0 deletions src/DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ public function getConfigTreeBuilder()
->arrayNode('graphql')
->children()
->scalarNode('not_allowed_policy')->info('throw exception = 1, return null = 2')->defaultValue(2)->end()
->booleanNode('output_cache_enabled')->info('enables output cache for graphql responses. It is disabled by default')->defaultValue(false)->end()
->integerNode('output_cache_lifetime')->info('output cache in seconds. Default is 30 seconds')->defaultValue(30)->end()
->end()
->end()
->end()
Expand Down
68 changes: 68 additions & 0 deletions src/Event/GraphQL/Model/OutputCachePreLoadEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php
/**
* Pimcore
*
* This source file is available under two different licenses:
* - GNU General Public License version 3 (GPLv3)
* - Pimcore Enterprise License (PEL)
* Full copyright and license information is available in
* LICENSE.md which is distributed with this source code.
*
* @copyright Copyright (c) Pimcore GmbH (http://www.pimcore.org)
* @license http://www.pimcore.org/license GPLv3 and PEL
*/

namespace Pimcore\Bundle\DataHubBundle\Event\GraphQL\Model;

use Pimcore\Event\Traits\RequestAwareTrait;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\HttpFoundation\Request;

class OutputCachePreLoadEvent extends Event
{
use RequestAwareTrait;

/**
* @var Request
*/
protected $request;

/**
* @var bool
*/
protected $useCache;

/**
* @return Request
*/
public function getRequest()
{
return $this->request;
}

/**
* @return bool
*/
public function isUseCache()
{
return $this->useCache;
}

/**
* @param bool $useCache
*/
public function setUseCache(bool $useCache)
{
$this->useCache = $useCache;
}

/**
* @param Request $request
* @param bool $useCache
*/
public function __construct(Request $request, bool $useCache)
{
$this->request = $request;
$this->useCache = $useCache;
}
}
71 changes: 71 additions & 0 deletions src/Event/GraphQL/Model/OutputCachePreSaveEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php
/**
* Pimcore
*
* This source file is available under two different licenses:
* - GNU General Public License version 3 (GPLv3)
* - Pimcore Enterprise License (PEL)
* Full copyright and license information is available in
* LICENSE.md which is distributed with this source code.
*
* @copyright Copyright (c) Pimcore GmbH (http://www.pimcore.org)
* @license http://www.pimcore.org/license GPLv3 and PEL
*/

namespace Pimcore\Bundle\DataHubBundle\Event\GraphQL\Model;

use Pimcore\Event\Traits\RequestAwareTrait;
use Pimcore\Event\Traits\ResponseAwareTrait;
use Symfony\Component\EventDispatcher\Event;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class OutputCachePreSaveEvent extends Event
{
use RequestAwareTrait;
use ResponseAwareTrait;

/**
* @var Request
*/
protected $request;

/**
* @var Response
*/
protected $response;

/**
* @return Request
*/
public function getRequest()
{
return $this->request;
}

/**
* @return Response
*/
public function getResponse()
{
return $this->response;
}

/**
* @param Response $response
*/
public function setResponse(Response $response)
{
$this->response = $response;
}

/**
* @param Request $request
* @param Response $response
*/
public function __construct(Request $request, Response $response)
{
$this->request = $request;
$this->response = $response;
}
}
40 changes: 40 additions & 0 deletions src/Event/GraphQL/OutputCacheEvents.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

declare(strict_types=1);

/**
* Pimcore
*
* This source file is available under two different licenses:
* - GNU General Public License version 3 (GPLv3)
* - Pimcore Enterprise License (PEL)
* Full copyright and license information is available in
* LICENSE.md which is distributed with this source code.
*
* @copyright Copyright (c) Pimcore GmbH (http://www.pimcore.org)
* @license http://www.pimcore.org/license GPLv3 and PEL
*/

namespace Pimcore\Bundle\DataHubBundle\Event\GraphQL;

final class OutputCacheEvents
{
/**
* Fired to determine if a response should be cached.
*
* @Event("Pimcore\Bundle\DataHubBundle\Event\GraphQL\Model\CachePreLoadEvent")
*
* @var string
*/
const PRE_LOAD = 'pimcore.datahub.graphql.cache.preLoad';

/**
* Fired before the response is written to cache. Can be used to set or purge
* data on the cached response.
*
* @Event("Pimcore\Bundle\DataHubBundle\Event\GraphQL\Model\CachePreSaveEvent")
*
* @var string
*/
const PRE_SAVE = 'pimcore.datahub.graphql.cache.preSave';
}
Loading

0 comments on commit bbf0c3f

Please sign in to comment.