From f48040e47b7c49389a36f39c10b07ebc7ce46ea9 Mon Sep 17 00:00:00 2001 From: Stuart Rowlands Date: Thu, 18 Apr 2024 16:12:49 +1000 Subject: [PATCH 01/16] Emit cache-keys. --- .../config/schema/quant_purger.schema.yml | 2 +- modules/quant_purger/src/Entity/Hash.php | 70 +++++++++++++++++++ .../Purge/TagsHeader/CacheTagsHeaderValue.php | 68 ++++++++++++++++++ .../Purge/TagsHeader/QuantCacheTagsHeader.php | 26 +++++++ 4 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 modules/quant_purger/src/Entity/Hash.php create mode 100644 modules/quant_purger/src/Plugin/Purge/TagsHeader/CacheTagsHeaderValue.php create mode 100644 modules/quant_purger/src/Plugin/Purge/TagsHeader/QuantCacheTagsHeader.php diff --git a/modules/quant_purger/config/schema/quant_purger.schema.yml b/modules/quant_purger/config/schema/quant_purger.schema.yml index 887cf0d5..94c10443 100644 --- a/modules/quant_purger/config/schema/quant_purger.schema.yml +++ b/modules/quant_purger/config/schema/quant_purger.schema.yml @@ -9,7 +9,7 @@ quant_purger.settings: translatable: false sequence: type: string - label: 'String that cannot be present in the ccache tag.' + 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.' diff --git a/modules/quant_purger/src/Entity/Hash.php b/modules/quant_purger/src/Entity/Hash.php new file mode 100644 index 00000000..db35698f --- /dev/null +++ b/modules/quant_purger/src/Entity/Hash.php @@ -0,0 +1,70 @@ + 4) { + $hashes[] = self::hashInput($tag, 4); + } + else { + $hashes[] = $tag; + } + } + return $hashes; + } + + /** + * Create a unique hash that identifies this site. + * + * @param string $site_name + * The identifier of the site on Acquia Cloud. + * @param string $site_path + * The path of the site, e.g. 'site/default' or 'site/database_a'. + * + * @return string + * Cryptographic hash that's long enough to be unique. + */ + public static function siteIdentifier($site_name, $site_path) { + return self::hashInput($site_name . $site_path, 16); + } + +} diff --git a/modules/quant_purger/src/Plugin/Purge/TagsHeader/CacheTagsHeaderValue.php b/modules/quant_purger/src/Plugin/Purge/TagsHeader/CacheTagsHeaderValue.php new file mode 100644 index 00000000..229f93dd --- /dev/null +++ b/modules/quant_purger/src/Plugin/Purge/TagsHeader/CacheTagsHeaderValue.php @@ -0,0 +1,68 @@ +tags = array_unique($tags); + $this->tagsHashed = array_unique($tags_hashed); + } + + /** + * Generate the header value for a cache tags header. + * + * @return string + * String representation of the cache tags for use on headers. + */ + public function __toString() { + return implode(self::SEPARATOR, $this->tagsHashed); + } + + /** + * Get an associative array mapping keys. + * + * @return array + * Associative mapping original and hashed cache tags. + */ + public function getTagsMap() { + return array_combine($this->tags, $this->tagsHashed); + } + +} diff --git a/modules/quant_purger/src/Plugin/Purge/TagsHeader/QuantCacheTagsHeader.php b/modules/quant_purger/src/Plugin/Purge/TagsHeader/QuantCacheTagsHeader.php new file mode 100644 index 00000000..7fc09a38 --- /dev/null +++ b/modules/quant_purger/src/Plugin/Purge/TagsHeader/QuantCacheTagsHeader.php @@ -0,0 +1,26 @@ + Date: Fri, 19 Apr 2024 14:20:50 +1000 Subject: [PATCH 02/16] Functional tag/path/everything purger. --- modules/quant_api/src/Client/QuantClient.php | 38 ++++ .../src/Client/QuantClientInterface.php | 28 +++ .../schema/quant_purger.data_types.schema.yml | 10 + .../config/schema/quant_purger.schema.yml | 44 ++++ .../src/Entity/QuantPurgeSettings.php | 115 ++++++++++ .../quant_purger/src/Form/QuantPurgeForm.php | 19 ++ .../src/Form/QuantPurgeFormBase.php | 215 ++++++++++++++++++ .../src/Plugin/Purge/Purger/.DS_Store | Bin 0 -> 6148 bytes .../src/Plugin/Purge/Purger/QuantPurge.php | 124 ++++++++++ .../Plugin/Purge/Purger/QuantPurgeBase.php | 210 +++++++++++++++++ 10 files changed, 803 insertions(+) create mode 100644 modules/quant_purger/config/schema/quant_purger.data_types.schema.yml create mode 100644 modules/quant_purger/src/Entity/QuantPurgeSettings.php create mode 100644 modules/quant_purger/src/Form/QuantPurgeForm.php create mode 100644 modules/quant_purger/src/Form/QuantPurgeFormBase.php create mode 100644 modules/quant_purger/src/Plugin/Purge/Purger/.DS_Store create mode 100644 modules/quant_purger/src/Plugin/Purge/Purger/QuantPurge.php create mode 100644 modules/quant_purger/src/Plugin/Purge/Purger/QuantPurgeBase.php diff --git a/modules/quant_api/src/Client/QuantClient.php b/modules/quant_api/src/Client/QuantClient.php index 1f03c168..06ab5ea6 100644 --- a/modules/quant_api/src/Client/QuantClient.php +++ b/modules/quant_api/src/Client/QuantClient.php @@ -231,6 +231,44 @@ public function search() { return FALSE; } + /** + * {@inheritdoc} + */ + public function purgePath(string $path) : array { + $response = $this->client->post($this->endpoint . '/purge', [ + RequestOptions::JSON => [], + 'headers' => [ + 'Quant-Customer' => $this->username, + 'Quant-Project' => $this->project, + 'Quant-Token' => $this->token, + 'Quant-Url' => $path, + ], + 'verify' => $this->tlsDisabled ? FALSE : TRUE, + ]); + + return json_decode($response->getBody(), TRUE); + } + + /** + * {@inheritdoc} + */ + public function purgeTags(array $tags) : array { + + $response = $this->client->post($this->endpoint . '/purge', [ + RequestOptions::JSON => [], + 'headers' => [ + 'Quant-Customer' => $this->username, + 'Quant-Project' => $this->project, + 'Quant-Token' => $this->token, + 'Cache-Keys' => implode(' ', $tags), + ], + 'verify' => $this->tlsDisabled ? FALSE : TRUE, + ]); + + return json_decode($response->getBody(), TRUE); + + } + /** * {@inheritdoc} */ diff --git a/modules/quant_api/src/Client/QuantClientInterface.php b/modules/quant_api/src/Client/QuantClientInterface.php index cc7a40fc..e2354242 100644 --- a/modules/quant_api/src/Client/QuantClientInterface.php +++ b/modules/quant_api/src/Client/QuantClientInterface.php @@ -47,6 +47,34 @@ public function search(); */ public function send(array $data) : array; + /** + * Sends a purge payload (path based) to the API. + * + * @param string $path + * The path to purge. + * + * @return array + * Return array of response data. + * + * @throws \Drupal\quant_api\Exception\InvalidPayload + * @throws \Drupal\quant_api\Exception\InvalidResposne + */ + public function purgePath(string $path) : array; + + /** + * Sends a purge payload (tags based) to the API. + * + * @param array $tags + * The array of tags to purge. + * + * @return array + * Return array of response data. + * + * @throws \Drupal\quant_api\Exception\InvalidPayload + * @throws \Drupal\quant_api\Exception\InvalidResposne + */ + public function purgeTags(array $tags) : array; + /** * Send a file to the API. * 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 new file mode 100644 index 00000000..bd915c6c --- /dev/null +++ b/modules/quant_purger/config/schema/quant_purger.data_types.schema.yml @@ -0,0 +1,10 @@ +quant_purge_header: + type: mapping + label: 'Quant Header' + mapping: + field: + type: string + translatable: false + value: + type: string + translatable: false diff --git a/modules/quant_purger/config/schema/quant_purger.schema.yml b/modules/quant_purger/config/schema/quant_purger.schema.yml index 94c10443..64380b15 100644 --- a/modules/quant_purger/config/schema/quant_purger.schema.yml +++ b/modules/quant_purger/config/schema/quant_purger.schema.yml @@ -19,3 +19,47 @@ quant_purger.settings: type: string label: 'String that cannot be present in a fully qualified URL.' translatable: false + +quant_purger.settings.*: + type: config_entity + label: 'Section Purger' + mapping: + + # + # Instance metadata: + # + id: + type: string + translatable: false + name: + type: string + translatable: false + invalidationtype: + type: string + translatable: false + + # + # Performance settings: + # + runtime_measurement: + type: boolean + translatable: false + timeout: + type: float + translatable: false + connect_timeout: + type: float + translatable: false + cooldown_time: + type: float + translatable: false + max_requests: + type: integer + translatable: false + + # + # Success resolution: + # + http_errors: + type: boolean + translatable: false diff --git a/modules/quant_purger/src/Entity/QuantPurgeSettings.php b/modules/quant_purger/src/Entity/QuantPurgeSettings.php new file mode 100644 index 00000000..47c09bc5 --- /dev/null +++ b/modules/quant_purger/src/Entity/QuantPurgeSettings.php @@ -0,0 +1,115 @@ +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 = QuantPurgeSettings::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\QuantPurgeSettings $settings + * Configuration entity for the purger being configured. + */ + public function buildFormMetadata(array &$form, FormStateInterface $form_state, QuantPurgeSettings $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\QuantPurgeSettingsPurgeSettings $settings + * Configuration entity for the purger being configured. + */ + public function buildFormPerformance(array &$form, FormStateInterface $form_state, QuantPurgeSettings $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 = QuantPurgeSettings::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/Plugin/Purge/Purger/.DS_Store b/modules/quant_purger/src/Plugin/Purge/Purger/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0processInvalidations($invalidations); + + if ($filtered_validations['everything']) { + $this->invalidateEverything($invalidations); + return; + } + + if (!empty($filtered_validations['tags'])) { + $this->invalidateTags($filtered_validations['tags']); + } + + if (!empty($filtered_validations['paths'])) { + $this->invalidatePaths($filtered_validations['paths']); + } + } + + + /** + * {@inheritdoc} + */ + public function invalidateTags(array $invalidations) { + $tags = []; + foreach ($invalidations as $invalidation) { + $tags[] = Hash::cacheTags([$invalidation->getExpression()])[0]; + } + + try { + $this->logger()->debug('[tags] Purging tags: ' . implode(' ', $tags)); + $this->purgeTags($tags); + $invalidation->setState(InvalidationInterface::SUCCEEDED); + } + catch (\Exception $e) { + $this->logger()->notice('Error attempting to purge cache path: ' . $e->getMessage()); + error_log($e->getMessage()); + $invalidation->setState(InvalidationInterface::FAILED); + } + + + + + } + + /** + * InvalidatePaths + * + * This will invalidate path based invalidations in a loop. + * + * @param array $invalidations + * This takes in an array of Invalidation, processing them all in a loop, + * generally from the purge queue. + */ + public function invalidatePaths(array $invalidations) { + + foreach ($invalidations as $invalidation) { + try { + $path = '/' . $invalidation->getExpression(); + $this->logger()->debug('[path] Purging path invalidation: ' . $path); + $this->purgePath($path); + $invalidation->setState(InvalidationInterface::SUCCEEDED); + } + catch (\Exception $e) { + $this->logger()->notice('Error attempting to purge cache path: ' . $e->getMessage()); + error_log($e->getMessage()); + $invalidation->setState(InvalidationInterface::FAILED); + } + } + } + + /** + * InvalidateEverything. + * + * This will invalidate with the path '/*' to purge the entire project cache. + * + * @param array $invalidations + * This takes in an array of Invalidation, processing them all in a loop, + * generally from the purge queue. + */ + public function invalidateEverything(array $invalidations) { + + try { + $this->logger()->debug('[everything] Purging entire site cache (/*)'); + $this->purgePath('/*'); + foreach ($invalidations as $invalidation) { + $invalidation->setState(InvalidationInterface::SUCCEEDED); + } + } + catch (\Exception $e) { + $this->logger()->notice('Error attempting to purge entire cache: ' . $e->getMessage()); + error_log($e->getMessage()); + foreach ($invalidations as $invalidation) { + $invalidation->setState(InvalidationInterface::FAILED); + } + } + } + +} diff --git a/modules/quant_purger/src/Plugin/Purge/Purger/QuantPurgeBase.php b/modules/quant_purger/src/Plugin/Purge/Purger/QuantPurgeBase.php new file mode 100644 index 00000000..e9aaf743 --- /dev/null +++ b/modules/quant_purger/src/Plugin/Purge/Purger/QuantPurgeBase.php @@ -0,0 +1,210 @@ +settings = QuantPurgeSettings::load($this->getId()); + // Note: We actually 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() { + QuantPurgeSettings::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(); + } + } + + /** + * Returns array of tags and paths to purge in the provided array. + * + * @param array $invalidations + * The invalidations array. + * + * @return array + */ + public function processInvalidations(array $invalidations) { + $filtered_tags = []; + $filtered_paths = []; + + $everything = false; + + foreach($invalidations as $invalidation) { + + if ($invalidation->getType() == 'tag') { + $invalidation->setState(InvalidationInterface::PROCESSING); + $filtered_tags[] = $invalidation; + } + elseif ($invalidation->getType() == 'path') { + // @todo: dunno what this looks like + $invalidation->setState(InvalidationInterface::PROCESSING); + $filtered_paths[] = $invalidation; + } + elseif ($invalidation->getType() == 'everything') { + // 'Everything' trumps everything and will issue a site-wide purge. + $invalidation->setState(InvalidationInterface::PROCESSING); + $everything = true; + $filtered_paths = []; + $filtered_tags = []; + break; + } + else { + $invalidation->setState(InvalidationInterface::NOT_SUPPORTED); + } + } + + $filtered_array = [ + 'everything' => $everything, + 'tags' => $filtered_tags, + 'paths' => $filtered_paths, + ]; + + return $filtered_array; + } + + /** + * {@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 [ + "tag", + "everything", + "path", + ]; + } + + /** + * {@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($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($tags) { + $this->client->purgeTags($tags); + } + +} From e9c80a745187d102277f98fa61bb189ce0f4f77b Mon Sep 17 00:00:00 2001 From: Stuart Rowlands Date: Fri, 19 Apr 2024 17:50:57 +1000 Subject: [PATCH 03/16] Rename header. --- .../src/Plugin/Purge/TagsHeader/QuantCacheTagsHeader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/quant_purger/src/Plugin/Purge/TagsHeader/QuantCacheTagsHeader.php b/modules/quant_purger/src/Plugin/Purge/TagsHeader/QuantCacheTagsHeader.php index 7fc09a38..b8083bad 100644 --- a/modules/quant_purger/src/Plugin/Purge/TagsHeader/QuantCacheTagsHeader.php +++ b/modules/quant_purger/src/Plugin/Purge/TagsHeader/QuantCacheTagsHeader.php @@ -11,7 +11,7 @@ * * @PurgeTagsHeader( * id = "quant_tagsheader", - * header_name = "Cache-Tags", + * header_name = "Cache-Keys", * ) */ class QuantCacheTagsHeader extends TagsHeaderBase implements TagsHeaderInterface { From 6b5d3e6f87cde132acf136c0cec37ab971d918e2 Mon Sep 17 00:00:00 2001 From: Stuart Rowlands Date: Mon, 22 Apr 2024 09:07:28 +1000 Subject: [PATCH 04/16] Remove cruft. --- .../src/Plugin/Purge/Purger/.DS_Store | Bin 6148 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 modules/quant_purger/src/Plugin/Purge/Purger/.DS_Store diff --git a/modules/quant_purger/src/Plugin/Purge/Purger/.DS_Store b/modules/quant_purger/src/Plugin/Purge/Purger/.DS_Store deleted file mode 100644 index 5008ddfcf53c02e82d7eee2e57c38e5672ef89f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0 Date: Mon, 10 Jun 2024 21:39:50 -0700 Subject: [PATCH 05/16] Fix linting. --- README.md | 11 ++- modules/quant_api/src/Client/QuantClient.php | 32 ++++---- .../src/Form/QuantPurgeFormBase.php | 2 +- .../src/Plugin/Purge/Purger/QuantPurge.php | 77 ++++++++----------- .../Plugin/Purge/Purger/QuantPurgeBase.php | 21 ++--- 5 files changed, 70 insertions(+), 73 deletions(-) diff --git a/README.md b/README.md index 54564663..a7944b16 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,15 @@ # QuantCDN Drupal module -This module acts as a static site generator for Drupal and integrates with the [QuantCDN static edge](https://www.quantcdn.io), and is compatible with Drupal 9.x and 10.x. +This module acts as a static site generator for Drupal and integrates with the +[QuantCDN static edge](https://www.quantcdn.io), and is compatible with Drupal +9.x and 10.x. -QuantCDN is a global CDN engineered specifically for the static web and Jamstack. +QuantCDN is a global CDN engineered specifically for the static web and +Jamstack. -It allows one-time compilation and push of an entire Drupal site, as well as tracking and pushes of ongoing content change; the simplest way to export and maintain a static export of your Drupal site. +It allows one-time compilation and push of an entire Drupal site, as well as +tracking and pushes of ongoing content change; the simplest way to export and +maintain a static export of your Drupal site. Development, issues and feature roadmap occurs in the [GitHub repository](https://github.com/quantcdn/drupal). diff --git a/modules/quant_api/src/Client/QuantClient.php b/modules/quant_api/src/Client/QuantClient.php index 06ab5ea6..eb854ceb 100644 --- a/modules/quant_api/src/Client/QuantClient.php +++ b/modules/quant_api/src/Client/QuantClient.php @@ -236,14 +236,14 @@ public function search() { */ public function purgePath(string $path) : array { $response = $this->client->post($this->endpoint . '/purge', [ - RequestOptions::JSON => [], - 'headers' => [ - 'Quant-Customer' => $this->username, - 'Quant-Project' => $this->project, - 'Quant-Token' => $this->token, - 'Quant-Url' => $path, - ], - 'verify' => $this->tlsDisabled ? FALSE : TRUE, + RequestOptions::JSON => [], + 'headers' => [ + 'Quant-Customer' => $this->username, + 'Quant-Project' => $this->project, + 'Quant-Token' => $this->token, + 'Quant-Url' => $path, + ], + 'verify' => $this->tlsDisabled ? FALSE : TRUE, ]); return json_decode($response->getBody(), TRUE); @@ -255,14 +255,14 @@ public function purgePath(string $path) : array { public function purgeTags(array $tags) : array { $response = $this->client->post($this->endpoint . '/purge', [ - RequestOptions::JSON => [], - 'headers' => [ - 'Quant-Customer' => $this->username, - 'Quant-Project' => $this->project, - 'Quant-Token' => $this->token, - 'Cache-Keys' => implode(' ', $tags), - ], - 'verify' => $this->tlsDisabled ? FALSE : TRUE, + RequestOptions::JSON => [], + 'headers' => [ + 'Quant-Customer' => $this->username, + 'Quant-Project' => $this->project, + 'Quant-Token' => $this->token, + 'Cache-Keys' => implode(' ', $tags), + ], + 'verify' => $this->tlsDisabled ? FALSE : TRUE, ]); return json_decode($response->getBody(), TRUE); diff --git a/modules/quant_purger/src/Form/QuantPurgeFormBase.php b/modules/quant_purger/src/Form/QuantPurgeFormBase.php index 180d36a6..12a005d1 100644 --- a/modules/quant_purger/src/Form/QuantPurgeFormBase.php +++ b/modules/quant_purger/src/Form/QuantPurgeFormBase.php @@ -105,7 +105,7 @@ public function buildFormMetadata(array &$form, FormStateInterface $form_state, * 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\QuantPurgeSettingsPurgeSettings $settings + * @param \Drupal\quant_purger\Entity\QuantPurgeSettings $settings * Configuration entity for the purger being configured. */ public function buildFormPerformance(array &$form, FormStateInterface $form_state, QuantPurgeSettings $settings) { diff --git a/modules/quant_purger/src/Plugin/Purge/Purger/QuantPurge.php b/modules/quant_purger/src/Plugin/Purge/Purger/QuantPurge.php index 2acf049a..19d541e5 100644 --- a/modules/quant_purger/src/Plugin/Purge/Purger/QuantPurge.php +++ b/modules/quant_purger/src/Plugin/Purge/Purger/QuantPurge.php @@ -29,49 +29,42 @@ public function invalidate(array $invalidations) { $filtered_validations = $this->processInvalidations($invalidations); if ($filtered_validations['everything']) { - $this->invalidateEverything($invalidations); - return; + $this->invalidateEverything($invalidations); + return; } if (!empty($filtered_validations['tags'])) { - $this->invalidateTags($filtered_validations['tags']); + $this->invalidateTags($filtered_validations['tags']); } if (!empty($filtered_validations['paths'])) { - $this->invalidatePaths($filtered_validations['paths']); + $this->invalidatePaths($filtered_validations['paths']); } } - /** * {@inheritdoc} */ public function invalidateTags(array $invalidations) { $tags = []; foreach ($invalidations as $invalidation) { - $tags[] = Hash::cacheTags([$invalidation->getExpression()])[0]; + $tags[] = Hash::cacheTags([$invalidation->getExpression()])[0]; } try { - $this->logger()->debug('[tags] Purging tags: ' . implode(' ', $tags)); - $this->purgeTags($tags); - $invalidation->setState(InvalidationInterface::SUCCEEDED); + $this->logger()->debug('[tags] Purging tags: ' . implode(' ', $tags)); + $this->purgeTags($tags); + $invalidation->setState(InvalidationInterface::SUCCEEDED); } catch (\Exception $e) { - $this->logger()->notice('Error attempting to purge cache path: ' . $e->getMessage()); - error_log($e->getMessage()); - $invalidation->setState(InvalidationInterface::FAILED); + $this->logger()->notice('Error attempting to purge cache path: ' . $e->getMessage()); + error_log($e->getMessage()); + $invalidation->setState(InvalidationInterface::FAILED); } - - - - } /** - * InvalidatePaths - * - * This will invalidate path based invalidations in a loop. + * Invalidate path-based invalidations in a loop. * * @param array $invalidations * This takes in an array of Invalidation, processing them all in a loop, @@ -80,24 +73,22 @@ public function invalidateTags(array $invalidations) { public function invalidatePaths(array $invalidations) { foreach ($invalidations as $invalidation) { - try { - $path = '/' . $invalidation->getExpression(); - $this->logger()->debug('[path] Purging path invalidation: ' . $path); - $this->purgePath($path); - $invalidation->setState(InvalidationInterface::SUCCEEDED); - } - catch (\Exception $e) { - $this->logger()->notice('Error attempting to purge cache path: ' . $e->getMessage()); - error_log($e->getMessage()); - $invalidation->setState(InvalidationInterface::FAILED); - } + try { + $path = '/' . $invalidation->getExpression(); + $this->logger()->debug('[path] Purging path invalidation: ' . $path); + $this->purgePath($path); + $invalidation->setState(InvalidationInterface::SUCCEEDED); + } + catch (\Exception $e) { + $this->logger()->notice('Error attempting to purge cache path: ' . $e->getMessage()); + error_log($e->getMessage()); + $invalidation->setState(InvalidationInterface::FAILED); + } } } /** - * InvalidateEverything. - * - * This will invalidate with the path '/*' to purge the entire project cache. + * Invalidate with the path '/*' to purge the entire project cache. * * @param array $invalidations * This takes in an array of Invalidation, processing them all in a loop, @@ -106,18 +97,18 @@ public function invalidatePaths(array $invalidations) { public function invalidateEverything(array $invalidations) { try { - $this->logger()->debug('[everything] Purging entire site cache (/*)'); - $this->purgePath('/*'); - foreach ($invalidations as $invalidation) { - $invalidation->setState(InvalidationInterface::SUCCEEDED); - } + $this->logger()->debug('[everything] Purging entire site cache (/*)'); + $this->purgePath('/*'); + foreach ($invalidations as $invalidation) { + $invalidation->setState(InvalidationInterface::SUCCEEDED); + } } catch (\Exception $e) { - $this->logger()->notice('Error attempting to purge entire cache: ' . $e->getMessage()); - error_log($e->getMessage()); - foreach ($invalidations as $invalidation) { - $invalidation->setState(InvalidationInterface::FAILED); - } + $this->logger()->notice('Error attempting to purge entire cache: ' . $e->getMessage()); + error_log($e->getMessage()); + foreach ($invalidations as $invalidation) { + $invalidation->setState(InvalidationInterface::FAILED); + } } } diff --git a/modules/quant_purger/src/Plugin/Purge/Purger/QuantPurgeBase.php b/modules/quant_purger/src/Plugin/Purge/Purger/QuantPurgeBase.php index e9aaf743..96bf6f67 100644 --- a/modules/quant_purger/src/Plugin/Purge/Purger/QuantPurgeBase.php +++ b/modules/quant_purger/src/Plugin/Purge/Purger/QuantPurgeBase.php @@ -58,7 +58,7 @@ abstract class QuantPurgeBase extends PurgerBase implements PurgerInterface { public function __construct(array $configuration, $plugin_id, $plugin_definition, ClientInterface $http_client, Token $token) { parent::__construct($configuration, $plugin_id, $plugin_definition); $this->settings = QuantPurgeSettings::load($this->getId()); - // Note: We actually use the Quant HTTP client rather than the generic Guzzle client. + // Note: We use the Quant HTTP client rather than the generic Guzzle client. $this->client = \Drupal::service('quant_api.client'); $this->token = $token; } @@ -116,28 +116,29 @@ public function getLabel() { * The invalidations array. * * @return array + * The array of filtered tags and paths. */ public function processInvalidations(array $invalidations) { $filtered_tags = []; $filtered_paths = []; - $everything = false; + $everything = FALSE; - foreach($invalidations as $invalidation) { + foreach ($invalidations as $invalidation) { if ($invalidation->getType() == 'tag') { $invalidation->setState(InvalidationInterface::PROCESSING); $filtered_tags[] = $invalidation; } elseif ($invalidation->getType() == 'path') { - // @todo: dunno what this looks like + // @todo Not sure what this looks like. $invalidation->setState(InvalidationInterface::PROCESSING); $filtered_paths[] = $invalidation; } elseif ($invalidation->getType() == 'everything') { // 'Everything' trumps everything and will issue a site-wide purge. $invalidation->setState(InvalidationInterface::PROCESSING); - $everything = true; + $everything = TRUE; $filtered_paths = []; $filtered_tags = []; break; @@ -148,9 +149,9 @@ public function processInvalidations(array $invalidations) { } $filtered_array = [ - 'everything' => $everything, - 'tags' => $filtered_tags, - 'paths' => $filtered_paths, + 'everything' => $everything, + 'tags' => $filtered_tags, + 'paths' => $filtered_paths, ]; return $filtered_array; @@ -193,7 +194,7 @@ public function hasRuntimeMeasurement() { * @param string $path * The path to purge. */ - public function purgePath($path) { + public function purgePath(string $path) { $this->client->purgePath($path); } @@ -203,7 +204,7 @@ public function purgePath($path) { * @param array $tags * The array of tags to purge. */ - public function purgeTags($tags) { + public function purgeTags(array $tags) { $this->client->purgeTags($tags); } From f54ba5fe514ed5af3a45886807b844f17a67f8aa Mon Sep 17 00:00:00 2001 From: Kristen Pol Date: Mon, 10 Jun 2024 21:57:33 -0700 Subject: [PATCH 06/16] Minor wording tweaks. --- modules/quant_api/src/Client/QuantClientInterface.php | 4 ++-- modules/quant_purger/src/Entity/Hash.php | 2 +- .../quant_purger/src/Plugin/Purge/Purger/QuantPurgeBase.php | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/modules/quant_api/src/Client/QuantClientInterface.php b/modules/quant_api/src/Client/QuantClientInterface.php index e2354242..78ebb35a 100644 --- a/modules/quant_api/src/Client/QuantClientInterface.php +++ b/modules/quant_api/src/Client/QuantClientInterface.php @@ -48,7 +48,7 @@ public function search(); public function send(array $data) : array; /** - * Sends a purge payload (path based) to the API. + * Sends a purge payload (path-based) to the API. * * @param string $path * The path to purge. @@ -62,7 +62,7 @@ public function send(array $data) : array; public function purgePath(string $path) : array; /** - * Sends a purge payload (tags based) to the API. + * Sends a purge payload (tags-based) to the API. * * @param array $tags * The array of tags to purge. diff --git a/modules/quant_purger/src/Entity/Hash.php b/modules/quant_purger/src/Entity/Hash.php index db35698f..e9880037 100644 --- a/modules/quant_purger/src/Entity/Hash.php +++ b/modules/quant_purger/src/Entity/Hash.php @@ -56,7 +56,7 @@ public static function cacheTags(array $tags) { * Create a unique hash that identifies this site. * * @param string $site_name - * The identifier of the site on Acquia Cloud. + * The identifier of the site on QuantCDN. * @param string $site_path * The path of the site, e.g. 'site/default' or 'site/database_a'. * diff --git a/modules/quant_purger/src/Plugin/Purge/Purger/QuantPurgeBase.php b/modules/quant_purger/src/Plugin/Purge/Purger/QuantPurgeBase.php index 96bf6f67..dcca448e 100644 --- a/modules/quant_purger/src/Plugin/Purge/Purger/QuantPurgeBase.php +++ b/modules/quant_purger/src/Plugin/Purge/Purger/QuantPurgeBase.php @@ -189,7 +189,7 @@ public function hasRuntimeMeasurement() { } /** - * Sends a path based purge request to the Quant API. + * Sends a path-based purge request to the Quant API. * * @param string $path * The path to purge. @@ -199,7 +199,7 @@ public function purgePath(string $path) { } /** - * Sends a tags based purge request to the Quant API. + * Sends a tags-based purge request to the Quant API. * * @param array $tags * The array of tags to purge. From 89bbdfb618edf3b4991e95ed8e5ff06aa113d429 Mon Sep 17 00:00:00 2001 From: Kristen Pol Date: Mon, 10 Jun 2024 22:35:14 -0700 Subject: [PATCH 07/16] Removed unused variable. --- .../quant_purger/src/Plugin/Purge/Purger/QuantPurgeBase.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/modules/quant_purger/src/Plugin/Purge/Purger/QuantPurgeBase.php b/modules/quant_purger/src/Plugin/Purge/Purger/QuantPurgeBase.php index dcca448e..ec595983 100644 --- a/modules/quant_purger/src/Plugin/Purge/Purger/QuantPurgeBase.php +++ b/modules/quant_purger/src/Plugin/Purge/Purger/QuantPurgeBase.php @@ -29,11 +29,6 @@ abstract class QuantPurgeBase extends PurgerBase implements PurgerInterface { */ protected $settings; - /** - * The settings entity holding the Quant API configuration. - */ - protected $api_settings; - /** * The token service. * From 948782ee4756b916622d1b899cd0cf3b729b9e61 Mon Sep 17 00:00:00 2001 From: Kristen Pol Date: Fri, 5 Jul 2024 19:02:05 -0700 Subject: [PATCH 08/16] Update modules/quant_api/src/Client/QuantClientInterface.php Fix typo. --- modules/quant_api/src/Client/QuantClientInterface.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/quant_api/src/Client/QuantClientInterface.php b/modules/quant_api/src/Client/QuantClientInterface.php index 78ebb35a..b8b435e5 100644 --- a/modules/quant_api/src/Client/QuantClientInterface.php +++ b/modules/quant_api/src/Client/QuantClientInterface.php @@ -57,7 +57,7 @@ public function send(array $data) : array; * Return array of response data. * * @throws \Drupal\quant_api\Exception\InvalidPayload - * @throws \Drupal\quant_api\Exception\InvalidResposne + * @throws \Drupal\quant_api\Exception\InvalidResponse */ public function purgePath(string $path) : array; From 2010e9f078255b4ad88ce871001dbbb68f29d38c Mon Sep 17 00:00:00 2001 From: Kristen Pol Date: Fri, 5 Jul 2024 19:03:50 -0700 Subject: [PATCH 09/16] Update modules/quant_api/src/Client/QuantClientInterface.php Fix typo. --- modules/quant_api/src/Client/QuantClientInterface.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/quant_api/src/Client/QuantClientInterface.php b/modules/quant_api/src/Client/QuantClientInterface.php index b8b435e5..60dc8930 100644 --- a/modules/quant_api/src/Client/QuantClientInterface.php +++ b/modules/quant_api/src/Client/QuantClientInterface.php @@ -71,7 +71,7 @@ public function purgePath(string $path) : array; * Return array of response data. * * @throws \Drupal\quant_api\Exception\InvalidPayload - * @throws \Drupal\quant_api\Exception\InvalidResposne + * @throws \Drupal\quant_api\Exception\InvalidResponse */ public function purgeTags(array $tags) : array; From 644c7094be0649e23d6024872e9e635793f71d87 Mon Sep 17 00:00:00 2001 From: Kristen Pol Date: Fri, 5 Jul 2024 19:09:48 -0700 Subject: [PATCH 10/16] Update modules/quant_purger/src/Entity/QuantPurgeSettings.php Update doc block. --- modules/quant_purger/src/Entity/QuantPurgeSettings.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/quant_purger/src/Entity/QuantPurgeSettings.php b/modules/quant_purger/src/Entity/QuantPurgeSettings.php index 47c09bc5..a284bb2e 100644 --- a/modules/quant_purger/src/Entity/QuantPurgeSettings.php +++ b/modules/quant_purger/src/Entity/QuantPurgeSettings.php @@ -6,7 +6,7 @@ use Drupal\purge\Plugin\Purge\Purger\PurgerSettingsInterface; /** - * Defines the QuantCDN purger settings entity. + * Defines the QuantPurgerSettings entity. * * @ConfigEntityType( * id = "quantpurgersettings", From 7fad3c06d46e5583a329e4ff6ce93c758dac25fe Mon Sep 17 00:00:00 2001 From: Kristen Pol Date: Fri, 5 Jul 2024 19:13:46 -0700 Subject: [PATCH 11/16] Update modules/quant_purger/src/Entity/QuantPurgeSettings.php Remove extraneous quote. --- modules/quant_purger/src/Entity/QuantPurgeSettings.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/quant_purger/src/Entity/QuantPurgeSettings.php b/modules/quant_purger/src/Entity/QuantPurgeSettings.php index a284bb2e..0ef14a59 100644 --- a/modules/quant_purger/src/Entity/QuantPurgeSettings.php +++ b/modules/quant_purger/src/Entity/QuantPurgeSettings.php @@ -79,7 +79,7 @@ class QuantPurgeSettings extends PurgerSettingsBase implements PurgerSettingsInt * Cooldown time. * * Number of seconds to wait after one or more invalidations took place (so - * that other purgers get fresh content).' + * that other purgers get fresh content). * * @var float */ From d52e3947598ea5e67fdf680286e2cceea16b522a Mon Sep 17 00:00:00 2001 From: Kristen Pol Date: Fri, 5 Jul 2024 19:53:35 -0700 Subject: [PATCH 12/16] Rename QuantPurgeSettings to QuantPurgerSettings. --- ...ntPurgeSettings.php => QuantPurgerSettings.php} | 2 +- .../quant_purger/src/Form/QuantPurgeFormBase.php | 14 +++++++------- .../src/Plugin/Purge/Purger/QuantPurgeBase.php | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) rename modules/quant_purger/src/Entity/{QuantPurgeSettings.php => QuantPurgerSettings.php} (96%) diff --git a/modules/quant_purger/src/Entity/QuantPurgeSettings.php b/modules/quant_purger/src/Entity/QuantPurgerSettings.php similarity index 96% rename from modules/quant_purger/src/Entity/QuantPurgeSettings.php rename to modules/quant_purger/src/Entity/QuantPurgerSettings.php index 0ef14a59..6c04fd7d 100644 --- a/modules/quant_purger/src/Entity/QuantPurgeSettings.php +++ b/modules/quant_purger/src/Entity/QuantPurgerSettings.php @@ -29,7 +29,7 @@ * }, * ) */ -class QuantPurgeSettings extends PurgerSettingsBase implements PurgerSettingsInterface { +class QuantPurgerSettings extends PurgerSettingsBase implements PurgerSettingsInterface { /** * Instance metadata. diff --git a/modules/quant_purger/src/Form/QuantPurgeFormBase.php b/modules/quant_purger/src/Form/QuantPurgeFormBase.php index 12a005d1..644ddafa 100644 --- a/modules/quant_purger/src/Form/QuantPurgeFormBase.php +++ b/modules/quant_purger/src/Form/QuantPurgeFormBase.php @@ -7,7 +7,7 @@ use Drupal\Core\Form\FormStateInterface; use Drupal\purge\Plugin\Purge\Invalidation\InvalidationsServiceInterface; use Drupal\purge_ui\Form\PurgerConfigFormBase; -use Drupal\quant_purger\Entity\QuantPurgeSettings; +use Drupal\quant_purger\Entity\QuantPurgerSettings; use Drupal\Core\Entity\EntityTypeManagerInterface; /** @@ -67,7 +67,7 @@ public function getFormId() { * {@inheritdoc} */ public function buildForm(array $form, FormStateInterface $form_state) { - $settings = QuantPurgeSettings::load($this->getId($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); @@ -81,10 +81,10 @@ public function buildForm(array $form, FormStateInterface $form_state) { * 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\QuantPurgeSettings $settings + * @param \Drupal\quant_purger\Entity\QuantPurgerSettings $settings * Configuration entity for the purger being configured. */ - public function buildFormMetadata(array &$form, FormStateInterface $form_state, QuantPurgeSettings $settings) { + public function buildFormMetadata(array &$form, FormStateInterface $form_state, QuantPurgerSettings $settings) { $form['name'] = [ '#title' => $this->t('Name'), '#type' => 'textfield', @@ -105,10 +105,10 @@ public function buildFormMetadata(array &$form, FormStateInterface $form_state, * 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\QuantPurgeSettings $settings + * @param \Drupal\quant_purger\Entity\QuantPurgerSettings $settings * Configuration entity for the purger being configured. */ - public function buildFormPerformance(array &$form, FormStateInterface $form_state, QuantPurgeSettings $settings) { + public function buildFormPerformance(array &$form, FormStateInterface $form_state, QuantPurgerSettings $settings) { $form['performance'] = [ '#type' => 'details', '#group' => 'tabs', @@ -201,7 +201,7 @@ public function validateForm(array &$form, FormStateInterface $form_state) { * {@inheritdoc} */ public function submitFormSuccess(array &$form, FormStateInterface $form_state) { - $settings = QuantPurgeSettings::load($this->getId($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) { diff --git a/modules/quant_purger/src/Plugin/Purge/Purger/QuantPurgeBase.php b/modules/quant_purger/src/Plugin/Purge/Purger/QuantPurgeBase.php index ec595983..4f624df7 100644 --- a/modules/quant_purger/src/Plugin/Purge/Purger/QuantPurgeBase.php +++ b/modules/quant_purger/src/Plugin/Purge/Purger/QuantPurgeBase.php @@ -6,7 +6,7 @@ use GuzzleHttp\ClientInterface; use Drupal\purge\Plugin\Purge\Purger\PurgerBase; use Drupal\purge\Plugin\Purge\Purger\PurgerInterface; -use Drupal\quant_purger\Entity\QuantPurgeSettings; +use Drupal\quant_purger\Entity\QuantPurgerSettings; use Symfony\Component\DependencyInjection\ContainerInterface; use Drupal\purge\Plugin\Purge\Invalidation\InvalidationInterface; @@ -25,7 +25,7 @@ abstract class QuantPurgeBase extends PurgerBase implements PurgerInterface { /** * The settings entity holding all configuration. * - * @var \Drupal\quant_purger\Entity\QuantPurgeSettings + * @var \Drupal\quant_purger\Entity\QuantPurgerSettings */ protected $settings; @@ -52,7 +52,7 @@ abstract class QuantPurgeBase extends PurgerBase implements PurgerInterface { */ public function __construct(array $configuration, $plugin_id, $plugin_definition, ClientInterface $http_client, Token $token) { parent::__construct($configuration, $plugin_id, $plugin_definition); - $this->settings = QuantPurgeSettings::load($this->getId()); + $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; @@ -75,7 +75,7 @@ public static function create(ContainerInterface $container, array $configuratio * {@inheritdoc} */ public function delete() { - QuantPurgeSettings::load($this->getId())->delete(); + QuantPurgerSettings::load($this->getId())->delete(); } /** From 5d5b59c1f611e67c54b71ea868ee45873066d23c Mon Sep 17 00:00:00 2001 From: Kristen Pol Date: Fri, 5 Jul 2024 23:45:16 -0700 Subject: [PATCH 13/16] Refactoring & renaming for consistency and clarity. --- .../src/Entity/QuantPurgerSettings.php | 2 +- ...urgeForm.php => QuantPurgerConfigForm.php} | 4 +- ...Base.php => QuantPurgerConfigFormBase.php} | 12 +-- ...rm.php => QuantPurgerQueuerConfigForm.php} | 10 ++- .../{QuantPurge.php => QuantPurger.php} | 78 ++++++++++--------- ...QuantPurgeBase.php => QuantPurgerBase.php} | 50 ++++++------ .../src/Plugin/Purge/Queuer/QuantPurger.php | 2 +- ...PurgerPlugin.php => QuantPurgerQueuer.php} | 9 ++- ...gsHeader.php => QuantPurgerTagsHeader.php} | 8 +- 9 files changed, 90 insertions(+), 85 deletions(-) rename modules/quant_purger/src/Form/{QuantPurgeForm.php => QuantPurgerConfigForm.php} (69%) rename modules/quant_purger/src/Form/{QuantPurgeFormBase.php => QuantPurgerConfigFormBase.php} (96%) rename modules/quant_purger/src/Form/{ConfigurationForm.php => QuantPurgerQueuerConfigForm.php} (93%) rename modules/quant_purger/src/Plugin/Purge/Purger/{QuantPurge.php => QuantPurger.php} (68%) rename modules/quant_purger/src/Plugin/Purge/Purger/{QuantPurgeBase.php => QuantPurgerBase.php} (85%) rename modules/quant_purger/src/Plugin/Purge/Queuer/{QuantPurgerPlugin.php => QuantPurgerQueuer.php} (54%) rename modules/quant_purger/src/Plugin/Purge/TagsHeader/{QuantCacheTagsHeader.php => QuantPurgerTagsHeader.php} (80%) diff --git a/modules/quant_purger/src/Entity/QuantPurgerSettings.php b/modules/quant_purger/src/Entity/QuantPurgerSettings.php index 6c04fd7d..627e159d 100644 --- a/modules/quant_purger/src/Entity/QuantPurgerSettings.php +++ b/modules/quant_purger/src/Entity/QuantPurgerSettings.php @@ -40,7 +40,7 @@ class QuantPurgerSettings extends PurgerSettingsBase implements PurgerSettingsIn * * @var string */ - public $name = ''; + public $name = 'Quant Purger'; /** * The invalidation plugin ID that this purger invalidates. diff --git a/modules/quant_purger/src/Form/QuantPurgeForm.php b/modules/quant_purger/src/Form/QuantPurgerConfigForm.php similarity index 69% rename from modules/quant_purger/src/Form/QuantPurgeForm.php rename to modules/quant_purger/src/Form/QuantPurgerConfigForm.php index ae19cd6c..3b83036e 100644 --- a/modules/quant_purger/src/Form/QuantPurgeForm.php +++ b/modules/quant_purger/src/Form/QuantPurgerConfigForm.php @@ -3,9 +3,9 @@ namespace Drupal\quant_purger\Form; /** - * Configuration form for the HTTP Bundled Purger. + * Configuration form for the Quant Purger. */ -class QuantPurgeForm extends QuantPurgeFormBase { +class QuantPurgerConfigForm extends QuantPurgerConfigFormBase { /** * The token group names this purger supports replacing tokens for. diff --git a/modules/quant_purger/src/Form/QuantPurgeFormBase.php b/modules/quant_purger/src/Form/QuantPurgerConfigFormBase.php similarity index 96% rename from modules/quant_purger/src/Form/QuantPurgeFormBase.php rename to modules/quant_purger/src/Form/QuantPurgerConfigFormBase.php index 644ddafa..ffd21a0a 100644 --- a/modules/quant_purger/src/Form/QuantPurgeFormBase.php +++ b/modules/quant_purger/src/Form/QuantPurgerConfigFormBase.php @@ -2,18 +2,18 @@ namespace Drupal\quant_purger\Form; -use Symfony\Component\DependencyInjection\ContainerInterface; 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 Drupal\Core\Entity\EntityTypeManagerInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; /** - * Abstract form base for HTTP based configurable purgers. + * Abstract form base for Quant Purger configuration. */ -abstract class QuantPurgeFormBase extends PurgerConfigFormBase { +abstract class QuantPurgerConfigFormBase extends PurgerConfigFormBase { /** * The service that generates invalidation objects on-demand. @@ -23,7 +23,7 @@ abstract class QuantPurgeFormBase extends PurgerConfigFormBase { protected $purgeInvalidationFactory; /** - * Constructs a \Drupal\quant_purger\Form\ConfigurationForm object. + * Constructs a base Quant Purger configuration form. * * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory * The factory for configuration objects. @@ -122,7 +122,7 @@ public function buildFormPerformance(array &$form, FormStateInterface $form_stat '#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)'), + '#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', diff --git a/modules/quant_purger/src/Form/ConfigurationForm.php b/modules/quant_purger/src/Form/QuantPurgerQueuerConfigForm.php similarity index 93% rename from modules/quant_purger/src/Form/ConfigurationForm.php rename to modules/quant_purger/src/Form/QuantPurgerQueuerConfigForm.php index cde184a4..1cd3ff12 100644 --- a/modules/quant_purger/src/Form/ConfigurationForm.php +++ b/modules/quant_purger/src/Form/QuantPurgerQueuerConfigForm.php @@ -8,9 +8,9 @@ use Drupal\purge_ui\Form\QueuerConfigFormBase; /** - * Configuration form for the Quant queuer. + * Configuration form for the Quant Purger Queuer. */ -class ConfigurationForm extends QueuerConfigFormBase { +class QuantPurgerQueuerConfigForm extends QueuerConfigFormBase { /** * {@inheritdoc} @@ -23,6 +23,8 @@ protected function getEditableConfigNames() { * {@inheritdoc} */ public function getFormId() { + // @todo This should be 'quant_purger.queuer_config_form' which will + // require an update hook. return 'quant_purger.configuration_form'; } @@ -39,7 +41,7 @@ public function buildForm(array $form, FormStateInterface $form_state) { ]; $form['info'] = [ - '#markup' => $this->t('

After making changes to the Quant Purger configuration, all content must be re-seeded so the database reflects the changes.

'), + '#markup' => $this->t('

After making changes to the Quant Purger Queuer configuration, all content must be re-seeded so the database reflects the changes.

'), '#weight' => -10, ]; @@ -153,7 +155,7 @@ public function submitFormSuccess(array &$form, FormStateInterface $form_state) ->set('path_allowlist', $form_state->getValue('path_allowlist')) ->save(); - \Drupal::messenger()->addMessage($this->t('Successfully saved the Quant Purger configuration. All content must be re-seeded so the database reflects the changes.')); + \Drupal::messenger()->addMessage($this->t('Successfully saved the Quant Purger Queuer configuration. All content must be re-seeded so the database reflects the changes.')); } /** diff --git a/modules/quant_purger/src/Plugin/Purge/Purger/QuantPurge.php b/modules/quant_purger/src/Plugin/Purge/Purger/QuantPurger.php similarity index 68% rename from modules/quant_purger/src/Plugin/Purge/Purger/QuantPurge.php rename to modules/quant_purger/src/Plugin/Purge/Purger/QuantPurger.php index 19d541e5..aabc227a 100644 --- a/modules/quant_purger/src/Plugin/Purge/Purger/QuantPurge.php +++ b/modules/quant_purger/src/Plugin/Purge/Purger/QuantPurger.php @@ -11,71 +11,75 @@ * * @PurgePurger( * id = "quant_purger", - * label = @Translation("QuantCDN Purger"), - * configform = "\Drupal\quant_purger\Form\QuantPurgeForm", + * 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."), * multi_instance = FALSE, - * types = {"tag", "everything", "path"}, + * types = {"everything", "path", "tag"}, * ) */ -class QuantPurge extends QuantPurgeBase implements PurgerInterface { +class QuantPurger extends QuantPurgerBase implements PurgerInterface { /** * {@inheritdoc} */ public function invalidate(array $invalidations) { - $filtered_validations = $this->processInvalidations($invalidations); + $processed = $this->processInvalidations($invalidations); - if ($filtered_validations['everything']) { + if ($processed['everything']) { $this->invalidateEverything($invalidations); return; } - if (!empty($filtered_validations['tags'])) { - $this->invalidateTags($filtered_validations['tags']); + if (!empty($processed['paths'])) { + $this->invalidatePaths($processed['paths']); } - if (!empty($filtered_validations['paths'])) { - $this->invalidatePaths($filtered_validations['paths']); + if (!empty($processed['tags'])) { + $this->invalidateTags($processed['tags']); } + } /** - * {@inheritdoc} + * 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. */ - public function invalidateTags(array $invalidations) { - $tags = []; - foreach ($invalidations as $invalidation) { - $tags[] = Hash::cacheTags([$invalidation->getExpression()])[0]; - } + public function invalidateEverything(array $invalidations) { try { - $this->logger()->debug('[tags] Purging tags: ' . implode(' ', $tags)); - $this->purgeTags($tags); - $invalidation->setState(InvalidationInterface::SUCCEEDED); + $this->logger()->debug('[everything] Purging entire site cache (/*)'); + $this->purgePath('/*'); + foreach ($invalidations as $invalidation) { + $invalidation->setState(InvalidationInterface::SUCCEEDED); + } } catch (\Exception $e) { - $this->logger()->notice('Error attempting to purge cache path: ' . $e->getMessage()); + $this->logger()->notice('Error attempting to purge entire cache: ' . $e->getMessage()); error_log($e->getMessage()); - $invalidation->setState(InvalidationInterface::FAILED); + foreach ($invalidations as $invalidation) { + $invalidation->setState(InvalidationInterface::FAILED); + } } } /** - * Invalidate path-based invalidations in a loop. + * Invalidate path-based invalidations. * * @param array $invalidations - * This takes in an array of Invalidation, processing them all in a loop, - * generally from the purge queue. + * Array of Invalidation objects to process. */ public function invalidatePaths(array $invalidations) { foreach ($invalidations as $invalidation) { try { $path = '/' . $invalidation->getExpression(); - $this->logger()->debug('[path] Purging path invalidation: ' . $path); + $this->logger()->debug('[path] Purging path: ' . $path); $this->purgePath($path); $invalidation->setState(InvalidationInterface::SUCCEEDED); } @@ -88,27 +92,27 @@ public function invalidatePaths(array $invalidations) { } /** - * Invalidate with the path '/*' to purge the entire project cache. + * Invalidate tag-based invalidations. * * @param array $invalidations - * This takes in an array of Invalidation, processing them all in a loop, - * generally from the purge queue. + * Array of Invalidation objects to process. */ - public function invalidateEverything(array $invalidations) { - + public function invalidateTags(array $invalidations) { try { - $this->logger()->debug('[everything] Purging entire site cache (/*)'); - $this->purgePath('/*'); + $this->logger()->debug('[tags] Purging tags: ' . implode(' ', $invalidations)); + + $tags = []; foreach ($invalidations as $invalidation) { - $invalidation->setState(InvalidationInterface::SUCCEEDED); + $tags[] = Hash::cacheTags([$invalidation->getExpression()])[0]; } + + $this->purgeTags($tags); + $invalidation->setState(InvalidationInterface::SUCCEEDED); } catch (\Exception $e) { - $this->logger()->notice('Error attempting to purge entire cache: ' . $e->getMessage()); + $this->logger()->notice('Error attempting to purge cache path: ' . $e->getMessage()); error_log($e->getMessage()); - foreach ($invalidations as $invalidation) { - $invalidation->setState(InvalidationInterface::FAILED); - } + $invalidation->setState(InvalidationInterface::FAILED); } } diff --git a/modules/quant_purger/src/Plugin/Purge/Purger/QuantPurgeBase.php b/modules/quant_purger/src/Plugin/Purge/Purger/QuantPurgerBase.php similarity index 85% rename from modules/quant_purger/src/Plugin/Purge/Purger/QuantPurgeBase.php rename to modules/quant_purger/src/Plugin/Purge/Purger/QuantPurgerBase.php index 4f624df7..bba5f4fa 100644 --- a/modules/quant_purger/src/Plugin/Purge/Purger/QuantPurgeBase.php +++ b/modules/quant_purger/src/Plugin/Purge/Purger/QuantPurgerBase.php @@ -3,17 +3,17 @@ namespace Drupal\quant_purger\Plugin\Purge\Purger; use Drupal\Core\Utility\Token; -use GuzzleHttp\ClientInterface; +use Drupal\purge\Plugin\Purge\Invalidation\InvalidationInterface; 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; -use Drupal\purge\Plugin\Purge\Invalidation\InvalidationInterface; /** - * Abstract base class for HTTP based configurable purgers. + * Abstract base class for Quant Purger. */ -abstract class QuantPurgeBase extends PurgerBase implements PurgerInterface { +abstract class QuantPurgerBase extends PurgerBase implements PurgerInterface { /** * The Guzzle HTTP client. @@ -105,51 +105,49 @@ public function getLabel() { } /** - * Returns array of tags and paths to purge in the provided array. + * Process invalidations based on type. * * @param array $invalidations * The invalidations array. * * @return array - * The array of filtered tags and paths. + * The processed array with 'everything', 'paths' and 'tags' keys. */ public function processInvalidations(array $invalidations) { - $filtered_tags = []; - $filtered_paths = []; - $everything = FALSE; + $paths = []; + $tags = []; foreach ($invalidations as $invalidation) { - if ($invalidation->getType() == 'tag') { + if ($invalidation->getType() == 'everything') { + // 'Everything' trumps everything and will issue a site-wide purge. $invalidation->setState(InvalidationInterface::PROCESSING); - $filtered_tags[] = $invalidation; + $everything = TRUE; + $paths = []; + $tags = []; + break; } elseif ($invalidation->getType() == 'path') { - // @todo Not sure what this looks like. $invalidation->setState(InvalidationInterface::PROCESSING); - $filtered_paths[] = $invalidation; + $paths[] = $invalidation; } - elseif ($invalidation->getType() == 'everything') { - // 'Everything' trumps everything and will issue a site-wide purge. + elseif ($invalidation->getType() == 'tag') { $invalidation->setState(InvalidationInterface::PROCESSING); - $everything = TRUE; - $filtered_paths = []; - $filtered_tags = []; - break; + $tags[] = $invalidation; } else { $invalidation->setState(InvalidationInterface::NOT_SUPPORTED); } } - $filtered_array = [ + $processed = [ 'everything' => $everything, - 'tags' => $filtered_tags, - 'paths' => $filtered_paths, + 'paths' => $paths, + 'tags' => $tags, ]; - return $filtered_array; + return $processed; } /** @@ -170,9 +168,9 @@ public function getTimeHint() { */ public function getTypes() { return [ - "tag", - "everything", - "path", + 'everything', + 'path', + 'tag', ]; } diff --git a/modules/quant_purger/src/Plugin/Purge/Queuer/QuantPurger.php b/modules/quant_purger/src/Plugin/Purge/Queuer/QuantPurger.php index a0182014..41f95648 100644 --- a/modules/quant_purger/src/Plugin/Purge/Queuer/QuantPurger.php +++ b/modules/quant_purger/src/Plugin/Purge/Queuer/QuantPurger.php @@ -44,7 +44,7 @@ class QuantPurger implements CacheTagsInvalidatorInterface, ContainerAwareInterf /** * The queuer plugin or FALSE when the plugin is disabled. * - * @var null|false|\Drupal\quant_purger\Plugin\Purge\Queuer\QuantPurgerPlugin + * @var null|false|\Drupal\quant_purger\Plugin\Purge\Queuer\QuantPurgerQueuer */ protected $queuer; diff --git a/modules/quant_purger/src/Plugin/Purge/Queuer/QuantPurgerPlugin.php b/modules/quant_purger/src/Plugin/Purge/Queuer/QuantPurgerQueuer.php similarity index 54% rename from modules/quant_purger/src/Plugin/Purge/Queuer/QuantPurgerPlugin.php rename to modules/quant_purger/src/Plugin/Purge/Queuer/QuantPurgerQueuer.php index 7ab7e9c2..6aef9c3e 100644 --- a/modules/quant_purger/src/Plugin/Purge/Queuer/QuantPurgerPlugin.php +++ b/modules/quant_purger/src/Plugin/Purge/Queuer/QuantPurgerQueuer.php @@ -6,15 +6,16 @@ use Drupal\purge\Plugin\Purge\Queuer\QueuerInterface; /** - * Quant purger. + * Quant Purger Queuer. * * @PurgeQueuer( * id = "quant", - * label = @Translation("Purge Quant"), + * label = @Translation("Quant Purger Queuer"), * description = @Translation("Queue impacted content updates."), * enable_by_default = true, * types = {"tag"}, - * configform = "\Drupal\quant_purger\Form\ConfigurationForm", + * configform = "\Drupal\quant_purger\Form\QuantPurgerQueuerConfigForm", * ) */ -class QuantPurgerPlugin extends QueuerBase implements QueuerInterface {} +// @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/TagsHeader/QuantCacheTagsHeader.php b/modules/quant_purger/src/Plugin/Purge/TagsHeader/QuantPurgerTagsHeader.php similarity index 80% rename from modules/quant_purger/src/Plugin/Purge/TagsHeader/QuantCacheTagsHeader.php rename to modules/quant_purger/src/Plugin/Purge/TagsHeader/QuantPurgerTagsHeader.php index b8083bad..e8e2027a 100644 --- a/modules/quant_purger/src/Plugin/Purge/TagsHeader/QuantCacheTagsHeader.php +++ b/modules/quant_purger/src/Plugin/Purge/TagsHeader/QuantPurgerTagsHeader.php @@ -2,19 +2,19 @@ namespace Drupal\quant_purger\Plugin\Purge\TagsHeader; -use Drupal\quant_purger\Entity\Hash; -use Drupal\purge\Plugin\Purge\TagsHeader\TagsHeaderInterface; use Drupal\purge\Plugin\Purge\TagsHeader\TagsHeaderBase; +use Drupal\purge\Plugin\Purge\TagsHeader\TagsHeaderInterface; +use Drupal\quant_purger\Entity\Hash; /** * Sets and formats the default response header with cache tags. * * @PurgeTagsHeader( - * id = "quant_tagsheader", + * id = "quant_purger_tags_header", * header_name = "Cache-Keys", * ) */ -class QuantCacheTagsHeader extends TagsHeaderBase implements TagsHeaderInterface { +class QuantPurgerTagsHeader extends TagsHeaderBase implements TagsHeaderInterface { /** * {@inheritdoc} From 1b957b44faca2c6ab5a15710e8d890b3886a6c1b Mon Sep 17 00:00:00 2001 From: Kristen Pol Date: Sat, 6 Jul 2024 13:26:59 -0700 Subject: [PATCH 14/16] Major refactoring, renaming and simplification. --- modules/quant_api/src/Client/QuantClient.php | 1 + modules/quant_purger/README.md | 30 ++- .../schema/quant_purger.data_types.schema.yml | 4 +- .../config/schema/quant_purger.schema.yml | 32 ++- modules/quant_purger/quant_purger.info.yml | 2 +- .../quant_purger/quant_purger.services.yml | 2 +- .../src/Entity/QuantPurgerSettings.php | 8 +- .../src/Form/QuantPurgerConfigForm.php | 211 ++++++++++++++++- .../src/Form/QuantPurgerConfigFormBase.php | 215 ----------------- .../src/Form/QuantPurgerQueuerConfigForm.php | 6 +- .../src/Plugin/Purge/Purger/QuantPurger.php | 219 +++++++++++++++++- .../Plugin/Purge/Purger/QuantPurgerBase.php | 204 ---------------- .../Plugin/Purge/Queuer/QuantPurgerQueuer.php | 5 +- ...r.php => QuantPurgerQueuerInvalidator.php} | 2 +- .../TagsHeader/QuantPurgerTagsHeader.php | 2 +- ...lue.php => QuantPurgerTagsHeaderValue.php} | 2 +- 16 files changed, 484 insertions(+), 461 deletions(-) delete mode 100644 modules/quant_purger/src/Form/QuantPurgerConfigFormBase.php delete mode 100644 modules/quant_purger/src/Plugin/Purge/Purger/QuantPurgerBase.php rename modules/quant_purger/src/Plugin/Purge/Queuer/{QuantPurger.php => QuantPurgerQueuerInvalidator.php} (96%) rename modules/quant_purger/src/Plugin/Purge/TagsHeader/{CacheTagsHeaderValue.php => QuantPurgerTagsHeaderValue.php} (97%) diff --git a/modules/quant_api/src/Client/QuantClient.php b/modules/quant_api/src/Client/QuantClient.php index eb854ceb..bfa0b670 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 d57b0926..baf51f5f 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 bd915c6c..1d372b05 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 64380b15..8676b65d 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 5c819d3c..6990a86a 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 01ec8b59..2120f704 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 627e159d..4262b938 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 3b83036e..1b09b221 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 ffd21a0a..00000000 --- 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 1cd3ff12..f16cacdb 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 aabc227a..3df3c4e8 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 bba5f4fa..00000000 --- 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 6aef9c3e..771201cf 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 41f95648..590b8860 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 e8e2027a..0f462c6f 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 229f93dd..3fbf0e9d 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. From ada1da47ac7b3db29345c318ee55484dd12eac03 Mon Sep 17 00:00:00 2001 From: Kristen Pol Date: Sat, 6 Jul 2024 13:28:45 -0700 Subject: [PATCH 15/16] Removed todo in schema file. --- modules/quant_purger/config/schema/quant_purger.schema.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/quant_purger/config/schema/quant_purger.schema.yml b/modules/quant_purger/config/schema/quant_purger.schema.yml index 8676b65d..20666544 100644 --- a/modules/quant_purger/config/schema/quant_purger.schema.yml +++ b/modules/quant_purger/config/schema/quant_purger.schema.yml @@ -35,7 +35,6 @@ quant_purger.settings: 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: 'Quant Purger Settings' From afc19ff7196a82cdab301d4d059c796fc7767518 Mon Sep 17 00:00:00 2001 From: Kristen Pol Date: Sat, 6 Jul 2024 13:38:57 -0700 Subject: [PATCH 16/16] Fix linting. --- modules/quant_purger/src/Entity/QuantPurgerSettings.php | 1 + modules/quant_purger/src/Form/QuantPurgerConfigForm.php | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/quant_purger/src/Entity/QuantPurgerSettings.php b/modules/quant_purger/src/Entity/QuantPurgerSettings.php index 4262b938..dbbe21b5 100644 --- a/modules/quant_purger/src/Entity/QuantPurgerSettings.php +++ b/modules/quant_purger/src/Entity/QuantPurgerSettings.php @@ -47,6 +47,7 @@ class QuantPurgerSettings extends PurgerSettingsBase implements PurgerSettingsIn * * @var string */ + // @phpcs:ignore public $invalidation_type = 'tag'; /** diff --git a/modules/quant_purger/src/Form/QuantPurgerConfigForm.php b/modules/quant_purger/src/Form/QuantPurgerConfigForm.php index 1b09b221..64d39bc7 100644 --- a/modules/quant_purger/src/Form/QuantPurgerConfigForm.php +++ b/modules/quant_purger/src/Form/QuantPurgerConfigForm.php @@ -38,8 +38,8 @@ class QuantPurgerConfigForm extends PurgerConfigFormBase { * 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. + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager + * The entity type manager. */ public function __construct(ConfigFactoryInterface $config_factory, InvalidationsServiceInterface $purge_invalidation_factory, EntityTypeManagerInterface $entity_type_manager) { $this->setConfigFactory($config_factory);