diff --git a/modules/quant_api/src/Client/QuantClient.php b/modules/quant_api/src/Client/QuantClient.php index eb854ce..bfa0b67 100644 --- a/modules/quant_api/src/Client/QuantClient.php +++ b/modules/quant_api/src/Client/QuantClient.php @@ -235,6 +235,7 @@ public function search() { * {@inheritdoc} */ public function purgePath(string $path) : array { + $response = $this->client->post($this->endpoint . '/purge', [ RequestOptions::JSON => [], 'headers' => [ diff --git a/modules/quant_purger/README.md b/modules/quant_purger/README.md index d57b092..baf51f5 100644 --- a/modules/quant_purger/README.md +++ b/modules/quant_purger/README.md @@ -1,7 +1,25 @@ -# Quant cache tag purger +# Quant Purger -Adds a cache tag plugin which listens to Drupal invalidation events in order to -queue Quant updates for related content. +The Quant Purger helps you keep content fresh on your static Quant site +after content updates within Drupal. + +## Purge Plugins + +This module is built on top of the [Purge module suite](https://www.drupal.org/project/purge). + +### Purger Plugin + +Processes cache invalidations based on type: 'everything', 'path' and 'tag'. +The Quant cache will be purged based on these invalidations. + +- *Everything:* A site-wide cache purge, e.g. `/*`. +- *Path:* Purges the given path. +- *Tag:* Purges the given tag. + +### Queuer Plugin + +Adds a cache tag queuer plugin which listens to Drupal invalidation events in +order to queue Quant updates for related content. For example, this allows node edits to trigger the main (`/node`) page to update along with any other pages associated with the node through cache tags (e.g. @@ -14,6 +32,12 @@ To ensure that queued content is processed in a timely manner, you can set up a Quant cron process that is separate from the core cron which just processes the Quant queue. This Quant cron can be run more regularly than the core cron. +### TagsHeader Plugin + +Sets and formats the default response header with hashed cache tags. + +## Documentation + See [Quant Purger documentation](https://docs.quantcdn.io/docs/integrations/drupal/purger) for additional information. diff --git a/modules/quant_purger/config/schema/quant_purger.data_types.schema.yml b/modules/quant_purger/config/schema/quant_purger.data_types.schema.yml index bd915c6..1d372b0 100644 --- a/modules/quant_purger/config/schema/quant_purger.data_types.schema.yml +++ b/modules/quant_purger/config/schema/quant_purger.data_types.schema.yml @@ -1,6 +1,6 @@ -quant_purge_header: +quant_purger_header: type: mapping - label: 'Quant Header' + label: 'Quant Purger Header' mapping: field: type: string diff --git a/modules/quant_purger/config/schema/quant_purger.schema.yml b/modules/quant_purger/config/schema/quant_purger.schema.yml index 64380b1..8676b65 100644 --- a/modules/quant_purger/config/schema/quant_purger.schema.yml +++ b/modules/quant_purger/config/schema/quant_purger.schema.yml @@ -1,28 +1,44 @@ - # Schema for the configuration files of the purge_queuer_url module. quant_purger.settings: type: config_object - label: 'Quant purger settings.' + label: 'Quant Purger Queuer Settings' mapping: - tag_blacklist: - label: 'A list of string tags that will not trigger a queue.' + tag_blocklist: + label: 'A list of tags that will not get queued.' type: sequence translatable: false sequence: type: string label: 'String that cannot be present in the cache tag.' translatable: false - path_blacklist: - label: 'A list of string patterns that will not get queued.' + tag_allowlist: + label: 'A list of tags that can get queued.' + type: sequence + translatable: false + sequence: + type: string + label: 'String that can be present in the cache tag.' + translatable: false + path_blocklist: + label: 'A list of paths that will not get queued.' type: sequence translatable: false sequence: type: string label: 'String that cannot be present in a fully qualified URL.' translatable: false + path_allowlist: + label: 'A list of paths that can not get queued.' + type: sequence + translatable: false + sequence: + type: string + label: 'String that can be present in a fully qualified URL.' + translatable: false +// @todo Why is this under the queuer settings? quant_purger.settings.*: type: config_entity - label: 'Section Purger' + label: 'Quant Purger Settings' mapping: # @@ -34,7 +50,7 @@ quant_purger.settings.*: name: type: string translatable: false - invalidationtype: + invalidation_type: type: string translatable: false diff --git a/modules/quant_purger/quant_purger.info.yml b/modules/quant_purger/quant_purger.info.yml index 5c819d3..6990a86 100644 --- a/modules/quant_purger/quant_purger.info.yml +++ b/modules/quant_purger/quant_purger.info.yml @@ -1,5 +1,5 @@ name: Quant Purger -description: Cache tag purger for Quant. +description: Purge content in Quant based on paths and cache tags. package: Quant type: module diff --git a/modules/quant_purger/quant_purger.services.yml b/modules/quant_purger/quant_purger.services.yml index 01ec8b5..2120f70 100644 --- a/modules/quant_purger/quant_purger.services.yml +++ b/modules/quant_purger/quant_purger.services.yml @@ -11,7 +11,7 @@ services: tags: - { name: http_middleware, priority: 250 } quant_purger.queuer: - class: Drupal\quant_purger\Plugin\Purge\Queuer\QuantPurger + class: Drupal\quant_purger\Plugin\Purge\Queuer\QuantPurgerQueuerInvalidator tags: - { name: cache_tags_invalidator } calls: diff --git a/modules/quant_purger/src/Entity/QuantPurgerSettings.php b/modules/quant_purger/src/Entity/QuantPurgerSettings.php index 627e159..4262b93 100644 --- a/modules/quant_purger/src/Entity/QuantPurgerSettings.php +++ b/modules/quant_purger/src/Entity/QuantPurgerSettings.php @@ -9,8 +9,8 @@ * Defines the QuantPurgerSettings entity. * * @ConfigEntityType( - * id = "quantpurgersettings", - * label = @Translation("QuantCDN"), + * id = "quant_purger_settings", + * label = @Translation("Quant Purger Settings"), * config_prefix = "settings", * static_cache = TRUE, * entity_keys = {"id" = "id"}, @@ -19,7 +19,7 @@ * "label", * "description", * "name", - * "invalidationtype", + * "invalidation_type", * "runtime_measurement", * "timeout", * "connect_timeout", @@ -47,7 +47,7 @@ class QuantPurgerSettings extends PurgerSettingsBase implements PurgerSettingsIn * * @var string */ - public $invalidationtype = 'tag'; + public $invalidation_type = 'tag'; /** * Runtime measurement. diff --git a/modules/quant_purger/src/Form/QuantPurgerConfigForm.php b/modules/quant_purger/src/Form/QuantPurgerConfigForm.php index 3b83036..1b09b22 100644 --- a/modules/quant_purger/src/Form/QuantPurgerConfigForm.php +++ b/modules/quant_purger/src/Form/QuantPurgerConfigForm.php @@ -2,13 +2,21 @@ namespace Drupal\quant_purger\Form; +use Drupal\Core\Config\ConfigFactoryInterface; +use Drupal\Core\Entity\EntityTypeManagerInterface; +use Drupal\Core\Form\FormStateInterface; +use Drupal\purge\Plugin\Purge\Invalidation\InvalidationsServiceInterface; +use Drupal\purge_ui\Form\PurgerConfigFormBase; +use Drupal\quant_purger\Entity\QuantPurgerSettings; +use Symfony\Component\DependencyInjection\ContainerInterface; + /** - * Configuration form for the Quant Purger. + * Form for Quant Purger configuration. */ -class QuantPurgerConfigForm extends QuantPurgerConfigFormBase { +class QuantPurgerConfigForm extends PurgerConfigFormBase { /** - * The token group names this purger supports replacing tokens for. + * The token group lists what this purger supports replacing tokens for. * * @var string[] * @@ -16,4 +24,201 @@ class QuantPurgerConfigForm extends QuantPurgerConfigFormBase { */ protected $tokenGroups = ['invalidation']; + /** + * The service that generates invalidation objects on-demand. + * + * @var \Drupal\purge\Plugin\Purge\Invalidation\InvalidationsServiceInterface + */ + protected $purgeInvalidationFactory; + + /** + * Constructs a base Quant Purger configuration form. + * + * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory + * The factory for configuration objects. + * @param \Drupal\purge\Plugin\Purge\Invalidation\InvalidationsServiceInterface $purge_invalidation_factory + * The invalidation objects factory service. + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager + * The entityTypeManager. + */ + public function __construct(ConfigFactoryInterface $config_factory, InvalidationsServiceInterface $purge_invalidation_factory, EntityTypeManagerInterface $entity_type_manager) { + $this->setConfigFactory($config_factory); + $this->purgeInvalidationFactory = $purge_invalidation_factory; + $this->entityTypeManager = $entity_type_manager; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container) { + return new static( + $container->get('config.factory'), + $container->get('purge.invalidation.factory'), + $container->get('entity_type.manager') + ); + } + + /** + * {@inheritdoc} + */ + protected function getEditableConfigNames() { + return []; + } + + /** + * {@inheritdoc} + */ + public function getFormId() { + return 'quant_purger.configuration_form'; + } + + /** + * {@inheritdoc} + */ + public function buildForm(array $form, FormStateInterface $form_state) { + $settings = QuantPurgerSettings::load($this->getId($form_state)); + $form['tabs'] = ['#type' => 'vertical_tabs', '#weight' => 10]; + $this->buildFormMetadata($form, $form_state, $settings); + $this->buildFormPerformance($form, $form_state, $settings); + return parent::buildForm($form, $form_state); + } + + /** + * Build the 'metadata' section of the form. + * + * @param array $form + * An associative array containing the structure of the form. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + * @param \Drupal\quant_purger\Entity\QuantPurgerSettings $settings + * Configuration entity for the purger being configured. + */ + public function buildFormMetadata(array &$form, FormStateInterface $form_state, QuantPurgerSettings $settings) { + $form['name'] = [ + '#title' => $this->t('Name'), + '#type' => 'textfield', + '#description' => $this->t('Purger to purge QuantCDN content.'), + '#default_value' => $settings->name, + '#required' => TRUE, + ]; + $types = []; + foreach ($this->purgeInvalidationFactory->getPlugins() as $type => $definition) { + $types[$type] = (string) $definition['label']; + } + } + + /** + * Build the 'performance' section of the form. + * + * @param array $form + * An associative array containing the structure of the form. + * @param \Drupal\Core\Form\FormStateInterface $form_state + * The current state of the form. + * @param \Drupal\quant_purger\Entity\QuantPurgerSettings $settings + * Configuration entity for the purger being configured. + */ + public function buildFormPerformance(array &$form, FormStateInterface $form_state, QuantPurgerSettings $settings) { + $form['performance'] = [ + '#type' => 'details', + '#group' => 'tabs', + '#title' => $this->t('Performance'), + ]; + $form['performance']['cooldown_time'] = [ + '#type' => 'number', + '#step' => 0.1, + '#min' => 0.0, + '#max' => 3.0, + '#title' => $this->t('Cooldown time'), + '#default_value' => $settings->cooldown_time, + '#required' => TRUE, + '#description' => $this->t('Number of seconds to wait after a group of HTTP requests so that other purgers get fresh content.'), + ]; + $form['performance']['max_requests'] = [ + '#type' => 'number', + '#step' => 1, + '#min' => 1, + '#max' => 500, + '#title' => $this->t('Maximum requests'), + '#default_value' => $settings->max_requests, + '#required' => TRUE, + '#description' => $this->t("Maximum number of HTTP requests that can be made during Drupal's execution lifetime. Usually PHP resource restraints lower this value dynamically, but can be met at the CLI."), + ]; + $form['performance']['runtime_measurement'] = [ + '#title' => $this->t('Runtime measurement'), + '#type' => 'checkbox', + '#default_value' => $settings->runtime_measurement, + ]; + $form['performance']['runtime_measurement_help'] = [ + '#type' => 'item', + '#states' => [ + 'visible' => [ + ':input[name="runtime_measurement"]' => ['checked' => FALSE], + ], + ], + '#description' => $this->t('When you uncheck this setting, capacity will be based on the sum of both timeouts. By default, capacity will automatically adjust (up and down) based on measured time data.'), + ]; + $form['performance']['timeout'] = [ + '#type' => 'number', + '#step' => 0.1, + '#min' => 0.1, + '#max' => 8.0, + '#title' => $this->t('Timeout'), + '#default_value' => $settings->timeout, + '#required' => TRUE, + '#states' => [ + 'visible' => [ + ':input[name="runtime_measurement"]' => ['checked' => FALSE], + ], + ], + '#description' => $this->t('The timeout of the request in seconds.'), + ]; + $form['performance']['connect_timeout'] = [ + '#type' => 'number', + '#step' => 0.1, + '#min' => 0.1, + '#max' => 4.0, + '#title' => $this->t('Connection timeout'), + '#default_value' => $settings->connect_timeout, + '#required' => TRUE, + '#states' => [ + 'visible' => [ + ':input[name="runtime_measurement"]' => ['checked' => FALSE], + ], + ], + '#description' => $this->t('The number of seconds to wait while trying to connect to a server.'), + ]; + } + + /** + * {@inheritdoc} + */ + public function validateForm(array &$form, FormStateInterface $form_state) { + + // Validate that our timeouts stay between the boundaries purge demands. + $timeout = $form_state->getValue('connect_timeout') + $form_state->getValue('timeout'); + if ($timeout > 10) { + $form_state->setErrorByName('connect_timeout'); + $form_state->setErrorByName('timeout', $this->t('The sum of both timeouts cannot be higher than 10.00 as this would affect performance too negatively.')); + } + elseif ($timeout < 0.4) { + $form_state->setErrorByName('connect_timeout'); + $form_state->setErrorByName('timeout', $this->t('The sum of both timeouts cannot be lower as 0.4 as this can lead to too many failures under real usage conditions.')); + } + } + + /** + * {@inheritdoc} + */ + public function submitFormSuccess(array &$form, FormStateInterface $form_state) { + $settings = QuantPurgerSettings::load($this->getId($form_state)); + + // Iterate the config object and overwrite values found in the form state. + foreach ($settings as $key => $default_value) { + if (!is_null($value = $form_state->getValue($key))) { + $settings->$key = $value; + } + } + $settings->save(); + } + } diff --git a/modules/quant_purger/src/Form/QuantPurgerConfigFormBase.php b/modules/quant_purger/src/Form/QuantPurgerConfigFormBase.php deleted file mode 100644 index ffd21a0..0000000 --- a/modules/quant_purger/src/Form/QuantPurgerConfigFormBase.php +++ /dev/null @@ -1,215 +0,0 @@ -setConfigFactory($config_factory); - $this->purgeInvalidationFactory = $purge_invalidation_factory; - $this->entityTypeManager = $entityTypeManager; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container) { - return new static( - $container->get('config.factory'), - $container->get('purge.invalidation.factory'), - $container->get('entity_type.manager') - ); - } - - /** - * {@inheritdoc} - */ - protected function getEditableConfigNames() { - return []; - } - - /** - * {@inheritdoc} - */ - public function getFormId() { - return 'quant_purger.configuration_form'; - } - - /** - * {@inheritdoc} - */ - public function buildForm(array $form, FormStateInterface $form_state) { - $settings = QuantPurgerSettings::load($this->getId($form_state)); - $form['tabs'] = ['#type' => 'vertical_tabs', '#weight' => 10]; - $this->buildFormMetadata($form, $form_state, $settings); - $this->buildFormPerformance($form, $form_state, $settings); - return parent::buildForm($form, $form_state); - } - - /** - * Build the 'metadata' quant of the form. - * - * @param array $form - * An associative array containing the structure of the form. - * @param \Drupal\Core\Form\FormStateInterface $form_state - * The current state of the form. - * @param \Drupal\quant_purger\Entity\QuantPurgerSettings $settings - * Configuration entity for the purger being configured. - */ - public function buildFormMetadata(array &$form, FormStateInterface $form_state, QuantPurgerSettings $settings) { - $form['name'] = [ - '#title' => $this->t('Name'), - '#type' => 'textfield', - '#description' => $this->t('Quant Purger for Drupal.'), - '#default_value' => $settings->name, - '#required' => TRUE, - ]; - $types = []; - foreach ($this->purgeInvalidationFactory->getPlugins() as $type => $definition) { - $types[$type] = (string) $definition['label']; - } - } - - /** - * Build the 'performance' section of the form. - * - * @param array $form - * An associative array containing the structure of the form. - * @param \Drupal\Core\Form\FormStateInterface $form_state - * The current state of the form. - * @param \Drupal\quant_purger\Entity\QuantPurgerSettings $settings - * Configuration entity for the purger being configured. - */ - public function buildFormPerformance(array &$form, FormStateInterface $form_state, QuantPurgerSettings $settings) { - $form['performance'] = [ - '#type' => 'details', - '#group' => 'tabs', - '#title' => $this->t('Performance'), - ]; - $form['performance']['cooldown_time'] = [ - '#type' => 'number', - '#step' => 0.1, - '#min' => 0.0, - '#max' => 3.0, - '#title' => $this->t('Cooldown time'), - '#default_value' => $settings->cooldown_time, - '#required' => TRUE, - '#description' => $this->t('Number of seconds to wait after a group of HTTP requests so that other purgers get fresh content.'), - ]; - $form['performance']['max_requests'] = [ - '#type' => 'number', - '#step' => 1, - '#min' => 1, - '#max' => 500, - '#title' => $this->t('Maximum requests'), - '#default_value' => $settings->max_requests, - '#required' => TRUE, - '#description' => $this->t("Maximum number of HTTP requests that can be made during Drupal's execution lifetime. Usually PHP resource restraints lower this value dynamically, but can be met at the CLI."), - ]; - $form['performance']['runtime_measurement'] = [ - '#title' => $this->t('Runtime measurement'), - '#type' => 'checkbox', - '#default_value' => $settings->runtime_measurement, - ]; - $form['performance']['runtime_measurement_help'] = [ - '#type' => 'item', - '#states' => [ - 'visible' => [ - ':input[name="runtime_measurement"]' => ['checked' => FALSE], - ], - ], - '#description' => $this->t('When you uncheck this setting, capacity will be based on the sum of both timeouts. By default, capacity will automatically adjust (up and down) based on measured time data.'), - ]; - $form['performance']['timeout'] = [ - '#type' => 'number', - '#step' => 0.1, - '#min' => 0.1, - '#max' => 8.0, - '#title' => $this->t('Timeout'), - '#default_value' => $settings->timeout, - '#required' => TRUE, - '#states' => [ - 'visible' => [ - ':input[name="runtime_measurement"]' => ['checked' => FALSE], - ], - ], - '#description' => $this->t('The timeout of the request in seconds.'), - ]; - $form['performance']['connect_timeout'] = [ - '#type' => 'number', - '#step' => 0.1, - '#min' => 0.1, - '#max' => 4.0, - '#title' => $this->t('Connection timeout'), - '#default_value' => $settings->connect_timeout, - '#required' => TRUE, - '#states' => [ - 'visible' => [ - ':input[name="runtime_measurement"]' => ['checked' => FALSE], - ], - ], - '#description' => $this->t('The number of seconds to wait while trying to connect to a server.'), - ]; - } - - /** - * {@inheritdoc} - */ - public function validateForm(array &$form, FormStateInterface $form_state) { - - // Validate that our timeouts stay between the boundaries purge demands. - $timeout = $form_state->getValue('connect_timeout') + $form_state->getValue('timeout'); - if ($timeout > 10) { - $form_state->setErrorByName('connect_timeout'); - $form_state->setErrorByName('timeout', $this->t('The sum of both timeouts cannot be higher than 10.00 as this would affect performance too negatively.')); - } - elseif ($timeout < 0.4) { - $form_state->setErrorByName('connect_timeout'); - $form_state->setErrorByName('timeout', $this->t('The sum of both timeouts cannot be lower as 0.4 as this can lead to too many failures under real usage conditions.')); - } - } - - /** - * {@inheritdoc} - */ - public function submitFormSuccess(array &$form, FormStateInterface $form_state) { - $settings = QuantPurgerSettings::load($this->getId($form_state)); - - // Iterate the config object and overwrite values found in the form state. - foreach ($settings as $key => $default_value) { - if (!is_null($value = $form_state->getValue($key))) { - $settings->$key = $value; - } - } - $settings->save(); - } - -} diff --git a/modules/quant_purger/src/Form/QuantPurgerQueuerConfigForm.php b/modules/quant_purger/src/Form/QuantPurgerQueuerConfigForm.php index 1cd3ff1..f16cacd 100644 --- a/modules/quant_purger/src/Form/QuantPurgerQueuerConfigForm.php +++ b/modules/quant_purger/src/Form/QuantPurgerQueuerConfigForm.php @@ -16,6 +16,8 @@ class QuantPurgerQueuerConfigForm extends QueuerConfigFormBase { * {@inheritdoc} */ protected function getEditableConfigNames() { + // @todo There are multiple purger config forms. Should this be + // 'quant_purger.queuer_settings'? Requires an update hook. return ['quant_purger.settings']; } @@ -23,8 +25,8 @@ protected function getEditableConfigNames() { * {@inheritdoc} */ public function getFormId() { - // @todo This should be 'quant_purger.queuer_config_form' which will - // require an update hook. + // @todo There are multiple purger config forms. Should this be + // 'quant_purger.queuer_config_form'? Requires an update hook. return 'quant_purger.configuration_form'; } diff --git a/modules/quant_purger/src/Plugin/Purge/Purger/QuantPurger.php b/modules/quant_purger/src/Plugin/Purge/Purger/QuantPurger.php index aabc227..3df3c4e 100644 --- a/modules/quant_purger/src/Plugin/Purge/Purger/QuantPurger.php +++ b/modules/quant_purger/src/Plugin/Purge/Purger/QuantPurger.php @@ -2,9 +2,13 @@ namespace Drupal\quant_purger\Plugin\Purge\Purger; -use Drupal\purge\Plugin\Purge\Purger\PurgerInterface; +use Drupal\Core\Utility\Token; use Drupal\purge\Plugin\Purge\Invalidation\InvalidationInterface; -use Drupal\quant_purger\Entity\Hash; +use Drupal\purge\Plugin\Purge\Purger\PurgerBase; +use Drupal\purge\Plugin\Purge\Purger\PurgerInterface; +use Drupal\quant_purger\Entity\QuantPurgerSettings; +use GuzzleHttp\ClientInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; /** * Quant Purger. @@ -14,25 +18,115 @@ * label = @Translation("Quant Purger"), * configform = "\Drupal\quant_purger\Form\QuantPurgerConfigForm", * cooldown_time = 0.2, - * description = @Translation("Purger that sends invalidation expressions from your Drupal instance to the QuantCDN platform."), + * description = @Translation("Purger that sends invalidations from your Drupal site to the QuantCDN platform."), * multi_instance = FALSE, * types = {"everything", "path", "tag"}, * ) */ -class QuantPurger extends QuantPurgerBase implements PurgerInterface { +class QuantPurger extends PurgerBase implements PurgerInterface { + + /** + * The Guzzle HTTP client. + * + * @var \GuzzleHttp\Client + */ + protected $client; + + /** + * The settings entity holding all configuration. + * + * @var \Drupal\quant_purger\Entity\QuantPurgerSettings + */ + protected $settings; + + /** + * The token service. + * + * @var \Drupal\Core\Utility\Token + */ + protected $token; + + /** + * Constructs the Quant Purger. + * + * @param array $configuration + * A configuration array containing information about the plugin instance. + * @param string $plugin_id + * The plugin_id for the plugin instance. + * @param mixed $plugin_definition + * The plugin implementation definition. + * @param \GuzzleHttp\ClientInterface $http_client + * An HTTP client that can perform remote requests. + * @param \Drupal\Core\Utility\Token $token + * The token service. + */ + public function __construct(array $configuration, $plugin_id, $plugin_definition, ClientInterface $http_client, Token $token) { + parent::__construct($configuration, $plugin_id, $plugin_definition); + $this->settings = QuantPurgerSettings::load($this->getId()); + // Note: We use the Quant HTTP client rather than the generic Guzzle client. + $this->client = \Drupal::service('quant_api.client'); + $this->token = $token; + } + + /** + * {@inheritdoc} + */ + public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { + return new static( + $configuration, + $plugin_id, + $plugin_definition, + $container->get('http_client'), + $container->get('token') + ); + } + + /** + * {@inheritdoc} + */ + public function getLabel() { + if ($this->settings->name) { + return $this->settings->name; + } + else { + return parent::getLabel(); + } + } + + /** + * {@inheritdoc} + */ + public function getTypes() { + // @todo Add url? + return [ + 'everything', + 'path', + 'tag', + ]; + } + + /** + * {@inheritdoc} + */ + public function delete() { + QuantPurgerSettings::load($this->getId())->delete(); + } /** * {@inheritdoc} */ public function invalidate(array $invalidations) { + // The processed data may include 'everything', 'paths' and/or 'tags'. $processed = $this->processInvalidations($invalidations); - if ($processed['everything']) { + // The 'everything' invaldiation type takes precedence. + if (!empty($processed['everything'])) { $this->invalidateEverything($invalidations); return; } + // Otherwise handle any paths and tags. if (!empty($processed['paths'])) { $this->invalidatePaths($processed['paths']); } @@ -43,12 +137,58 @@ public function invalidate(array $invalidations) { } + /** + * Process invalidations based on type. + * + * @param array $invalidations + * The array of Invalidation items to process. + * + * @return array + * The processed array with 'everything', 'paths' and 'tags' keys. + */ + public function processInvalidations(array $invalidations) { + $everything = FALSE; + $paths = []; + $tags = []; + + foreach ($invalidations as $invalidation) { + + if ($invalidation->getType() == 'everything') { + // 'Everything' takes precedence and will issue a site-wide purge. + $invalidation->setState(InvalidationInterface::PROCESSING); + $everything = TRUE; + $paths = []; + $tags = []; + break; + } + elseif ($invalidation->getType() == 'path') { + $invalidation->setState(InvalidationInterface::PROCESSING); + $paths[] = $invalidation; + } + elseif ($invalidation->getType() == 'tag') { + $invalidation->setState(InvalidationInterface::PROCESSING); + $tags[] = $invalidation; + } + else { + $invalidation->setState(InvalidationInterface::NOT_SUPPORTED); + $this->logger()->warning('Invalidation type not supported: @type', ['@type' => $invalidation->getType()]); + } + } + + $processed = [ + 'everything' => $everything, + 'paths' => $paths, + 'tags' => $tags, + ]; + + return $processed; + } + /** * Invalidate with the path '/*' to purge the entire project cache. * * @param array $invalidations - * This takes in an array of Invalidation objects, processing them all in a - * loop, generally from the purge queue. + * Array of Invalidation objects to process. */ public function invalidateEverything(array $invalidations) { @@ -60,7 +200,7 @@ public function invalidateEverything(array $invalidations) { } } catch (\Exception $e) { - $this->logger()->notice('Error attempting to purge entire cache: ' . $e->getMessage()); + $this->logger()->error('Error attempting to purge entire cache: @message', ['@message' => $e->getMessage()]); error_log($e->getMessage()); foreach ($invalidations as $invalidation) { $invalidation->setState(InvalidationInterface::FAILED); @@ -79,12 +219,12 @@ public function invalidatePaths(array $invalidations) { foreach ($invalidations as $invalidation) { try { $path = '/' . $invalidation->getExpression(); - $this->logger()->debug('[path] Purging path: ' . $path); + $this->logger()->debug('[path] Purging path: @path', ['@path' => $path]); $this->purgePath($path); $invalidation->setState(InvalidationInterface::SUCCEEDED); } catch (\Exception $e) { - $this->logger()->notice('Error attempting to purge cache path: ' . $e->getMessage()); + $this->logger()->error('Error attempting to purge paths: @message', ['@message' => $e->getMessage()]); error_log($e->getMessage()); $invalidation->setState(InvalidationInterface::FAILED); } @@ -99,7 +239,8 @@ public function invalidatePaths(array $invalidations) { */ public function invalidateTags(array $invalidations) { try { - $this->logger()->debug('[tags] Purging tags: ' . implode(' ', $invalidations)); + // Log tags prior to hashing. + $this->logger()->debug('[tags] Purging tags: @tags', ['@tags' => implode(' ', $invalidations)]); $tags = []; foreach ($invalidations as $invalidation) { @@ -110,10 +251,64 @@ public function invalidateTags(array $invalidations) { $invalidation->setState(InvalidationInterface::SUCCEEDED); } catch (\Exception $e) { - $this->logger()->notice('Error attempting to purge cache path: ' . $e->getMessage()); + $this->logger()->error('Error attempting to purge tags: @message', ['@message' => $e->getMessage()]); error_log($e->getMessage()); $invalidation->setState(InvalidationInterface::FAILED); } } + /** + * {@inheritdoc} + */ + public function getCooldownTime() { + return $this->settings->cooldown_time; + } + + /** + * {@inheritdoc} + */ + public function getIdealConditionsLimit() { + return $this->settings->max_requests; + } + + /** + * {@inheritdoc} + */ + public function getTimeHint() { + // When runtime measurement is enabled, we just use the base implementation. + if ($this->settings->runtime_measurement) { + return parent::getTimeHint(); + } + // Theoretically connection timeouts and general timeouts can add up, so + // we add up our assumption of the worst possible time it takes as well. + return $this->settings->connect_timeout + $this->settings->timeout; + } + + /** + * {@inheritdoc} + */ + public function hasRuntimeMeasurement() { + return (bool) $this->settings->runtime_measurement; + } + + /** + * Sends a path-based purge request to the Quant API. + * + * @param string $path + * The path to purge. + */ + public function purgePath(string $path) { + $this->client->purgePath($path); + } + + /** + * Sends a tags-based purge request to the Quant API. + * + * @param array $tags + * The array of tags to purge. + */ + public function purgeTags(array $tags) { + $this->client->purgeTags($tags); + } + } diff --git a/modules/quant_purger/src/Plugin/Purge/Purger/QuantPurgerBase.php b/modules/quant_purger/src/Plugin/Purge/Purger/QuantPurgerBase.php deleted file mode 100644 index bba5f4f..0000000 --- a/modules/quant_purger/src/Plugin/Purge/Purger/QuantPurgerBase.php +++ /dev/null @@ -1,204 +0,0 @@ -settings = QuantPurgerSettings::load($this->getId()); - // Note: We use the Quant HTTP client rather than the generic Guzzle client. - $this->client = \Drupal::service('quant_api.client'); - $this->token = $token; - } - - /** - * {@inheritdoc} - */ - public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) { - return new static( - $configuration, - $plugin_id, - $plugin_definition, - $container->get('http_client'), - $container->get('token') - ); - } - - /** - * {@inheritdoc} - */ - public function delete() { - QuantPurgerSettings::load($this->getId())->delete(); - } - - /** - * {@inheritdoc} - */ - public function getCooldownTime() { - return $this->settings->cooldown_time; - } - - /** - * {@inheritdoc} - */ - public function getIdealConditionsLimit() { - return $this->settings->max_requests; - } - - /** - * {@inheritdoc} - */ - public function getLabel() { - if ($this->settings->name) { - return $this->settings->name; - } - else { - return parent::getLabel(); - } - } - - /** - * Process invalidations based on type. - * - * @param array $invalidations - * The invalidations array. - * - * @return array - * The processed array with 'everything', 'paths' and 'tags' keys. - */ - public function processInvalidations(array $invalidations) { - $everything = FALSE; - $paths = []; - $tags = []; - - foreach ($invalidations as $invalidation) { - - if ($invalidation->getType() == 'everything') { - // 'Everything' trumps everything and will issue a site-wide purge. - $invalidation->setState(InvalidationInterface::PROCESSING); - $everything = TRUE; - $paths = []; - $tags = []; - break; - } - elseif ($invalidation->getType() == 'path') { - $invalidation->setState(InvalidationInterface::PROCESSING); - $paths[] = $invalidation; - } - elseif ($invalidation->getType() == 'tag') { - $invalidation->setState(InvalidationInterface::PROCESSING); - $tags[] = $invalidation; - } - else { - $invalidation->setState(InvalidationInterface::NOT_SUPPORTED); - } - } - - $processed = [ - 'everything' => $everything, - 'paths' => $paths, - 'tags' => $tags, - ]; - - return $processed; - } - - /** - * {@inheritdoc} - */ - public function getTimeHint() { - // When runtime measurement is enabled, we just use the base implementation. - if ($this->settings->runtime_measurement) { - return parent::getTimeHint(); - } - // Theoretically connection timeouts and general timeouts can add up, so - // we add up our assumption of the worst possible time it takes as well. - return $this->settings->connect_timeout + $this->settings->timeout; - } - - /** - * {@inheritdoc} - */ - public function getTypes() { - return [ - 'everything', - 'path', - 'tag', - ]; - } - - /** - * {@inheritdoc} - */ - public function hasRuntimeMeasurement() { - return (bool) $this->settings->runtime_measurement; - } - - /** - * Sends a path-based purge request to the Quant API. - * - * @param string $path - * The path to purge. - */ - public function purgePath(string $path) { - $this->client->purgePath($path); - } - - /** - * Sends a tags-based purge request to the Quant API. - * - * @param array $tags - * The array of tags to purge. - */ - public function purgeTags(array $tags) { - $this->client->purgeTags($tags); - } - -} diff --git a/modules/quant_purger/src/Plugin/Purge/Queuer/QuantPurgerQueuer.php b/modules/quant_purger/src/Plugin/Purge/Queuer/QuantPurgerQueuer.php index 6aef9c3..771201c 100644 --- a/modules/quant_purger/src/Plugin/Purge/Queuer/QuantPurgerQueuer.php +++ b/modules/quant_purger/src/Plugin/Purge/Queuer/QuantPurgerQueuer.php @@ -9,13 +9,12 @@ * Quant Purger Queuer. * * @PurgeQueuer( - * id = "quant", + * id = "quant_purger_queuer", * label = @Translation("Quant Purger Queuer"), - * description = @Translation("Queue impacted content updates."), + * description = @Translation("Update Quant queue based on content updates."), * enable_by_default = true, * types = {"tag"}, * configform = "\Drupal\quant_purger\Form\QuantPurgerQueuerConfigForm", * ) */ -// @todo Change id to 'quant_purger_queuer' which requires an update hook. class QuantPurgerQueuer extends QueuerBase implements QueuerInterface {} diff --git a/modules/quant_purger/src/Plugin/Purge/Queuer/QuantPurger.php b/modules/quant_purger/src/Plugin/Purge/Queuer/QuantPurgerQueuerInvalidator.php similarity index 96% rename from modules/quant_purger/src/Plugin/Purge/Queuer/QuantPurger.php rename to modules/quant_purger/src/Plugin/Purge/Queuer/QuantPurgerQueuerInvalidator.php index 41f9564..590b886 100644 --- a/modules/quant_purger/src/Plugin/Purge/Queuer/QuantPurger.php +++ b/modules/quant_purger/src/Plugin/Purge/Queuer/QuantPurgerQueuerInvalidator.php @@ -10,7 +10,7 @@ /** * Queues URLs with Quant when Drupal invalidates cache tags. */ -class QuantPurger implements CacheTagsInvalidatorInterface, ContainerAwareInterface { +class QuantPurgerQueuerInvalidator implements CacheTagsInvalidatorInterface, ContainerAwareInterface { use ContainerAwareTrait; /** diff --git a/modules/quant_purger/src/Plugin/Purge/TagsHeader/QuantPurgerTagsHeader.php b/modules/quant_purger/src/Plugin/Purge/TagsHeader/QuantPurgerTagsHeader.php index e8e2027..0f462c6 100644 --- a/modules/quant_purger/src/Plugin/Purge/TagsHeader/QuantPurgerTagsHeader.php +++ b/modules/quant_purger/src/Plugin/Purge/TagsHeader/QuantPurgerTagsHeader.php @@ -20,7 +20,7 @@ class QuantPurgerTagsHeader extends TagsHeaderBase implements TagsHeaderInterfac * {@inheritdoc} */ public function getValue(array $tags) { - return new CacheTagsHeaderValue($tags, Hash::cacheTags($tags)); + return new QuantPurgerTagsHeaderValue($tags, Hash::cacheTags($tags)); } } diff --git a/modules/quant_purger/src/Plugin/Purge/TagsHeader/CacheTagsHeaderValue.php b/modules/quant_purger/src/Plugin/Purge/TagsHeader/QuantPurgerTagsHeaderValue.php similarity index 97% rename from modules/quant_purger/src/Plugin/Purge/TagsHeader/CacheTagsHeaderValue.php rename to modules/quant_purger/src/Plugin/Purge/TagsHeader/QuantPurgerTagsHeaderValue.php index 229f93d..3fbf0e9 100644 --- a/modules/quant_purger/src/Plugin/Purge/TagsHeader/CacheTagsHeaderValue.php +++ b/modules/quant_purger/src/Plugin/Purge/TagsHeader/QuantPurgerTagsHeaderValue.php @@ -5,7 +5,7 @@ /** * Provides simple value object for cache tag headers. */ -class CacheTagsHeaderValue { +class QuantPurgerTagsHeaderValue { /** * String: separation character used.