From b69157a58440e699c2ea7ffca7a8b486d537a0b9 Mon Sep 17 00:00:00 2001 From: Ben Croker Date: Mon, 26 Feb 2024 08:05:42 -0600 Subject: [PATCH 01/12] Update pricing docs --- README.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index b403c0a..b3f691e 100644 --- a/README.md +++ b/README.md @@ -9,11 +9,9 @@ The CloudFront Purger allows the [Blitz](https://putyourlightson.com/plugins/bli **Note that Amazon CloudFront charges for invalidation requests. Since invalidation requests can quickly add up when purging individual URLs, you should be aware of the potential costs. PutYourLightsOn takes no responsibility whatsoever for expenses incurred.** -> No additional charge for the first 1,000 paths requested for invalidation each month. Thereafter, $0.005 per path requested for invalidation. +> The first 1,000 invalidation paths that you submit per month are free; you pay for each invalidation path over 1,000 in a month. An invalidation path can be for a single file (such as `/images/logo.jpg`) or for multiple files (such as `/images/*`). A path that includes the `*` wildcard counts as one path even if it causes CloudFront to invalidate thousands of files. -> A path listed in your invalidation request represents the URL (or multiple URLs if the path contains a wildcard character) of the object(s) you want to invalidate from CloudFront cache. - -Source: [aws.amazon.com/cloudfront/pricing](https://aws.amazon.com/cloudfront/pricing/) +Source: [docs.aws.amazon.com](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Invalidation.html#PayingForInvalidation) ## Usage From 601fa43218a317a3d2b0db39208f6047921a3f62 Mon Sep 17 00:00:00 2001 From: Ben Croker Date: Mon, 26 Feb 2024 09:21:19 -0600 Subject: [PATCH 02/12] Add workflow --- .github/workflows/create-release.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .github/workflows/create-release.yml diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml new file mode 100644 index 0000000..8e33492 --- /dev/null +++ b/.github/workflows/create-release.yml @@ -0,0 +1,21 @@ +name: Create Release +run-name: Create release for ${{ github.event.client_payload.version }} + +on: + repository_dispatch: + types: + - craftcms/new-release + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: ncipollo/release-action@v1 + with: + body: ${{ github.event.client_payload.notes }} + makeLatest: ${{ github.event.client_payload.latest }} + name: ${{ github.event.client_payload.version }} + prerelease: ${{ github.event.client_payload.prerelease }} + tag: ${{ github.event.client_payload.tag }} From a5a10624d5179b5e6f2e4655dec4abed5d3944a8 Mon Sep 17 00:00:00 2001 From: Ben Croker Date: Mon, 26 Feb 2024 09:22:45 -0600 Subject: [PATCH 03/12] Start condense URLs --- CHANGELOG.md | 4 ++++ src/CloudFrontPurger.php | 43 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4ad11d1..c63355d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Release Notes for Blitz CloudFront Purger +## 3.1.0 - Unreleased +### Added +- Added a `condenseUrls` config setting that condenses multiple URLs into a single invalidation request with a wildcard character after the longest common prefix between all URLs. + ## 3.0.4 - 2023-02-15 ### Fixed - Fixed invalidation errors when URLs contained the `*` character in paths ([#10](https://github.com/putyourlightson/craft-blitz-cloudfront/issues/10)). diff --git a/src/CloudFrontPurger.php b/src/CloudFrontPurger.php index 81606f3..84f0235 100644 --- a/src/CloudFrontPurger.php +++ b/src/CloudFrontPurger.php @@ -67,7 +67,7 @@ public static function displayName(): string public function init(): void { Event::on(View::class, View::EVENT_REGISTER_CP_TEMPLATE_ROOTS, - function (RegisterTemplateRootsEvent $event) { + function(RegisterTemplateRootsEvent $event) { $event->roots['blitz-cloudfront'] = __DIR__ . '/templates/'; } ); @@ -142,6 +142,11 @@ public function purgeUrisWithProgress(array $siteUris, callable $setProgressHand $urls = SiteUriHelper::getUrlsFromSiteUris($siteUris); + // TODO: add a config setting. + if ($condenseUrls ?? true) { + $urls = $this->condenseUrls($urls); + } + $count = 0; $total = count($urls); $label = 'Purging {total} pages.'; @@ -151,7 +156,7 @@ public function purgeUrisWithProgress(array $siteUris, callable $setProgressHand call_user_func($setProgressHandler, $count, $total, $progressLabel); } - $paths = array_map(fn ($url) => $this->_getPathFromUrl($url), $urls); + $paths = array_map(fn($url) => $this->_getPathFromUrl($url), $urls); $this->_sendRequest($paths); @@ -187,6 +192,40 @@ public function getSettingsHtml(): ?string ]); } + /** + * Returns condensed URLs by adding a wildcard character where appropriate. + * This overly simplified method returns a single URL with a wildcard character after the longest common prefix between all URLs. + * + * @param string[] $urls + * @return string[] + */ + private function condenseUrls(array $urls): array + { + if (count($urls) < 2) { + return $urls; + } + + // Get the longest common prefix between the two most dissimilar strings. + sort($urls); + + return [$this->getLongestCommonPrefix(reset($urls), end($urls)) . '*']; + } + + /** + * Returns the longest common prefix between two strings. + */ + private function getLongestCommonPrefix($str1, $str2): string + { + $length = min(strlen($str1), strlen($str2)); + for ($i = 0; $i < $length; $i++) { + if ($str1[$i] !== $str2[$i]) { + return substr($str1, 0, $i); + } + } + + return substr($str1, 0, $length); + } + /** * Returns a path from a URL. */ From b1894d61c012acb100481f4afe13d92b04d5ccba Mon Sep 17 00:00:00 2001 From: Ben Croker Date: Mon, 26 Feb 2024 11:52:08 -0600 Subject: [PATCH 04/12] Add test --- .gitattributes | 1 + ecs.php | 1 + src/CloudFrontPurger.php | 4 +-- tests/README.md | 32 ++++++++++++++++++ tests/TESTS.md | 13 +++++++ tests/pest/.gitignore | 1 + tests/pest/Feature/CondenseUrlsTest.php | 24 +++++++++++++ tests/pest/Pest.php | 45 +++++++++++++++++++++++++ tests/pest/README.md | 35 +++++++++++++++++++ tests/pest/phpunit.xml | 12 +++++++ 10 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 tests/README.md create mode 100644 tests/TESTS.md create mode 100644 tests/pest/.gitignore create mode 100644 tests/pest/Feature/CondenseUrlsTest.php create mode 100644 tests/pest/Pest.php create mode 100644 tests/pest/README.md create mode 100644 tests/pest/phpunit.xml diff --git a/.gitattributes b/.gitattributes index 2bcebd8..1e5c2bb 100644 --- a/.gitattributes +++ b/.gitattributes @@ -4,6 +4,7 @@ /.gitignore export-ignore /ecs.php export-ignore /phpstan.neon export-ignore +/tests/ export-ignore # Auto detect text files and perform LF normalization * text=auto diff --git a/ecs.php b/ecs.php index ff09fdd..faffead 100644 --- a/ecs.php +++ b/ecs.php @@ -7,6 +7,7 @@ $ecsConfig->parallel(); $ecsConfig->paths([ __DIR__ . '/src', + __DIR__ . '/tests', __FILE__, ]); diff --git a/src/CloudFrontPurger.php b/src/CloudFrontPurger.php index 84f0235..452eb89 100644 --- a/src/CloudFrontPurger.php +++ b/src/CloudFrontPurger.php @@ -194,12 +194,12 @@ public function getSettingsHtml(): ?string /** * Returns condensed URLs by adding a wildcard character where appropriate. - * This overly simplified method returns a single URL with a wildcard character after the longest common prefix between all URLs. + * This overly simplified method returns a single URL with a wildcard character after the longest common prefix. * * @param string[] $urls * @return string[] */ - private function condenseUrls(array $urls): array + public function condenseUrls(array $urls): array { if (count($urls) < 2) { return $urls; diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000..fe4a161 --- /dev/null +++ b/tests/README.md @@ -0,0 +1,32 @@ +# Testing + +## Static Analysis + +To run static analysis on the module, +install [PHPStan for Craft CMS](https://github.com/craftcms/phpstan) and run the following command from the root of your project. + +```shell +./vendor/bin/phpstan analyse -c vendor/putyourlightson/craft-blitz-cloudfront/phpstan.neon --memory-limit 1G +``` + +## Easy Coding Standard + +To run the Easy Coding Standard on the plugin, install [ECS for Craft CMS](https://github.com/craftcms/ecs) and run the following command from the root of your project. + +```shell +./vendor/bin/ecs check -c vendor/putyourlightson/craft-blitz-cloudfront/ecs.php +``` + +## Pest Tests + +To run Pest tests, install [Craft Pest](https://craft-pest.com/) and run the following command from the root of your project. + +```shell +php craft pest/test --test-directory=vendor/putyourlightson/craft-blitz-cloudfront/tests/pest +``` + +Or to run a specific test. + +```shell +php craft pest/test --test-directory=vendor/putyourlightson/craft-blitz-cloudfront/tests/pest --filter=CacheRequestTest +``` diff --git a/tests/TESTS.md b/tests/TESTS.md new file mode 100644 index 0000000..8e33b9b --- /dev/null +++ b/tests/TESTS.md @@ -0,0 +1,13 @@ +# Test Specification + +This document outlines the test specification for the Blitz CloudFront module. + +--- + +## Feature Tests + +### [CondenseUrls](pest/Feature/CondenseUrlsTest.php) + +_Tests condensing URLs._ + +![Pass](https://raw.githubusercontent.com/putyourlightson/craft-generate-test-spec/main/icons/pass.svg) URLs are condensed into a single URL with a wildcard character after the longest common prefix. diff --git a/tests/pest/.gitignore b/tests/pest/.gitignore new file mode 100644 index 0000000..e2a69e6 --- /dev/null +++ b/tests/pest/.gitignore @@ -0,0 +1 @@ +/test-results.xml diff --git a/tests/pest/Feature/CondenseUrlsTest.php b/tests/pest/Feature/CondenseUrlsTest.php new file mode 100644 index 0000000..71cc287 --- /dev/null +++ b/tests/pest/Feature/CondenseUrlsTest.php @@ -0,0 +1,24 @@ +condenseUrls($urls)) + ->toBe(['https://example.com/foo*']); +}); diff --git a/tests/pest/Pest.php b/tests/pest/Pest.php new file mode 100644 index 0000000..62c3c13 --- /dev/null +++ b/tests/pest/Pest.php @@ -0,0 +1,45 @@ +in('./'); + +/* +|-------------------------------------------------------------------------- +| Expectations +|-------------------------------------------------------------------------- +| +| When you're writing tests, you often need to check that values meet certain conditions. The +| "expect()" function gives you access to a set of "expectations" methods that you can use +| to assert different things. Of course, you may extend the Expectation API at any time. +| +*/ + +/* +|-------------------------------------------------------------------------- +| Constants +|-------------------------------------------------------------------------- +*/ + +/* +|-------------------------------------------------------------------------- +| Functions +|-------------------------------------------------------------------------- +| +| While Pest is very powerful out-of-the-box, you may have some testing code specific to your +| project that you don't want to repeat in every file. Here you can also expose helpers as +| global functions to help you to reduce the number of lines of code in your test files. +| +*/ diff --git a/tests/pest/README.md b/tests/pest/README.md new file mode 100644 index 0000000..72198f2 --- /dev/null +++ b/tests/pest/README.md @@ -0,0 +1,35 @@ +# Testing + +## Usage + +1. Install the [Craft Pest](https://craft-pest.com) plugin. + ```shell + composer require-dev markhuot/craft-pest --dev + php craft plugin/install pest + ``` +2. Copy `phpunit.xml` to the root of your project. +3. Execute the following command from the root of your project. + ```shell + php craft pest/test --test-directory=vendor/putyourlightson/craft-blitz-cloudfront/tests/pest + ``` + +### Makefile + +A Makefile can be used to simplify the running of tests. + +```makefile +# Default values +vendor?=putyourlightson +plugin?=blitz-cloudfront +filter?=test +test: + php craft pest/test --test-directory=vendor/$(vendor)/craft-$(plugin)/tests/pest --filter=$(filter) +``` + +```shell +# Run tests using the default values +make test + +# Run tests using all optional values +make test vendor=putyourlightson plugin=blitz-cloudfront filter=queue +``` diff --git a/tests/pest/phpunit.xml b/tests/pest/phpunit.xml new file mode 100644 index 0000000..b6e27a1 --- /dev/null +++ b/tests/pest/phpunit.xml @@ -0,0 +1,12 @@ + + + + + . + + + From ccaad2fb00127b6e5fbdff39dddb7cf742b11bd4 Mon Sep 17 00:00:00 2001 From: Ben Croker Date: Mon, 26 Feb 2024 12:18:46 -0600 Subject: [PATCH 05/12] Refactor method name --- src/CloudFrontPurger.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/CloudFrontPurger.php b/src/CloudFrontPurger.php index 452eb89..d20c66e 100644 --- a/src/CloudFrontPurger.php +++ b/src/CloudFrontPurger.php @@ -193,7 +193,7 @@ public function getSettingsHtml(): ?string } /** - * Returns condensed URLs by adding a wildcard character where appropriate. + * Returns condensed URLs by eagerly adding a wildcard character. * This overly simplified method returns a single URL with a wildcard character after the longest common prefix. * * @param string[] $urls @@ -208,13 +208,13 @@ public function condenseUrls(array $urls): array // Get the longest common prefix between the two most dissimilar strings. sort($urls); - return [$this->getLongestCommonPrefix(reset($urls), end($urls)) . '*']; + return [$this->_getLongestCommonPrefix(reset($urls), end($urls)) . '*']; } /** * Returns the longest common prefix between two strings. */ - private function getLongestCommonPrefix($str1, $str2): string + private function _getLongestCommonPrefix($str1, $str2): string { $length = min(strlen($str1), strlen($str2)); for ($i = 0; $i < $length; $i++) { From b8b115d750b05cf7f187e2a81867a251f19a4a9a Mon Sep 17 00:00:00 2001 From: Ben Croker Date: Mon, 26 Feb 2024 17:14:57 -0600 Subject: [PATCH 06/12] Add lightswitch setting --- CHANGELOG.md | 2 +- src/CloudFrontPurger.php | 12 ++++++++---- src/templates/settings.twig | 14 ++++++++++++++ tests/pest/Feature/CondenseUrlsTest.php | 2 +- 4 files changed, 24 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c63355d..fba5fe2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ ## 3.1.0 - Unreleased ### Added -- Added a `condenseUrls` config setting that condenses multiple URLs into a single invalidation request with a wildcard character after the longest common prefix between all URLs. +- Added a `condenseUrls` setting that can help reduce the number of invalidation paths sent to the CloudFront API, potentially saving on invalidation request charges. ## 3.0.4 - 2023-02-15 ### Fixed diff --git a/src/CloudFrontPurger.php b/src/CloudFrontPurger.php index d20c66e..69ee7a2 100644 --- a/src/CloudFrontPurger.php +++ b/src/CloudFrontPurger.php @@ -48,6 +48,11 @@ class CloudFrontPurger extends BaseCachePurger */ public string $distributionId = ''; + /** + * @var bool + */ + public bool $condenseUrls = false; + /** * @var string */ @@ -142,9 +147,8 @@ public function purgeUrisWithProgress(array $siteUris, callable $setProgressHand $urls = SiteUriHelper::getUrlsFromSiteUris($siteUris); - // TODO: add a config setting. - if ($condenseUrls ?? true) { - $urls = $this->condenseUrls($urls); + if ($this->condenseUrls) { + $urls = $this->getCondensedUrls($urls); } $count = 0; @@ -199,7 +203,7 @@ public function getSettingsHtml(): ?string * @param string[] $urls * @return string[] */ - public function condenseUrls(array $urls): array + public function getCondensedUrls(array $urls): array { if (count($urls) < 2) { return $urls; diff --git a/src/templates/settings.twig b/src/templates/settings.twig index bdfc78f..b1a9e48 100644 --- a/src/templates/settings.twig +++ b/src/templates/settings.twig @@ -37,3 +37,17 @@ errors: purger.getErrors('distributionId'), required: true, }) }} + +{% set info %} + + {{- "Enabling this can help reduce the number of invalidation paths sent to the CloudFront API, potentially saving on invalidation request charges. This works by condensing multiple invalidation URLs into a single URL with a wildcard character after the longest common prefix. A path that includes the `*` wildcard counts as one path even if it causes CloudFront to invalidate thousands of files."|t('blitz') -}} + +{% endset %} +{{ forms.lightswitchField({ + label: "Condense URLs"|t('blitz'), + id: 'condenseUrls', + name: 'condenseUrls', + instructions: "Whether to condense multiple invalidation URLs into a single URL with a wildcard."|t('blitz') ~ info, + on: purger.condenseUrls, + errors: purger.getErrors('condenseUrls'), +}) }} diff --git a/tests/pest/Feature/CondenseUrlsTest.php b/tests/pest/Feature/CondenseUrlsTest.php index 71cc287..a6c797c 100644 --- a/tests/pest/Feature/CondenseUrlsTest.php +++ b/tests/pest/Feature/CondenseUrlsTest.php @@ -19,6 +19,6 @@ 'https://example.com/foo/bar/baz', ]; - expect($purger->condenseUrls($urls)) + expect($purger->getCondensedUrls($urls)) ->toBe(['https://example.com/foo*']); }); From fe7ea3fbefc69316f1310d5be4a614c4df84ba9c Mon Sep 17 00:00:00 2001 From: Ben Croker Date: Wed, 28 Feb 2024 16:48:13 -0600 Subject: [PATCH 07/12] Better test --- tests/pest/Feature/CondenseUrlsTest.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/pest/Feature/CondenseUrlsTest.php b/tests/pest/Feature/CondenseUrlsTest.php index a6c797c..c34b058 100644 --- a/tests/pest/Feature/CondenseUrlsTest.php +++ b/tests/pest/Feature/CondenseUrlsTest.php @@ -14,11 +14,12 @@ test('URLs are condensed into a single URL with a wildcard character after the longest common prefix', function() { $purger = new CloudFrontPurger(); $urls = [ - 'https://example.com/foo/bar/baz/qux', 'https://example.com/foo', + 'https://example.com/foo/bar', 'https://example.com/foo/bar/baz', + 'https://example.com/fun', ]; expect($purger->getCondensedUrls($urls)) - ->toBe(['https://example.com/foo*']); + ->toBe(['https://example.com/f*']); }); From ae4c21ffd3bb515fe7a6e3f604c732f6ff86918e Mon Sep 17 00:00:00 2001 From: Ben Croker Date: Tue, 19 Mar 2024 19:30:14 -0600 Subject: [PATCH 08/12] Convert to plugin --- CHANGELOG.md | 74 ++----------------------------------- README.md | 31 ++++++++-------- composer.json | 74 +++++++++++++++++++------------------ src/Plugin.php | 28 ++++++++++++++ src/icon.svg | 1 + src/templates/settings.twig | 21 +++++------ 6 files changed, 97 insertions(+), 132 deletions(-) create mode 100644 src/Plugin.php create mode 100644 src/icon.svg diff --git a/CHANGELOG.md b/CHANGELOG.md index fba5fe2..7681589 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,77 +1,9 @@ # Release Notes for Blitz CloudFront Purger -## 3.1.0 - Unreleased -### Added -- Added a `condenseUrls` setting that can help reduce the number of invalidation paths sent to the CloudFront API, potentially saving on invalidation request charges. - -## 3.0.4 - 2023-02-15 -### Fixed -- Fixed invalidation errors when URLs contained the `*` character in paths ([#10](https://github.com/putyourlightson/craft-blitz-cloudfront/issues/10)). - -## 3.0.3 - 2022-11-09 -### Fixed -- Fixed a bug in which the CloudFront Purger could throw errors when purging site URIs. - ([#9](https://github.com/putyourlightson/craft-blitz-cloudfront/issues/9)). - -## 3.0.2 - 2022-11-02 -### Fixed -- Fixed a bug in which test invalidations could cause errors ([#8](https://github.com/putyourlightson/craft-blitz-cloudfront/issues/8)). +## 4.0.0 - Unreleased -## 3.0.1 - 2022-11-01 -### Fixed -- Fixed a bug in which logging exceptions was causing an error due to a non-existent log level ([#7](https://github.com/putyourlightson/craft-blitz-cloudfront/issues/7)). +> {warning} Blitz CloudFront Purger is now a Craft CMS plugin rather than a PHP package, and as such it must be installed via Craft to be usable. -## 3.0.0 - 2022-07-04 ### Added -- Added compatibility with Craft 4. -### Changed -- Changed minimum required version of Blitz to 4.0.0. - -## 2.0.9 - 2022-02-01 -### Fixed -- Fixed invalidating paths that were missing trailing slashes when the `addTrailingSlashesToUrls` general config was `true`. - -## 2.0.8 - 2022-01-02 -### Fixed -- Fixed invalidation errors by URL encoding non-reserved special characters in paths ([#6](https://github.com/putyourlightson/craft-blitz-cloudfront/issues/6)). - -## 2.0.7 - 2021-12-13 -### Fixed -- Fixed an error that could be thrown if AWS returned a null error message ([#5](https://github.com/putyourlightson/craft-blitz-cloudfront/issues/5)). - -## 2.0.6 - 2020-12-07 -### Added -- Added the “Warm Cache Delay” setting to the purger settings page. -- Added logging of exceptions on failed requests to CloudFront. - -## 2.0.5 - 2020-10-25 -### Fixed -- Fixed an issue in which `Quantity` did not match the number of `Items` ([#4](https://github.com/putyourlightson/craft-blitz-cloudfront/issues/4)). - -## 2.0.4 - 2020-08-17 -### Changed -- If either the API key or secret is empty then it is assumed that we are running on EC2 and we have an IAM role assigned ([#3](https://github.com/putyourlightson/craft-blitz-cloudfront/issues/3)). -- Site URIs to purge can be modified using the `EVENT_BEFORE_PURGE_CACHE` event. - -### Fixed -- The Distribution ID can now be set to an environment variable ([#2](https://github.com/putyourlightson/craft-blitz-cloudfront/issues/2)). - -## 2.0.3 - 2020-03-31 -### Fixed -- Fixed a bug when purging individual URIs ([#1](https://github.com/putyourlightson/craft-blitz-cloudfront/issues/1)). - -## 2.0.2 - 2020-01-29 -### Changed -- Removed the regions setting as the CloudFront service endpoint only allows connecting through the `us-east-1` region. - -## 2.0.1 - 2020-01-25 -### Fixed -- Fixed a bug in getting URLS to purge. - -## 2.0.0 - 2020-01-22 -### Changed -- Changed minimum required version of Blitz to 3.0.0. - -## 1.0.0 - 2019-04-15 -- Initial release. +- Added a `condenseUrls` setting that can help reduce the number of invalidation paths sent to the CloudFront API, potentially saving on invalidation request charges. diff --git a/README.md b/README.md index b3f691e..17f5d2f 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@

-# Blitz CloudFront Purger for Craft CMS +# Blitz CloudFront Purger Plugin for Craft CMS The CloudFront Purger allows the [Blitz](https://putyourlightson.com/plugins/blitz) plugin for [Craft CMS](https://craftcms.com/) to intelligently purge cached pages. @@ -13,21 +13,24 @@ The CloudFront Purger allows the [Blitz](https://putyourlightson.com/plugins/bli Source: [docs.aws.amazon.com](https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Invalidation.html#PayingForInvalidation) -## Usage +## Documentation -Install the purger using composer. +Read the documentation at [putyourlightson.com/plugins/blitz](https://putyourlightson.com/plugins/blitz#reverse-proxy-purgers). -```shell -composer require putyourlightson/craft-blitz-cloudfront -``` +## License -Then add the class to the `cachePurgerTypes` config setting in `config/blitz.php`. +This plugin is licensed for free under the MIT License. -```php -// The purger type classes to add to the plugin’s default purger types. -'cachePurgerTypes' => [ - 'putyourlightson\blitzcloudfront\CloudFrontPurger', -], +## Requirements + +This plugin requires [Craft CMS](https://craftcms.com/) 3.0.0 or later, or 4.0.0 or later. + +## Installation + +To install the plugin, search for “Blitz CloudFront Purger” in the Craft Plugin Store, or install manually using composer. + +```shell +composer require putyourlightson/craft-blitz-cloudfront ``` You can then select the purger and settings either in the control panel or in `config/blitz.php`. @@ -46,8 +49,6 @@ You can then select the purger and settings either in the control panel or in `c ], ``` -## Documentation - -Read the documentation at [putyourlightson.com/plugins/blitz](https://putyourlightson.com/plugins/blitz#reverse-proxy-purgers). +--- Created by [PutYourLightsOn](https://putyourlightson.com/). diff --git a/composer.json b/composer.json index 2e25204..bb9228c 100644 --- a/composer.json +++ b/composer.json @@ -1,38 +1,42 @@ { - "name": "putyourlightson/craft-blitz-cloudfront", - "description": "CloudFront cache purger for the Blitz plugin.", - "version": "3.0.4", - "type": "blitz-purger", - "license": "proprietary", - "keywords": [ - "craft", - "cms", - "craftcms", - "plugin", - "cache", - "blitz", - "purger" - ], - "require": { - "php": "^8.0.2", - "craftcms/cms": "^4.0", - "putyourlightson/craft-blitz": "^4.0", - "aws/aws-sdk-php": "^3.0" - }, - "autoload": { - "psr-4": { - "putyourlightson\\blitzcloudfront\\": "src/" - } - }, - "support": { - "docs": "https://github.com/putyourlightson/craft-blitz-cloudfront", - "source": "https://github.com/putyourlightson/craft-blitz-cloudfront", - "issues": "https://github.com/putyourlightson/craft-blitz-cloudfront/issues" - }, - "extra": { - "name": "Blitz CloudFront Purger", - "developer": "PutYourLightsOn", - "developerUrl": "https://putyourlightson.com/", - "changelogUrl": "https://raw.githubusercontent.com/putyourlightson/craft-blitz-cloudfront/v3/CHANGELOG.md" + "name": "putyourlightson/craft-blitz-cloudfront", + "description": "CloudFront cache purger for the Blitz plugin.", + "version": "4.0.0", + "type": "craft-plugin", + "homepage": "https://putyourlightson.com/plugins/blitz", + "license": "proprietary", + "keywords": [ + "craftcms", + "amazon", + "aws", + "cloudfront", + "cache", + "blitz", + "purger" + ], + "require": { + "php": "^8.0.2", + "craftcms/cms": "^4.0", + "putyourlightson/craft-blitz": "^4.0", + "aws/aws-sdk-php": "^3.0" + }, + "autoload": { + "psr-4": { + "putyourlightson\\blitzcloudfront\\": "src/" } + }, + "support": { + "docs": "https://github.com/putyourlightson/craft-blitz-cloudfront", + "source": "https://github.com/putyourlightson/craft-blitz-cloudfront", + "issues": "https://github.com/putyourlightson/craft-blitz-cloudfront/issues" + }, + "extra": { + "name": "Blitz CloudFront Purger", + "handle": "blitz-cloudfront-purger", + "developer": "PutYourLightsOn", + "developerUrl": "https://putyourlightson.com/", + "documentationUrl": "https://putyourlightson.com/plugins/blitz", + "changelogUrl": "https://raw.githubusercontent.com/putyourlightson/craft-blitz-cloudfront/v4/CHANGELOG.md", + "class": "putyourlightson\\blitzcloudfront\\Plugin" + } } diff --git a/src/Plugin.php b/src/Plugin.php new file mode 100644 index 0000000..4d9f50c --- /dev/null +++ b/src/Plugin.php @@ -0,0 +1,28 @@ +types[] = CloudFrontPurger::class; + } + ); + } +} diff --git a/src/icon.svg b/src/icon.svg new file mode 100644 index 0000000..430d002 --- /dev/null +++ b/src/icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/templates/settings.twig b/src/templates/settings.twig index b1a9e48..30f189c 100644 --- a/src/templates/settings.twig +++ b/src/templates/settings.twig @@ -1,11 +1,10 @@ -{% import "_includes/forms" as forms %} - +{% import '_includes/forms' as forms %} {{ forms.autosuggestField({ - label: "API Key"|t('blitz'), + label: 'API Key'|t('blitz'), id: 'apiKey', name: 'apiKey', - instructions: "An API key for your AWS account."|t('blitz'), + instructions: 'An API key for your AWS account.'|t('blitz'), suggestEnvVars: true, suggestions: craft.cp.getEnvSuggestions(), value: purger.apiKey, @@ -15,10 +14,10 @@ }) }} {{ forms.autosuggestField({ - label: "API Secret"|t('blitz'), + label: 'API Secret'|t('blitz'), id: 'apiSecret', name: 'apiSecret', - instructions: "An API secret for your AWS account."|t('blitz'), + instructions: 'An API secret for your AWS account.'|t('blitz'), suggestEnvVars: true, suggestions: craft.cp.getEnvSuggestions(), value: purger.apiSecret, @@ -27,10 +26,10 @@ }) }} {{ forms.autosuggestField({ - label: "Distribution ID"|t('blitz'), + label: 'Distribution ID'|t('blitz'), id: 'distributionId', name: 'distributionId', - instructions: "The ID of the CloudFlare distribution that should be purged."|t('blitz'), + instructions: 'The ID of the CloudFlare distribution that should be purged.'|t('blitz'), suggestEnvVars: true, suggestions: craft.cp.getEnvSuggestions(), value: purger.distributionId, @@ -40,14 +39,14 @@ {% set info %} - {{- "Enabling this can help reduce the number of invalidation paths sent to the CloudFront API, potentially saving on invalidation request charges. This works by condensing multiple invalidation URLs into a single URL with a wildcard character after the longest common prefix. A path that includes the `*` wildcard counts as one path even if it causes CloudFront to invalidate thousands of files."|t('blitz') -}} + {{- 'Enabling this can help reduce the number of invalidation paths sent to the CloudFront API, potentially saving on invalidation request charges. This works by condensing multiple invalidation URLs into a single URL with a wildcard character after the longest common prefix. A path that includes the `*` wildcard counts as one path even if it causes CloudFront to invalidate thousands of files.'|t('blitz') -}} {% endset %} {{ forms.lightswitchField({ - label: "Condense URLs"|t('blitz'), + label: 'Condense URLs'|t('blitz'), id: 'condenseUrls', name: 'condenseUrls', - instructions: "Whether to condense multiple invalidation URLs into a single URL with a wildcard."|t('blitz') ~ info, + instructions: 'Whether to condense multiple invalidation URLs into a single URL with a wildcard.'|t('blitz') ~ info, on: purger.condenseUrls, errors: purger.getErrors('condenseUrls'), }) }} From 72331daa95c070936f32782d25b5db2940b54c2b Mon Sep 17 00:00:00 2001 From: Ben Croker Date: Tue, 19 Mar 2024 19:42:47 -0600 Subject: [PATCH 09/12] Prepare release --- CHANGELOG.md | 4 ++-- composer.json | 23 ++++++++++++++++++++++- src/templates/settings.twig | 18 +++++++++--------- 3 files changed, 33 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7681589..b8a0135 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,8 @@ # Release Notes for Blitz CloudFront Purger -## 4.0.0 - Unreleased +## 4.0.0 - 2024-03-19 -> {warning} Blitz CloudFront Purger is now a Craft CMS plugin rather than a PHP package, and as such it must be installed via Craft to be usable. +> {warning} Blitz CloudFront Purger is now a Craft CMS plugin rather than a PHP package, and as such it must be installed via Craft to be usable. You should manually remove `CloudFrontPurger` from the `cachePurgerTypes` config setting in your `config/blitz.php` file, if it exists, since the plugin now registers the purger automatically. ### Added diff --git a/composer.json b/composer.json index bb9228c..94608ac 100644 --- a/composer.json +++ b/composer.json @@ -20,11 +20,32 @@ "putyourlightson/craft-blitz": "^4.0", "aws/aws-sdk-php": "^3.0" }, + "require-dev": { + "craftcms/ecs": "dev-main", + "craftcms/phpstan": "dev-main", + "markhuot/craft-pest-core": "^2.0.0-rc2", + "mockery/mockery": "^1.0", + "putyourlightson/craft-generate-test-spec": "v2.x-dev" + }, + "scripts": { + "phpstan": "phpstan --ansi --memory-limit=1G", + "check-cs": "ecs check --ansi", + "fix-cs": "ecs check --fix --ansi" + }, "autoload": { "psr-4": { "putyourlightson\\blitzcloudfront\\": "src/" } }, + "config": { + "allow-plugins": { + "craftcms/plugin-installer": true, + "pestphp/pest-plugin": true, + "yiisoft/yii2-composer": true + }, + "optimize-autoloader": true, + "sort-packages": true + }, "support": { "docs": "https://github.com/putyourlightson/craft-blitz-cloudfront", "source": "https://github.com/putyourlightson/craft-blitz-cloudfront", @@ -32,7 +53,7 @@ }, "extra": { "name": "Blitz CloudFront Purger", - "handle": "blitz-cloudfront-purger", + "handle": "blitz-cloudfront", "developer": "PutYourLightsOn", "developerUrl": "https://putyourlightson.com/", "documentationUrl": "https://putyourlightson.com/plugins/blitz", diff --git a/src/templates/settings.twig b/src/templates/settings.twig index 30f189c..42bbfc5 100644 --- a/src/templates/settings.twig +++ b/src/templates/settings.twig @@ -1,10 +1,10 @@ {% import '_includes/forms' as forms %} {{ forms.autosuggestField({ - label: 'API Key'|t('blitz'), + label: 'API Key'|t('blitz-cloudfront'), id: 'apiKey', name: 'apiKey', - instructions: 'An API key for your AWS account.'|t('blitz'), + instructions: 'An API key for your AWS account.'|t('blitz-cloudfront'), suggestEnvVars: true, suggestions: craft.cp.getEnvSuggestions(), value: purger.apiKey, @@ -14,10 +14,10 @@ }) }} {{ forms.autosuggestField({ - label: 'API Secret'|t('blitz'), + label: 'API Secret'|t('blitz-cloudfront'), id: 'apiSecret', name: 'apiSecret', - instructions: 'An API secret for your AWS account.'|t('blitz'), + instructions: 'An API secret for your AWS account.'|t('blitz-cloudfront'), suggestEnvVars: true, suggestions: craft.cp.getEnvSuggestions(), value: purger.apiSecret, @@ -26,10 +26,10 @@ }) }} {{ forms.autosuggestField({ - label: 'Distribution ID'|t('blitz'), + label: 'Distribution ID'|t('blitz-cloudfront'), id: 'distributionId', name: 'distributionId', - instructions: 'The ID of the CloudFlare distribution that should be purged.'|t('blitz'), + instructions: 'The ID of the CloudFlare distribution that should be purged.'|t('blitz-cloudfront'), suggestEnvVars: true, suggestions: craft.cp.getEnvSuggestions(), value: purger.distributionId, @@ -39,14 +39,14 @@ {% set info %} - {{- 'Enabling this can help reduce the number of invalidation paths sent to the CloudFront API, potentially saving on invalidation request charges. This works by condensing multiple invalidation URLs into a single URL with a wildcard character after the longest common prefix. A path that includes the `*` wildcard counts as one path even if it causes CloudFront to invalidate thousands of files.'|t('blitz') -}} + {{- 'Enabling this can help reduce the number of invalidation paths sent to the CloudFront API, potentially saving on invalidation request charges. This works by condensing multiple invalidation URLs into a single URL with a wildcard character after the longest common prefix. A path that includes the `*` wildcard counts as one path even if it causes CloudFront to invalidate thousands of files.'|t('blitz-cloudfront') -}} {% endset %} {{ forms.lightswitchField({ - label: 'Condense URLs'|t('blitz'), + label: 'Condense URLs'|t('blitz-cloudfront'), id: 'condenseUrls', name: 'condenseUrls', - instructions: 'Whether to condense multiple invalidation URLs into a single URL with a wildcard.'|t('blitz') ~ info, + instructions: 'Whether to condense multiple invalidation URLs into a single URL with a wildcard.'|t('blitz-cloudfront') ~ info, on: purger.condenseUrls, errors: purger.getErrors('condenseUrls'), }) }} From fcd8a6129f0d18809af0fb585986136a5d787ec8 Mon Sep 17 00:00:00 2001 From: Ben Croker Date: Tue, 19 Mar 2024 19:45:50 -0600 Subject: [PATCH 10/12] New icon --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 17f5d2f..d50d6b6 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ [![Stable Version](https://img.shields.io/packagist/v/putyourlightson/craft-blitz-cloudfront?label=stable)]((https://packagist.org/packages/putyourlightson/craft-blitz-cloudfront)) [![Total Downloads](https://img.shields.io/packagist/dt/putyourlightson/craft-blitz-cloudfront)](https://packagist.org/packages/putyourlightson/craft-blitz-cloudfront) -

+

# Blitz CloudFront Purger Plugin for Craft CMS -The CloudFront Purger allows the [Blitz](https://putyourlightson.com/plugins/blitz) plugin for [Craft CMS](https://craftcms.com/) to intelligently purge cached pages. +The CloudFront Purger plugin allows the [Blitz](https://putyourlightson.com/plugins/blitz) plugin for [Craft CMS](https://craftcms.com/) to intelligently purge cached pages. **Note that Amazon CloudFront charges for invalidation requests. Since invalidation requests can quickly add up when purging individual URLs, you should be aware of the potential costs. PutYourLightsOn takes no responsibility whatsoever for expenses incurred.** From 2857b4a344cfdee0af58ee52a886da31788c1757 Mon Sep 17 00:00:00 2001 From: Ben Croker Date: Tue, 19 Mar 2024 19:52:24 -0600 Subject: [PATCH 11/12] Fix docs URL --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 94608ac..b0a7717 100644 --- a/composer.json +++ b/composer.json @@ -56,7 +56,7 @@ "handle": "blitz-cloudfront", "developer": "PutYourLightsOn", "developerUrl": "https://putyourlightson.com/", - "documentationUrl": "https://putyourlightson.com/plugins/blitz", + "documentationUrl": "https://github.com/putyourlightson/craft-blitz-cloudfront", "changelogUrl": "https://raw.githubusercontent.com/putyourlightson/craft-blitz-cloudfront/v4/CHANGELOG.md", "class": "putyourlightson\\blitzcloudfront\\Plugin" } From f323c61d6c20892151edc16bc6315fffabf245cd Mon Sep 17 00:00:00 2001 From: Ben Croker Date: Tue, 19 Mar 2024 19:59:16 -0600 Subject: [PATCH 12/12] Refactoring --- src/CloudFrontPurger.php | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/CloudFrontPurger.php b/src/CloudFrontPurger.php index 69ee7a2..23cc718 100644 --- a/src/CloudFrontPurger.php +++ b/src/CloudFrontPurger.php @@ -33,6 +33,11 @@ class CloudFrontPurger extends BaseCachePurger */ public const REGION = 'us-east-1'; + /** + * @var string + */ + public const VERSION = 'latest'; + /** * @var string */ @@ -53,11 +58,6 @@ class CloudFrontPurger extends BaseCachePurger */ public bool $condenseUrls = false; - /** - * @var string - */ - private string $_version = 'latest'; - /** * @inheritdoc */ @@ -129,7 +129,7 @@ public function purgeAll(callable $setProgressHandler = null, bool $queue = true return; } - $this->_sendRequest(['/*']); + $this->sendRequest(['/*']); if ($this->hasEventHandlers(self::EVENT_AFTER_PURGE_ALL_CACHE)) { $this->trigger(self::EVENT_AFTER_PURGE_ALL_CACHE, $event); @@ -160,9 +160,9 @@ public function purgeUrisWithProgress(array $siteUris, callable $setProgressHand call_user_func($setProgressHandler, $count, $total, $progressLabel); } - $paths = array_map(fn($url) => $this->_getPathFromUrl($url), $urls); + $paths = array_map(fn($url) => $this->getPathFromUrl($url), $urls); - $this->_sendRequest($paths); + $this->sendRequest($paths); $count = $total; @@ -177,7 +177,7 @@ public function purgeUrisWithProgress(array $siteUris, callable $setProgressHand */ public function test(): bool { - $response = $this->_sendRequest(['/test']); + $response = $this->sendRequest(['/test']); if (!$response) { return false; @@ -212,13 +212,13 @@ public function getCondensedUrls(array $urls): array // Get the longest common prefix between the two most dissimilar strings. sort($urls); - return [$this->_getLongestCommonPrefix(reset($urls), end($urls)) . '*']; + return [$this->getLongestCommonPrefix(reset($urls), end($urls)) . '*']; } /** * Returns the longest common prefix between two strings. */ - private function _getLongestCommonPrefix($str1, $str2): string + private function getLongestCommonPrefix($str1, $str2): string { $length = min(strlen($str1), strlen($str2)); for ($i = 0; $i < $length; $i++) { @@ -233,7 +233,7 @@ private function _getLongestCommonPrefix($str1, $str2): string /** * Returns a path from a URL. */ - private function _getPathFromUrl(string $url): string + private function getPathFromUrl(string $url): string { $queryString = parse_url($url, PHP_URL_QUERY); $path = parse_url($url, PHP_URL_PATH); @@ -257,10 +257,10 @@ private function _getPathFromUrl(string $url): string /** * Sends a request to the API. */ - private function _sendRequest(array $paths): bool + private function sendRequest(array $paths): bool { $config = [ - 'version' => $this->_version, + 'version' => self::VERSION, 'region' => self::REGION, ];