From 0635615149ae0c6eea311601f9377d4bf20e430e Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Thu, 17 Oct 2024 15:03:55 +0200 Subject: [PATCH 01/25] Run RoadRunner in docker with exec to ensure signals are properly handled --- CHANGELOG.md | 17 +++++++++++++++++ data/infra/roadrunner.Dockerfile | 4 ++-- docker/docker-entrypoint.sh | 3 ++- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7575178b..4f821b7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,23 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com), and this project adheres to [Semantic Versioning](https://semver.org). +## [Unreleased] +### Added +* *Nothing* + +### Changed +* Run RoadRunner in docker with `exec` to ensure signals are properly handled. + +### Deprecated +* *Nothing* + +### Removed +* *Nothing* + +### Fixed +* *Nothing* + + ## [4.2.3] - 2024-10-17 ### Added * *Nothing* diff --git a/data/infra/roadrunner.Dockerfile b/data/infra/roadrunner.Dockerfile index 33768eda..006aea91 100644 --- a/data/infra/roadrunner.Dockerfile +++ b/data/infra/roadrunner.Dockerfile @@ -72,5 +72,5 @@ CMD \ if [[ ! -d "./vendor" ]]; then /usr/local/bin/composer install ; fi && \ # Download roadrunner binary if [[ ! -f "./bin/rr" ]]; then ./vendor/bin/rr get --no-interaction --no-config --location bin/ && chmod +x bin/rr ; fi && \ - # This forces the app to be started every second until the exit code is 0 - until ./bin/rr serve -c config/roadrunner/.rr.dev.yml; do sleep 1 ; done + # Run with `exec` so that signals are properly handled + exec ./bin/rr serve -c config/roadrunner/.rr.dev.yml diff --git a/docker/docker-entrypoint.sh b/docker/docker-entrypoint.sh index e2cce68f..e1acd118 100644 --- a/docker/docker-entrypoint.sh +++ b/docker/docker-entrypoint.sh @@ -26,5 +26,6 @@ fi php vendor/bin/shlink-installer init ${flags} if [ "$SHLINK_RUNTIME" = 'rr' ]; then - ./bin/rr serve -c config/roadrunner/.rr.yml + # Run with `exec` so that signals are properly handled + exec ./bin/rr serve -c config/roadrunner/.rr.yml fi From 9b167497376c2b9bcefddfc767ce461222483351 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Thu, 17 Oct 2024 16:27:38 +0200 Subject: [PATCH 02/25] Remove twitter badge from readme --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 7f8cc164..77cbaa43 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,6 @@ [![Mastodon](https://img.shields.io/mastodon/follow/109329425426175098?color=%236364ff&domain=https%3A%2F%2Ffosstodon.org&label=follow&logo=mastodon&logoColor=white&style=flat-square)](https://fosstodon.org/@shlinkio) [![Bluesky](https://img.shields.io/badge/follow-shlinkio-0285FF.svg?style=flat-square&logo=bluesky&logoColor=white)](https://bsky.app/profile/shlinkio.bsky.social) -[![Twitter](https://img.shields.io/badge/follow-shlinkio-blue.svg?style=flat-square&logo=x&color=black)](https://twitter.com/shlinkio) [![Paypal donate](https://img.shields.io/badge/Donate-paypal-blue.svg?style=flat-square&logo=paypal&colorA=aaaaaa)](https://slnk.to/donate) A PHP-based self-hosted URL shortener that can be used to serve shortened URLs under your own domain. From 044efe6ee44ee5a9224ef9608a64c5befcc2d76e Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Mon, 21 Oct 2024 11:54:45 +0200 Subject: [PATCH 03/25] Switch to xdebug for code coverage reports --- .github/actions/ci-setup/action.yml | 3 +-- .gitignore | 1 - bin/test/run-api-tests.sh | 2 ++ composer.json | 10 +++++----- data/infra/php.Dockerfile | 9 +++++---- data/infra/php.ini | 2 -- data/infra/roadrunner.Dockerfile | 9 +++++---- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/.github/actions/ci-setup/action.yml b/.github/actions/ci-setup/action.yml index f800cf09..37ec30df 100644 --- a/.github/actions/ci-setup/action.yml +++ b/.github/actions/ci-setup/action.yml @@ -40,8 +40,7 @@ runs: php-version: ${{ inputs.php-version }} tools: composer extensions: ${{ inputs.php-extensions }} - coverage: pcov - ini-values: pcov.directory=module + coverage: xdebug - name: Install dependencies if: ${{ inputs.install-deps == 'yes' }} run: composer install --no-interaction --prefer-dist ${{ inputs.php-version == '8.4' && '--ignore-platform-req=php' || '' }} diff --git a/.gitignore b/.gitignore index a7f9b895..04c8ed56 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,3 @@ docs/mercure.html docker-compose.override.yml .phpunit.result.cache docs/swagger/swagger-inlined.json -phpcov* diff --git a/bin/test/run-api-tests.sh b/bin/test/run-api-tests.sh index b96cf671..ffc152b7 100755 --- a/bin/test/run-api-tests.sh +++ b/bin/test/run-api-tests.sh @@ -6,6 +6,8 @@ export TEST_RUNTIME="${TEST_RUNTIME:-"rr"}" # rr is the only runtime currently s export DB_DRIVER="${DB_DRIVER:-"postgres"}" export GENERATE_COVERAGE="${GENERATE_COVERAGE:-"no"}" +[ "$GENERATE_COVERAGE" != 'no' ] && export XDEBUG_MODE=coverage + # Reset logs OUTPUT_LOGS=data/log/api-tests/output.log rm -rf data/log/api-tests diff --git a/composer.json b/composer.json index 8c85e1eb..013f03ee 100644 --- a/composer.json +++ b/composer.json @@ -120,11 +120,11 @@ "@parallel test:api test:cli" ], "test:unit": "COLUMNS=120 vendor/bin/phpunit --order-by=random --testdox --testdox-summary", - "test:unit:ci": "@test:unit --coverage-php=build/coverage-unit.cov", - "test:unit:pretty": "@test:unit --coverage-html build/coverage-unit/coverage-html", + "test:unit:ci": "XDEBUG_MODE=coverage composer test:unit -- --coverage-php=build/coverage-unit.cov", + "test:unit:pretty": "XDEBUG_MODE=coverage composer test:unit -- --coverage-html build/coverage-unit/coverage-html", "test:db": "@parallel test:db:sqlite:ci test:db:mysql test:db:maria test:db:postgres test:db:ms", "test:db:sqlite": "APP_ENV=test php vendor/bin/phpunit --order-by=random --testdox --testdox-summary -c phpunit-db.xml", - "test:db:sqlite:ci": "@test:db:sqlite --coverage-php build/coverage-db.cov", + "test:db:sqlite:ci": "XDEBUG_MODE=coverage composer test:db:sqlite -- --coverage-php build/coverage-db.cov", "test:db:mysql": "DB_DRIVER=mysql composer test:db:sqlite -- $*", "test:db:maria": "DB_DRIVER=maria composer test:db:sqlite -- $*", "test:db:postgres": "DB_DRIVER=postgres composer test:db:sqlite -- $*", @@ -137,8 +137,8 @@ "test:api:ci": "GENERATE_COVERAGE=yes composer test:api && vendor/bin/phpcov merge build/coverage-api --php build/coverage-api.cov && rm build/coverage-api/*.cov", "test:api:pretty": "GENERATE_COVERAGE=yes composer test:api && vendor/bin/phpcov merge build/coverage-api --html build/coverage-api/coverage-html && rm build/coverage-api/*.cov", "test:cli": "APP_ENV=test DB_DRIVER=maria TEST_ENV=cli php vendor/bin/phpunit --order-by=random --testdox --testdox-summary -c phpunit-cli.xml", - "test:cli:ci": "GENERATE_COVERAGE=yes composer test:cli && vendor/bin/phpcov merge build/coverage-cli --php build/coverage-cli.cov && rm build/coverage-cli/*.cov", - "test:cli:pretty": "GENERATE_COVERAGE=yes composer test:cli && vendor/bin/phpcov merge build/coverage-cli --html build/coverage-cli/coverage-html && rm build/coverage-cli/*.cov", + "test:cli:ci": "XDEBUG_MODE=coverage GENERATE_COVERAGE=yes composer test:cli && vendor/bin/phpcov merge build/coverage-cli --php build/coverage-cli.cov && rm build/coverage-cli/*.cov", + "test:cli:pretty": "XDEBUG_MODE=coverage GENERATE_COVERAGE=yes composer test:cli && vendor/bin/phpcov merge build/coverage-cli --html build/coverage-cli/coverage-html && rm build/coverage-cli/*.cov", "swagger:validate": "php-openapi validate docs/swagger/swagger.json", "swagger:inline": "php-openapi inline docs/swagger/swagger.json docs/swagger/swagger-inlined.json", "clean:dev": "rm -f data/database.sqlite && rm -f config/params/generated_config.php" diff --git a/data/infra/php.Dockerfile b/data/infra/php.Dockerfile index 20732e3f..4a7904bf 100644 --- a/data/infra/php.Dockerfile +++ b/data/infra/php.Dockerfile @@ -46,12 +46,13 @@ RUN mkdir -p /usr/src/php/ext/apcu \ && rm /usr/local/etc/php/conf.d/docker-php-ext-apcu.ini \ && echo extension=apcu.so > /usr/local/etc/php/conf.d/20-php-ext-apcu.ini -# Install pcov and sqlsrv driver -RUN wget https://download.microsoft.com/download/${MS_ODBC_DOWNLOAD}/msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk && \ +# Install xdebug and sqlsrv driver +RUN apk add --update linux-headers && \ + wget https://download.microsoft.com/download/${MS_ODBC_DOWNLOAD}/msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk && \ apk add --allow-untrusted msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk && \ apk add --no-cache --virtual .phpize-deps $PHPIZE_DEPS unixodbc-dev && \ - pecl install pdo_sqlsrv-${PDO_SQLSRV_VERSION} pcov && \ - docker-php-ext-enable pdo_sqlsrv pcov && \ + pecl install pdo_sqlsrv-${PDO_SQLSRV_VERSION} xdebug && \ + docker-php-ext-enable pdo_sqlsrv xdebug && \ apk del .phpize-deps && \ rm msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk diff --git a/data/infra/php.ini b/data/infra/php.ini index 46ad43bb..ba8b1620 100644 --- a/data/infra/php.ini +++ b/data/infra/php.ini @@ -3,5 +3,3 @@ error_reporting=-1 log_errors_max_len=0 zend.assertions=1 assert.exception=1 -pcov.enabled=1 -pcov.directory=module diff --git a/data/infra/roadrunner.Dockerfile b/data/infra/roadrunner.Dockerfile index 006aea91..2cb660d2 100644 --- a/data/infra/roadrunner.Dockerfile +++ b/data/infra/roadrunner.Dockerfile @@ -46,12 +46,13 @@ RUN mkdir -p /usr/src/php/ext/apcu \ && rm /usr/local/etc/php/conf.d/docker-php-ext-apcu.ini \ && echo extension=apcu.so > /usr/local/etc/php/conf.d/20-php-ext-apcu.ini -# Install pcov and sqlsrv driver -RUN wget https://download.microsoft.com/download/${MS_ODBC_DOWNLOAD}/msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk && \ +# Install xdebug and sqlsrv driver +RUN apk add --update linux-headers && \ + wget https://download.microsoft.com/download/${MS_ODBC_DOWNLOAD}/msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk && \ apk add --allow-untrusted msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk && \ apk add --no-cache --virtual .phpize-deps $PHPIZE_DEPS unixodbc-dev && \ - pecl install pdo_sqlsrv-${PDO_SQLSRV_VERSION} pcov && \ - docker-php-ext-enable pdo_sqlsrv pcov && \ + pecl install pdo_sqlsrv-${PDO_SQLSRV_VERSION} xdebug && \ + docker-php-ext-enable pdo_sqlsrv xdebug && \ apk del .phpize-deps && \ rm msodbcsql${MS_ODBC_SQL_VERSION}-1_amd64.apk From 5fb6c8708c940ad29a018586a2e856d4af60a1fb Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Tue, 22 Oct 2024 09:02:32 +0200 Subject: [PATCH 04/25] Update to endroid/qr-code 6.0 --- CHANGELOG.md | 3 ++- composer.json | 4 +-- module/Core/src/Action/QrCodeAction.php | 35 ++++++++++++++++--------- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f821b7d..eec09c68 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com), and this * *Nothing* ### Changed -* Run RoadRunner in docker with `exec` to ensure signals are properly handled. +* [#2232](https://github.com/shlinkio/shlink/issues/2232) Run RoadRunner in docker with `exec` to ensure signals are properly handled. +* [#2231](https://github.com/shlinkio/shlink/issues/2231) Update to `endroid/qr-code` 6.0. ### Deprecated * *Nothing* diff --git a/composer.json b/composer.json index 013f03ee..6f8feba4 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,7 @@ "doctrine/dbal": "^4.1", "doctrine/migrations": "^3.6", "doctrine/orm": "^3.2", - "endroid/qr-code": "^5.0", + "endroid/qr-code": "^6.0", "friendsofphp/proxy-manager-lts": "^1.0", "geoip2/geoip2": "^3.0", "guzzlehttp/guzzle": "^7.5", @@ -44,7 +44,7 @@ "pagerfanta/core": "^3.8", "ramsey/uuid": "^4.7", "shlinkio/doctrine-specification": "^2.1.1", - "shlinkio/shlink-common": "^6.3", + "shlinkio/shlink-common": "dev-main#e0c872c as 6.4", "shlinkio/shlink-config": "^3.2.1", "shlinkio/shlink-event-dispatcher": "^4.1", "shlinkio/shlink-importer": "^5.3.2", diff --git a/module/Core/src/Action/QrCodeAction.php b/module/Core/src/Action/QrCodeAction.php index 53fb1251..4da8bb32 100644 --- a/module/Core/src/Action/QrCodeAction.php +++ b/module/Core/src/Action/QrCodeAction.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace Shlinkio\Shlink\Core\Action; use Endroid\QrCode\Builder\Builder; +use Endroid\QrCode\Writer\Result\ResultInterface; use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; use Psr\Http\Server\MiddlewareInterface; @@ -42,22 +43,30 @@ readonly class QrCodeAction implements MiddlewareInterface } $params = QrCodeParams::fromRequest($request, $this->options); - $qrCodeBuilder = Builder::create() - ->data($this->stringifier->stringify($shortUrl)) - ->size($params->size) - ->margin($params->margin) - ->writer($params->writer) - ->errorCorrectionLevel($params->errorCorrectionLevel) - ->roundBlockSizeMode($params->roundBlockSizeMode) - ->foregroundColor($params->color) - ->backgroundColor($params->bgColor); + $qrCodeBuilder = new Builder( + writer: $params->writer, + data: $this->stringifier->stringify($shortUrl), + errorCorrectionLevel: $params->errorCorrectionLevel, + size: $params->size, + margin: $params->margin, + roundBlockSizeMode: $params->roundBlockSizeMode, + foregroundColor: $params->color, + backgroundColor: $params->bgColor, + ); + return new QrCodeResponse($this->buildQrCode($qrCodeBuilder, $params)); + } + + private function buildQrCode(Builder $qrCodeBuilder, QrCodeParams $params): ResultInterface + { $logoUrl = $this->options->logoUrl; - if ($logoUrl !== null) { - $qrCodeBuilder->logoPath($logoUrl) - ->logoResizeToHeight((int) ($params->size / 4)); + if ($logoUrl === null) { + return $qrCodeBuilder->build(); } - return new QrCodeResponse($qrCodeBuilder->build()); + return $qrCodeBuilder->build( + logoPath: $logoUrl, + logoResizeToHeight: (int) ($params->size / 4), + ); } } From 582033ceb345a4bdd901af5bebcdf8518eab1025 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Wed, 16 Oct 2024 08:55:38 +0200 Subject: [PATCH 05/25] Migrate dev-specific configuration to env vars via .env file --- .gitignore | 1 + config/autoload/delete_short_urls.global.php | 20 ------ config/autoload/entity-manager.local.php.dist | 46 ------------- config/autoload/matomo.global.php | 16 ----- config/autoload/matomo.local.php.dist | 26 -------- config/autoload/mercure.global.php | 47 +++++++------- config/autoload/mercure.local.php.dist | 13 ---- config/autoload/qr-codes.global.php | 21 ------ config/autoload/rabbit.global.php | 1 + config/autoload/rabbit.local.php.dist | 15 ----- config/autoload/redirects.global.php | 20 ------ config/autoload/redis.local.php.local | 26 -------- config/autoload/robots.global.php | 14 ---- config/autoload/router.local.php.dist | 1 - config/autoload/tracking.global.php | 38 ----------- config/autoload/url-shortener.local.php.dist | 21 ------ docker-compose.yml | 12 +++- module/CLI/config/dependencies.config.php | 4 +- .../ShortUrl/CreateShortUrlCommand.php | 2 +- module/CLI/src/Factory/ApplicationFactory.php | 2 +- .../CLI/src/GeoLite/GeolocationDbUpdater.php | 2 +- module/CLI/src/Input/ShortUrlDataInput.php | 2 +- .../Domain/DomainRedirectsCommandTest.php | 2 +- .../Command/Domain/ListDomainsCommandTest.php | 2 +- .../ShortUrl/CreateShortUrlCommandTest.php | 2 +- .../test/Factory/ApplicationFactoryTest.php | 2 +- .../test/GeoLite/GeolocationDbUpdaterTest.php | 2 +- module/Core/config/dependencies.config.php | 53 ++++++++------- .../Core/config/event_dispatcher.config.php | 6 +- module/Core/src/Action/Model/QrCodeParams.php | 2 +- module/Core/src/Action/QrCodeAction.php | 2 +- module/Core/src/Action/RobotsAction.php | 2 +- .../src/{ => Config}/Options/AppOptions.php | 2 +- .../Config/Options/DeleteShortUrlsOptions.php | 28 ++++++++ .../Options/NotFoundRedirectOptions.php | 20 ++++-- .../{ => Config}/Options/QrCodeOptions.php | 21 +++++- .../src/Config/Options/RabbitMqOptions.php | 19 ++++++ .../{ => Config}/Options/RedirectOptions.php | 17 +++-- .../Core/src/Config/Options/RobotsOptions.php | 33 ++++++++++ .../src/Config/Options/TrackingOptions.php | 62 ++++++++++++++++++ .../Config/Options/UrlShortenerOptions.php | 62 ++++++++++++++++++ .../ErrorHandler/NotFoundRedirectHandler.php | 6 +- .../Helper/EnabledListenerChecker.php | 2 +- .../RabbitMq/NotifyNewShortUrlToRabbitMq.php | 2 +- .../RabbitMq/NotifyVisitToRabbitMq.php | 2 +- module/Core/src/Matomo/MatomoOptions.php | 26 ++++++-- .../src/Options/DeleteShortUrlsOptions.php | 16 ----- module/Core/src/Options/RabbitMqOptions.php | 13 ---- module/Core/src/Options/RobotsOptions.php | 22 ------- module/Core/src/Options/TrackingOptions.php | 33 ---------- .../Core/src/Options/UrlShortenerOptions.php | 36 ---------- .../src/ShortUrl/DeleteShortUrlService.php | 2 +- .../Helper/ShortCodeUniquenessHelper.php | 2 +- .../Helper/ShortUrlRedirectionBuilder.php | 2 +- .../Helper/ShortUrlTitleResolutionHelper.php | 2 +- .../ExtraPathRedirectMiddleware.php | 2 +- .../TrimTrailingSlashMiddleware.php | 2 +- .../src/ShortUrl/Model/ShortUrlCreation.php | 2 +- .../Model/Validation/CustomSlugFilter.php | 2 +- .../Model/Validation/CustomSlugValidator.php | 2 +- .../Model/Validation/ShortUrlInputFilter.php | 2 +- .../PersistenceShortUrlRelationResolver.php | 2 +- .../Core/src/ShortUrl/ShortUrlListService.php | 2 +- module/Core/src/ShortUrl/ShortUrlResolver.php | 2 +- .../Core/src/Util/RedirectResponseHelper.php | 2 +- module/Core/src/Visit/Model/Visitor.php | 2 +- module/Core/src/Visit/RequestTracker.php | 2 +- module/Core/src/Visit/VisitsTracker.php | 2 +- module/Core/test/Action/QrCodeActionTest.php | 2 +- module/Core/test/Action/RobotsActionTest.php | 2 +- .../Config/NotFoundRedirectResolverTest.php | 2 +- .../NotFoundRedirectHandlerTest.php | 2 +- .../Helper/EnabledListenerCheckerTest.php | 2 +- .../NotifyNewShortUrlToRabbitMqTest.php | 2 +- .../RabbitMq/NotifyVisitToRabbitMqTest.php | 2 +- .../ShortUrl/DeleteShortUrlServiceTest.php | 2 +- .../test/ShortUrl/Entity/ShortUrlTest.php | 2 +- .../Helper/ShortCodeUniquenessHelperTest.php | 2 +- .../Helper/ShortUrlRedirectionBuilderTest.php | 2 +- .../ShortUrlTitleResolutionHelperTest.php | 2 +- .../ExtraPathRedirectMiddlewareTest.php | 2 +- .../TrimTrailingSlashMiddlewareTest.php | 2 +- .../ShortUrl/Model/ShortUrlCreationTest.php | 2 +- .../Validation/CustomSlugValidatorTest.php | 2 +- ...ersistenceShortUrlRelationResolverTest.php | 2 +- .../test/ShortUrl/ShortUrlListServiceTest.php | 2 +- .../test/ShortUrl/ShortUrlResolverTest.php | 2 +- .../test/Util/RedirectResponseHelperTest.php | 2 +- module/Core/test/Visit/Model/VisitorTest.php | 2 +- module/Core/test/Visit/RequestTrackerTest.php | 2 +- module/Core/test/Visit/VisitsTrackerTest.php | 2 +- module/Rest/config/dependencies.config.php | 10 +-- .../src/Action/Domain/ListDomainsAction.php | 2 +- module/Rest/src/Action/HealthAction.php | 2 +- .../ShortUrl/AbstractCreateShortUrlAction.php | 2 +- .../Action/Domain/ListDomainsActionTest.php | 2 +- .../Request/DomainRedirectsRequestTest.php | 2 +- module/Rest/test/Action/HealthActionTest.php | 2 +- .../ShortUrl/CreateShortUrlActionTest.php | 2 +- .../SingleStepCreateShortUrlActionTest.php | 2 +- shlink-dev.env | 65 +++++++++++++++++++ 101 files changed, 475 insertions(+), 543 deletions(-) delete mode 100644 config/autoload/delete_short_urls.global.php delete mode 100644 config/autoload/entity-manager.local.php.dist delete mode 100644 config/autoload/matomo.global.php delete mode 100644 config/autoload/matomo.local.php.dist delete mode 100644 config/autoload/mercure.local.php.dist delete mode 100644 config/autoload/qr-codes.global.php delete mode 100644 config/autoload/rabbit.local.php.dist delete mode 100644 config/autoload/redirects.global.php delete mode 100644 config/autoload/redis.local.php.local delete mode 100644 config/autoload/robots.global.php delete mode 100644 config/autoload/tracking.global.php delete mode 100644 config/autoload/url-shortener.local.php.dist rename module/Core/src/{ => Config}/Options/AppOptions.php (86%) create mode 100644 module/Core/src/Config/Options/DeleteShortUrlsOptions.php rename module/Core/src/{ => Config}/Options/NotFoundRedirectOptions.php (54%) rename module/Core/src/{ => Config}/Options/QrCodeOptions.php (54%) create mode 100644 module/Core/src/Config/Options/RabbitMqOptions.php rename module/Core/src/{ => Config}/Options/RedirectOptions.php (59%) create mode 100644 module/Core/src/Config/Options/RobotsOptions.php create mode 100644 module/Core/src/Config/Options/TrackingOptions.php create mode 100644 module/Core/src/Config/Options/UrlShortenerOptions.php delete mode 100644 module/Core/src/Options/DeleteShortUrlsOptions.php delete mode 100644 module/Core/src/Options/RabbitMqOptions.php delete mode 100644 module/Core/src/Options/RobotsOptions.php delete mode 100644 module/Core/src/Options/TrackingOptions.php delete mode 100644 module/Core/src/Options/UrlShortenerOptions.php create mode 100644 shlink-dev.env diff --git a/.gitignore b/.gitignore index 04c8ed56..e539b609 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,4 @@ docs/mercure.html docker-compose.override.yml .phpunit.result.cache docs/swagger/swagger-inlined.json +shlink-dev.local.env diff --git a/config/autoload/delete_short_urls.global.php b/config/autoload/delete_short_urls.global.php deleted file mode 100644 index 2d203ea1..00000000 --- a/config/autoload/delete_short_urls.global.php +++ /dev/null @@ -1,20 +0,0 @@ -loadFromEnv(); - - return [ - - 'delete_short_urls' => [ - 'check_visits_threshold' => $threshold !== null, - 'visits_threshold' => (int) ($threshold ?? DEFAULT_DELETE_SHORT_URL_THRESHOLD), - ], - - ]; -})(); diff --git a/config/autoload/entity-manager.local.php.dist b/config/autoload/entity-manager.local.php.dist deleted file mode 100644 index abe5dd87..00000000 --- a/config/autoload/entity-manager.local.php.dist +++ /dev/null @@ -1,46 +0,0 @@ - [ - 'connection' => [ - // MySQL - 'user' => 'root', - 'password' => 'root', - 'driver' => 'pdo_mysql', - 'host' => 'shlink_db_mysql', - 'dbname' => 'shlink', -// 'dbname' => 'shlink_foo', - 'charset' => 'utf8mb4', - - // MariaDB -// 'user' => 'root', -// 'password' => 'root', -// 'driver' => 'pdo_mysql', -// 'host' => 'shlink_db_maria', -// 'dbname' => 'shlink_foo', -// 'charset' => 'utf8mb4', - - // Postgres -// 'user' => 'postgres', -// 'password' => 'root', -// 'driver' => 'pdo_pgsql', -// 'host' => 'shlink_db_postgres', -// 'dbname' => 'shlink_foo', -// 'charset' => 'utf8', - - // MSSQL -// 'user' => 'sa', -// 'password' => 'Passw0rd!', -// 'driver' => 'pdo_sqlsrv', -// 'host' => 'shlink_db_ms', -// 'dbname' => 'shlink_foo', -// 'driverOptions' => [ -// 'TrustServerCertificate' => 'true', -// ], - ], - ], - -]; diff --git a/config/autoload/matomo.global.php b/config/autoload/matomo.global.php deleted file mode 100644 index d7369ea7..00000000 --- a/config/autoload/matomo.global.php +++ /dev/null @@ -1,16 +0,0 @@ - [ - 'enabled' => (bool) EnvVars::MATOMO_ENABLED->loadFromEnv(), - 'base_url' => EnvVars::MATOMO_BASE_URL->loadFromEnv(), - 'site_id' => EnvVars::MATOMO_SITE_ID->loadFromEnv(), - 'api_token' => EnvVars::MATOMO_API_TOKEN->loadFromEnv(), - ], - -]; diff --git a/config/autoload/matomo.local.php.dist b/config/autoload/matomo.local.php.dist deleted file mode 100644 index 2a940407..00000000 --- a/config/autoload/matomo.local.php.dist +++ /dev/null @@ -1,26 +0,0 @@ - [ -// 'enabled' => true, -// 'base_url' => 'http://shlink_matomo', -// 'site_id' => '...', -// 'api_token' => '...', - ], - -]; diff --git a/config/autoload/mercure.global.php b/config/autoload/mercure.global.php index c50e1f8e..83abed1a 100644 --- a/config/autoload/mercure.global.php +++ b/config/autoload/mercure.global.php @@ -8,34 +8,31 @@ use Shlinkio\Shlink\Core\Config\EnvVars; use Symfony\Component\Mercure\Hub; use Symfony\Component\Mercure\HubInterface; -return (static function (): array { - $publicUrl = EnvVars::MERCURE_PUBLIC_HUB_URL->loadFromEnv(); +return [ - return [ + // This config is used by shlink-common. Do not delete + 'mercure' => [ + 'public_hub_url' => EnvVars::MERCURE_PUBLIC_HUB_URL->loadFromEnv(), + 'internal_hub_url' => EnvVars::MERCURE_INTERNAL_HUB_URL->loadFromEnv(), + 'jwt_secret' => EnvVars::MERCURE_JWT_SECRET->loadFromEnv(), + 'jwt_issuer' => 'Shlink', + ], - 'mercure' => [ - 'public_hub_url' => $publicUrl, - 'internal_hub_url' => EnvVars::MERCURE_INTERNAL_HUB_URL->loadFromEnv(), - 'jwt_secret' => EnvVars::MERCURE_JWT_SECRET->loadFromEnv(), - 'jwt_issuer' => 'Shlink', - ], - - 'dependencies' => [ - 'delegators' => [ - LcobucciJwtProvider::class => [ - LazyServiceFactory::class, - ], - Hub::class => [ - LazyServiceFactory::class, - ], + 'dependencies' => [ + 'delegators' => [ + LcobucciJwtProvider::class => [ + LazyServiceFactory::class, ], - 'lazy_services' => [ - 'class_map' => [ - LcobucciJwtProvider::class => LcobucciJwtProvider::class, - Hub::class => HubInterface::class, - ], + Hub::class => [ + LazyServiceFactory::class, ], ], + 'lazy_services' => [ + 'class_map' => [ + LcobucciJwtProvider::class => LcobucciJwtProvider::class, + Hub::class => HubInterface::class, + ], + ], + ], - ]; -})(); +]; diff --git a/config/autoload/mercure.local.php.dist b/config/autoload/mercure.local.php.dist deleted file mode 100644 index 13a74022..00000000 --- a/config/autoload/mercure.local.php.dist +++ /dev/null @@ -1,13 +0,0 @@ - [ - 'public_hub_url' => 'http://localhost:8002', - 'internal_hub_url' => 'http://shlink_mercure_proxy', - 'jwt_secret' => 'mercure_jwt_key_long_enough_to_avoid_error', - ], - -]; diff --git a/config/autoload/qr-codes.global.php b/config/autoload/qr-codes.global.php deleted file mode 100644 index 23caadf2..00000000 --- a/config/autoload/qr-codes.global.php +++ /dev/null @@ -1,21 +0,0 @@ - [ - 'size' => (int) EnvVars::DEFAULT_QR_CODE_SIZE->loadFromEnv(), - 'margin' => (int) EnvVars::DEFAULT_QR_CODE_MARGIN->loadFromEnv(), - 'format' => EnvVars::DEFAULT_QR_CODE_FORMAT->loadFromEnv(), - 'error_correction' => EnvVars::DEFAULT_QR_CODE_ERROR_CORRECTION->loadFromEnv(), - 'round_block_size' => (bool) EnvVars::DEFAULT_QR_CODE_ROUND_BLOCK_SIZE->loadFromEnv(), - 'enabled_for_disabled_short_urls' => (bool) EnvVars::QR_CODE_FOR_DISABLED_SHORT_URLS->loadFromEnv(), - 'color' => EnvVars::DEFAULT_QR_CODE_COLOR->loadFromEnv(), - 'bg_color' => EnvVars::DEFAULT_QR_CODE_BG_COLOR->loadFromEnv(), - 'logo_url' => EnvVars::DEFAULT_QR_CODE_LOGO_URL->loadFromEnv(), - ], - -]; diff --git a/config/autoload/rabbit.global.php b/config/autoload/rabbit.global.php index 86e2dd6a..c9532377 100644 --- a/config/autoload/rabbit.global.php +++ b/config/autoload/rabbit.global.php @@ -6,6 +6,7 @@ use Shlinkio\Shlink\Core\Config\EnvVars; return [ + // This config is used by shlink-common. Do not delete 'rabbitmq' => [ 'enabled' => (bool) EnvVars::RABBITMQ_ENABLED->loadFromEnv(), 'host' => EnvVars::RABBITMQ_HOST->loadFromEnv(), diff --git a/config/autoload/rabbit.local.php.dist b/config/autoload/rabbit.local.php.dist deleted file mode 100644 index 37faf181..00000000 --- a/config/autoload/rabbit.local.php.dist +++ /dev/null @@ -1,15 +0,0 @@ - [ - 'enabled' => true, - 'host' => 'shlink_rabbitmq', - 'port' => 5672, - 'user' => 'rabbit', - 'password' => 'rabbit', - ], - -]; diff --git a/config/autoload/redirects.global.php b/config/autoload/redirects.global.php deleted file mode 100644 index dbf026db..00000000 --- a/config/autoload/redirects.global.php +++ /dev/null @@ -1,20 +0,0 @@ - [ - 'invalid_short_url' => EnvVars::DEFAULT_INVALID_SHORT_URL_REDIRECT->loadFromEnv(), - 'regular_404' => EnvVars::DEFAULT_REGULAR_404_REDIRECT->loadFromEnv(), - 'base_url' => EnvVars::DEFAULT_BASE_URL_REDIRECT->loadFromEnv(), - ], - - 'redirects' => [ - 'redirect_status_code' => (int) EnvVars::REDIRECT_STATUS_CODE->loadFromEnv(), - 'redirect_cache_lifetime' => (int) EnvVars::REDIRECT_CACHE_LIFETIME->loadFromEnv(), - ], - -]; diff --git a/config/autoload/redis.local.php.local b/config/autoload/redis.local.php.local deleted file mode 100644 index 7fd57112..00000000 --- a/config/autoload/redis.local.php.local +++ /dev/null @@ -1,26 +0,0 @@ - [ - 'redis' => [ - 'servers' => 'tcp://shlink_redis:6379', -// 'servers' => 'tcp://barbar@shlink_redis_acl:6379', -// 'servers' => 'tcp://foo:bar@shlink_redis_acl:6379', - ], - ], - - 'redis' => [ - 'pub_sub_enabled' => true, - ], - - 'dependencies' => [ - 'aliases' => [ - // With this config, a user could alias 'lock_store' => 'redis_lock_store' to override the default -// 'lock_store' => 'redis_lock_store', - ], - ], - -]; diff --git a/config/autoload/robots.global.php b/config/autoload/robots.global.php deleted file mode 100644 index 35235d72..00000000 --- a/config/autoload/robots.global.php +++ /dev/null @@ -1,14 +0,0 @@ - [ - 'allow-all-short-urls' => (bool) Config\EnvVars::ROBOTS_ALLOW_ALL_SHORT_URLS->loadFromEnv(), - 'user-agents' => splitByComma(Config\EnvVars::ROBOTS_USER_AGENTS->loadFromEnv()), - ], - -]; diff --git a/config/autoload/router.local.php.dist b/config/autoload/router.local.php.dist index 39bd7169..dc67b587 100644 --- a/config/autoload/router.local.php.dist +++ b/config/autoload/router.local.php.dist @@ -7,7 +7,6 @@ use Mezzio\Router\FastRouteRouter; return [ 'router' => [ -// 'base_path' => '', 'fastroute' => [ FastRouteRouter::CONFIG_CACHE_ENABLED => false, ], diff --git a/config/autoload/tracking.global.php b/config/autoload/tracking.global.php deleted file mode 100644 index 713a209e..00000000 --- a/config/autoload/tracking.global.php +++ /dev/null @@ -1,38 +0,0 @@ - [ - // Tells if IP addresses should be anonymized before persisting, to fulfil data protection regulations - // This applies only if IP address tracking is enabled - 'anonymize_remote_addr' => (bool) EnvVars::ANONYMIZE_REMOTE_ADDR->loadFromEnv(), - - // Tells if visits to not-found URLs should be tracked. The disable_tracking option takes precedence - 'track_orphan_visits' => (bool) EnvVars::TRACK_ORPHAN_VISITS->loadFromEnv(), - - // A query param that, if provided, will disable tracking of one particular visit. Always takes precedence - 'disable_track_param' => EnvVars::DISABLE_TRACK_PARAM->loadFromEnv(), - - // If true, visits will not be tracked at all - 'disable_tracking' => (bool) EnvVars::DISABLE_TRACKING->loadFromEnv(), - - // If true, visits will be tracked, but neither the IP address, nor the location will be resolved - 'disable_ip_tracking' => (bool) EnvVars::DISABLE_IP_TRACKING->loadFromEnv(), - - // If true, the referrer will not be tracked - 'disable_referrer_tracking' => (bool) EnvVars::DISABLE_REFERRER_TRACKING->loadFromEnv(), - - // If true, the user agent will not be tracked - 'disable_ua_tracking' => (bool) EnvVars::DISABLE_UA_TRACKING->loadFromEnv(), - - // A list of IP addresses, patterns or CIDR blocks from which tracking is disabled by default - 'disable_tracking_from' => splitByComma(EnvVars::DISABLE_TRACKING_FROM->loadFromEnv()), - ], - -]; diff --git a/config/autoload/url-shortener.local.php.dist b/config/autoload/url-shortener.local.php.dist deleted file mode 100644 index 715d2822..00000000 --- a/config/autoload/url-shortener.local.php.dist +++ /dev/null @@ -1,21 +0,0 @@ - [ - 'domain' => [ - 'schema' => 'http', - 'hostname' => sprintf('localhost:%s', match (true) { - runningInRoadRunner() => '8800', - default => '8000', - }), - ], -// 'multi_segment_slugs_enabled' => true, -// 'trailing_slash_enabled' => true, - ], - -]; diff --git a/docker-compose.yml b/docker-compose.yml index 41f54cba..718ba4c1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -33,7 +33,11 @@ services: - shlink_rabbitmq - shlink_matomo environment: - LC_ALL: C + DEFAULT_DOMAIN: localhost:8000 + env_file: + - path: shlink-dev.env + - path: shlink-dev.local.env + required: false extra_hosts: - 'host.docker.internal:host-gateway' @@ -59,7 +63,11 @@ services: - shlink_rabbitmq - shlink_matomo environment: - LC_ALL: C + DEFAULT_DOMAIN: localhost:8800 + env_file: + - path: shlink-dev.env + - path: shlink-dev.local.env + required: false extra_hosts: - 'host.docker.internal:host-gateway' diff --git a/module/CLI/config/dependencies.config.php b/module/CLI/config/dependencies.config.php index 0ee1a331..fd26a5bc 100644 --- a/module/CLI/config/dependencies.config.php +++ b/module/CLI/config/dependencies.config.php @@ -7,10 +7,10 @@ namespace Shlinkio\Shlink\CLI; use Laminas\ServiceManager\AbstractFactory\ConfigAbstractFactory; use Laminas\ServiceManager\Factory\InvokableFactory; use Shlinkio\Shlink\Common\Doctrine\NoDbNameConnectionFactory; +use Shlinkio\Shlink\Core\Config\Options\TrackingOptions; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Domain\DomainService; use Shlinkio\Shlink\Core\Matomo; -use Shlinkio\Shlink\Core\Options\TrackingOptions; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\RedirectRule\ShortUrlRedirectRuleService; use Shlinkio\Shlink\Core\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifier; diff --git a/module/CLI/src/Command/ShortUrl/CreateShortUrlCommand.php b/module/CLI/src/Command/ShortUrl/CreateShortUrlCommand.php index 0273da71..fdff2ddc 100644 --- a/module/CLI/src/Command/ShortUrl/CreateShortUrlCommand.php +++ b/module/CLI/src/Command/ShortUrl/CreateShortUrlCommand.php @@ -6,8 +6,8 @@ namespace Shlinkio\Shlink\CLI\Command\ShortUrl; use Shlinkio\Shlink\CLI\Input\ShortUrlDataInput; use Shlinkio\Shlink\CLI\Util\ExitCode; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Exception\NonUniqueSlugException; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifierInterface; use Shlinkio\Shlink\Core\ShortUrl\UrlShortenerInterface; use Symfony\Component\Console\Command\Command; diff --git a/module/CLI/src/Factory/ApplicationFactory.php b/module/CLI/src/Factory/ApplicationFactory.php index ab716f7e..89cf4271 100644 --- a/module/CLI/src/Factory/ApplicationFactory.php +++ b/module/CLI/src/Factory/ApplicationFactory.php @@ -5,7 +5,7 @@ declare(strict_types=1); namespace Shlinkio\Shlink\CLI\Factory; use Psr\Container\ContainerInterface; -use Shlinkio\Shlink\Core\Options\AppOptions; +use Shlinkio\Shlink\Core\Config\Options\AppOptions; use Symfony\Component\Console\Application as CliApp; use Symfony\Component\Console\CommandLoader\ContainerCommandLoader; diff --git a/module/CLI/src/GeoLite/GeolocationDbUpdater.php b/module/CLI/src/GeoLite/GeolocationDbUpdater.php index ff42c930..85ae1d3a 100644 --- a/module/CLI/src/GeoLite/GeolocationDbUpdater.php +++ b/module/CLI/src/GeoLite/GeolocationDbUpdater.php @@ -9,7 +9,7 @@ use Closure; use GeoIp2\Database\Reader; use MaxMind\Db\Reader\Metadata; use Shlinkio\Shlink\CLI\Exception\GeolocationDbUpdateFailedException; -use Shlinkio\Shlink\Core\Options\TrackingOptions; +use Shlinkio\Shlink\Core\Config\Options\TrackingOptions; use Shlinkio\Shlink\IpGeolocation\Exception\DbUpdateException; use Shlinkio\Shlink\IpGeolocation\Exception\MissingLicenseException; use Shlinkio\Shlink\IpGeolocation\Exception\WrongIpException; diff --git a/module/CLI/src/Input/ShortUrlDataInput.php b/module/CLI/src/Input/ShortUrlDataInput.php index 10c2da7c..2d3bf91e 100644 --- a/module/CLI/src/Input/ShortUrlDataInput.php +++ b/module/CLI/src/Input/ShortUrlDataInput.php @@ -4,7 +4,7 @@ declare(strict_types=1); namespace Shlinkio\Shlink\CLI\Input; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlCreation; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlEdition; use Shlinkio\Shlink\Core\ShortUrl\Model\Validation\ShortUrlInputFilter; diff --git a/module/CLI/test/Command/Domain/DomainRedirectsCommandTest.php b/module/CLI/test/Command/Domain/DomainRedirectsCommandTest.php index 0bc77aca..32240fc5 100644 --- a/module/CLI/test/Command/Domain/DomainRedirectsCommandTest.php +++ b/module/CLI/test/Command/Domain/DomainRedirectsCommandTest.php @@ -10,10 +10,10 @@ use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Shlinkio\Shlink\CLI\Command\Domain\DomainRedirectsCommand; use Shlinkio\Shlink\Core\Config\NotFoundRedirects; +use Shlinkio\Shlink\Core\Config\Options\NotFoundRedirectOptions; use Shlinkio\Shlink\Core\Domain\DomainServiceInterface; use Shlinkio\Shlink\Core\Domain\Entity\Domain; use Shlinkio\Shlink\Core\Domain\Model\DomainItem; -use Shlinkio\Shlink\Core\Options\NotFoundRedirectOptions; use ShlinkioTest\Shlink\CLI\Util\CliTestUtils; use Symfony\Component\Console\Tester\CommandTester; diff --git a/module/CLI/test/Command/Domain/ListDomainsCommandTest.php b/module/CLI/test/Command/Domain/ListDomainsCommandTest.php index cfa09e18..2621f940 100644 --- a/module/CLI/test/Command/Domain/ListDomainsCommandTest.php +++ b/module/CLI/test/Command/Domain/ListDomainsCommandTest.php @@ -11,10 +11,10 @@ use PHPUnit\Framework\TestCase; use Shlinkio\Shlink\CLI\Command\Domain\ListDomainsCommand; use Shlinkio\Shlink\CLI\Util\ExitCode; use Shlinkio\Shlink\Core\Config\NotFoundRedirects; +use Shlinkio\Shlink\Core\Config\Options\NotFoundRedirectOptions; use Shlinkio\Shlink\Core\Domain\DomainServiceInterface; use Shlinkio\Shlink\Core\Domain\Entity\Domain; use Shlinkio\Shlink\Core\Domain\Model\DomainItem; -use Shlinkio\Shlink\Core\Options\NotFoundRedirectOptions; use ShlinkioTest\Shlink\CLI\Util\CliTestUtils; use Symfony\Component\Console\Tester\CommandTester; diff --git a/module/CLI/test/Command/ShortUrl/CreateShortUrlCommandTest.php b/module/CLI/test/Command/ShortUrl/CreateShortUrlCommandTest.php index 33031c6b..86bd87fb 100644 --- a/module/CLI/test/Command/ShortUrl/CreateShortUrlCommandTest.php +++ b/module/CLI/test/Command/ShortUrl/CreateShortUrlCommandTest.php @@ -12,8 +12,8 @@ use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Shlinkio\Shlink\CLI\Command\ShortUrl\CreateShortUrlCommand; use Shlinkio\Shlink\CLI\Util\ExitCode; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Exception\NonUniqueSlugException; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifierInterface; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlCreation; diff --git a/module/CLI/test/Factory/ApplicationFactoryTest.php b/module/CLI/test/Factory/ApplicationFactoryTest.php index 83b0fc66..88b1280b 100644 --- a/module/CLI/test/Factory/ApplicationFactoryTest.php +++ b/module/CLI/test/Factory/ApplicationFactoryTest.php @@ -8,7 +8,7 @@ use Laminas\ServiceManager\ServiceManager; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; use Shlinkio\Shlink\CLI\Factory\ApplicationFactory; -use Shlinkio\Shlink\Core\Options\AppOptions; +use Shlinkio\Shlink\Core\Config\Options\AppOptions; use ShlinkioTest\Shlink\CLI\Util\CliTestUtils; class ApplicationFactoryTest extends TestCase diff --git a/module/CLI/test/GeoLite/GeolocationDbUpdaterTest.php b/module/CLI/test/GeoLite/GeolocationDbUpdaterTest.php index cbb4af5c..3b0f452e 100644 --- a/module/CLI/test/GeoLite/GeolocationDbUpdaterTest.php +++ b/module/CLI/test/GeoLite/GeolocationDbUpdaterTest.php @@ -14,7 +14,7 @@ use PHPUnit\Framework\TestCase; use Shlinkio\Shlink\CLI\Exception\GeolocationDbUpdateFailedException; use Shlinkio\Shlink\CLI\GeoLite\GeolocationDbUpdater; use Shlinkio\Shlink\CLI\GeoLite\GeolocationResult; -use Shlinkio\Shlink\Core\Options\TrackingOptions; +use Shlinkio\Shlink\Core\Config\Options\TrackingOptions; use Shlinkio\Shlink\IpGeolocation\Exception\DbUpdateException; use Shlinkio\Shlink\IpGeolocation\Exception\MissingLicenseException; use Shlinkio\Shlink\IpGeolocation\GeoLite2\DbUpdaterInterface; diff --git a/module/Core/config/dependencies.config.php b/module/Core/config/dependencies.config.php index 8f8b609b..f50469c0 100644 --- a/module/Core/config/dependencies.config.php +++ b/module/Core/config/dependencies.config.php @@ -9,7 +9,7 @@ use Laminas\ServiceManager\Factory\InvokableFactory; use Psr\EventDispatcher\EventDispatcherInterface; use Shlinkio\Shlink\Common\Doctrine\EntityRepositoryFactory; use Shlinkio\Shlink\Config\Factory\ValinorConfigFactory; -use Shlinkio\Shlink\Core\Options\NotFoundRedirectOptions; +use Shlinkio\Shlink\Core\Config\Options\NotFoundRedirectOptions; use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifier; use Shlinkio\Shlink\Importer\ImportedLinksProcessorInterface; use Shlinkio\Shlink\IpGeolocation\Resolver\IpLocationResolverInterface; @@ -24,15 +24,15 @@ return [ ErrorHandler\NotFoundRedirectHandler::class => ConfigAbstractFactory::class, ErrorHandler\NotFoundTemplateHandler::class => InvokableFactory::class, - Options\AppOptions::class => [ValinorConfigFactory::class, 'config.app_options'], - Options\DeleteShortUrlsOptions::class => [ValinorConfigFactory::class, 'config.delete_short_urls'], - Options\NotFoundRedirectOptions::class => [ValinorConfigFactory::class, 'config.not_found_redirects'], - Options\RedirectOptions::class => [ValinorConfigFactory::class, 'config.redirects'], - Options\UrlShortenerOptions::class => [ValinorConfigFactory::class, 'config.url_shortener'], - Options\TrackingOptions::class => [ValinorConfigFactory::class, 'config.tracking'], - Options\QrCodeOptions::class => [ValinorConfigFactory::class, 'config.qr_codes'], - Options\RabbitMqOptions::class => [ValinorConfigFactory::class, 'config.rabbitmq'], - Options\RobotsOptions::class => [ValinorConfigFactory::class, 'config.robots'], + Config\Options\AppOptions::class => [ValinorConfigFactory::class, 'config.app_options'], + Config\Options\DeleteShortUrlsOptions::class => [Config\Options\DeleteShortUrlsOptions::class, 'fromEnv'], + Config\Options\NotFoundRedirectOptions::class => [Config\Options\NotFoundRedirectOptions::class, 'fromEnv'], + Config\Options\RedirectOptions::class => [Config\Options\RedirectOptions::class, 'fromEnv'], + Config\Options\UrlShortenerOptions::class => [Config\Options\UrlShortenerOptions::class, 'fromEnv'], + Config\Options\TrackingOptions::class => [Config\Options\TrackingOptions::class, 'fromEnv'], + Config\Options\QrCodeOptions::class => [Config\Options\QrCodeOptions::class, 'fromEnv'], + Config\Options\RabbitMqOptions::class => [Config\Options\RabbitMqOptions::class, 'fromEnv'], + Config\Options\RobotsOptions::class => [Config\Options\RobotsOptions::class, 'fromEnv'], RedirectRule\ShortUrlRedirectRuleService::class => ConfigAbstractFactory::class, RedirectRule\ShortUrlRedirectionResolver::class => ConfigAbstractFactory::class, @@ -101,7 +101,7 @@ return [ Crawling\CrawlingHelper::class => ConfigAbstractFactory::class, - Matomo\MatomoOptions::class => [ValinorConfigFactory::class, 'config.matomo'], + Matomo\MatomoOptions::class => [Matomo\MatomoOptions::class, 'fromEnv'], Matomo\MatomoTrackerBuilder::class => ConfigAbstractFactory::class, Matomo\MatomoVisitSender::class => ConfigAbstractFactory::class, ], @@ -137,9 +137,9 @@ return [ Visit\VisitsTracker::class => [ 'em', EventDispatcherInterface::class, - Options\TrackingOptions::class, + Config\Options\TrackingOptions::class, ], - Visit\RequestTracker::class => [Visit\VisitsTracker::class, Options\TrackingOptions::class], + Visit\RequestTracker::class => [Visit\VisitsTracker::class, Config\Options\TrackingOptions::class], Visit\VisitsDeleter::class => [Visit\Repository\VisitDeleterRepository::class], ShortUrl\ShortUrlService::class => [ 'em', @@ -149,7 +149,7 @@ return [ ], ShortUrl\ShortUrlListService::class => [ ShortUrl\Repository\ShortUrlListRepository::class, - Options\UrlShortenerOptions::class, + Config\Options\UrlShortenerOptions::class, ], Visit\Geolocation\VisitLocator::class => ['em', Visit\Repository\VisitIterationRepository::class], Visit\Geolocation\VisitToLocationHelper::class => [IpLocationResolverInterface::class], @@ -157,20 +157,20 @@ return [ Tag\TagService::class => ['em'], ShortUrl\DeleteShortUrlService::class => [ 'em', - Options\DeleteShortUrlsOptions::class, + Config\Options\DeleteShortUrlsOptions::class, ShortUrl\ShortUrlResolver::class, ShortUrl\Repository\ExpiredShortUrlsRepository::class, ], - ShortUrl\ShortUrlResolver::class => ['em', Options\UrlShortenerOptions::class], + ShortUrl\ShortUrlResolver::class => ['em', Config\Options\UrlShortenerOptions::class], ShortUrl\ShortUrlVisitsDeleter::class => [ Visit\Repository\VisitDeleterRepository::class, ShortUrl\ShortUrlResolver::class, ], - ShortUrl\Helper\ShortCodeUniquenessHelper::class => ['em', Options\UrlShortenerOptions::class], + ShortUrl\Helper\ShortCodeUniquenessHelper::class => ['em', Config\Options\UrlShortenerOptions::class], Domain\DomainService::class => ['em', 'config.url_shortener.domain.hostname'], Util\DoctrineBatchHelper::class => ['em'], - Util\RedirectResponseHelper::class => [Options\RedirectOptions::class], + Util\RedirectResponseHelper::class => [Config\Options\RedirectOptions::class], Config\NotFoundRedirectResolver::class => [Util\RedirectResponseHelper::class, 'Logger_Shlink'], @@ -188,19 +188,22 @@ return [ ShortUrl\ShortUrlResolver::class, ShortUrl\Helper\ShortUrlStringifier::class, 'Logger_Shlink', - Options\QrCodeOptions::class, + Config\Options\QrCodeOptions::class, ], - Action\RobotsAction::class => [Crawling\CrawlingHelper::class, Options\RobotsOptions::class], + Action\RobotsAction::class => [Crawling\CrawlingHelper::class, Config\Options\RobotsOptions::class], ShortUrl\Resolver\PersistenceShortUrlRelationResolver::class => [ 'em', - Options\UrlShortenerOptions::class, + Config\Options\UrlShortenerOptions::class, Lock\LockFactory::class, ], ShortUrl\Helper\ShortUrlStringifier::class => ['config.url_shortener.domain', 'config.router.base_path'], - ShortUrl\Helper\ShortUrlTitleResolutionHelper::class => ['httpClient', Options\UrlShortenerOptions::class], + ShortUrl\Helper\ShortUrlTitleResolutionHelper::class => [ + 'httpClient', + Config\Options\UrlShortenerOptions::class, + ], ShortUrl\Helper\ShortUrlRedirectionBuilder::class => [ - Options\TrackingOptions::class, + Config\Options\TrackingOptions::class, RedirectRule\ShortUrlRedirectionResolver::class, ], ShortUrl\Transformer\ShortUrlDataTransformer::class => [ShortUrl\Helper\ShortUrlStringifier::class], @@ -209,9 +212,9 @@ return [ Visit\RequestTracker::class, ShortUrl\Helper\ShortUrlRedirectionBuilder::class, Util\RedirectResponseHelper::class, - Options\UrlShortenerOptions::class, + Config\Options\UrlShortenerOptions::class, ], - ShortUrl\Middleware\TrimTrailingSlashMiddleware::class => [Options\UrlShortenerOptions::class], + ShortUrl\Middleware\TrimTrailingSlashMiddleware::class => [Config\Options\UrlShortenerOptions::class], EventDispatcher\PublishingUpdatesGenerator::class => [ShortUrl\Transformer\ShortUrlDataTransformer::class], diff --git a/module/Core/config/event_dispatcher.config.php b/module/Core/config/event_dispatcher.config.php index f401f255..2491d606 100644 --- a/module/Core/config/event_dispatcher.config.php +++ b/module/Core/config/event_dispatcher.config.php @@ -129,14 +129,14 @@ return (static function (): array { EventDispatcher\PublishingUpdatesGenerator::class, 'em', 'Logger_Shlink', - Options\RabbitMqOptions::class, + Config\Options\RabbitMqOptions::class, ], EventDispatcher\RabbitMq\NotifyNewShortUrlToRabbitMq::class => [ RabbitMqPublishingHelper::class, EventDispatcher\PublishingUpdatesGenerator::class, 'em', 'Logger_Shlink', - Options\RabbitMqOptions::class, + Config\Options\RabbitMqOptions::class, ], EventDispatcher\RedisPubSub\NotifyVisitToRedis::class => [ RedisPublishingHelper::class, @@ -167,7 +167,7 @@ return (static function (): array { ], EventDispatcher\Helper\EnabledListenerChecker::class => [ - Options\RabbitMqOptions::class, + Config\Options\RabbitMqOptions::class, 'config.redis.pub_sub_enabled', MercureOptions::class, GeoLite2Options::class, diff --git a/module/Core/src/Action/Model/QrCodeParams.php b/module/Core/src/Action/Model/QrCodeParams.php index 2a3907cc..46d90056 100644 --- a/module/Core/src/Action/Model/QrCodeParams.php +++ b/module/Core/src/Action/Model/QrCodeParams.php @@ -12,7 +12,7 @@ use Endroid\QrCode\Writer\PngWriter; use Endroid\QrCode\Writer\SvgWriter; use Endroid\QrCode\Writer\WriterInterface; use Psr\Http\Message\ServerRequestInterface; -use Shlinkio\Shlink\Core\Options\QrCodeOptions; +use Shlinkio\Shlink\Core\Config\Options\QrCodeOptions; use function ctype_xdigit; use function hexdec; diff --git a/module/Core/src/Action/QrCodeAction.php b/module/Core/src/Action/QrCodeAction.php index 4da8bb32..607fab50 100644 --- a/module/Core/src/Action/QrCodeAction.php +++ b/module/Core/src/Action/QrCodeAction.php @@ -13,8 +13,8 @@ use Psr\Http\Server\RequestHandlerInterface; use Psr\Log\LoggerInterface; use Shlinkio\Shlink\Common\Response\QrCodeResponse; use Shlinkio\Shlink\Core\Action\Model\QrCodeParams; +use Shlinkio\Shlink\Core\Config\Options\QrCodeOptions; use Shlinkio\Shlink\Core\Exception\ShortUrlNotFoundException; -use Shlinkio\Shlink\Core\Options\QrCodeOptions; use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifierInterface; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlIdentifier; use Shlinkio\Shlink\Core\ShortUrl\ShortUrlResolverInterface; diff --git a/module/Core/src/Action/RobotsAction.php b/module/Core/src/Action/RobotsAction.php index 29c2c8d2..21b7d21e 100644 --- a/module/Core/src/Action/RobotsAction.php +++ b/module/Core/src/Action/RobotsAction.php @@ -9,8 +9,8 @@ use GuzzleHttp\Psr7\Response; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; +use Shlinkio\Shlink\Core\Config\Options\RobotsOptions; use Shlinkio\Shlink\Core\Crawling\CrawlingHelperInterface; -use Shlinkio\Shlink\Core\Options\RobotsOptions; use function sprintf; diff --git a/module/Core/src/Options/AppOptions.php b/module/Core/src/Config/Options/AppOptions.php similarity index 86% rename from module/Core/src/Options/AppOptions.php rename to module/Core/src/Config/Options/AppOptions.php index ec545352..42f07642 100644 --- a/module/Core/src/Options/AppOptions.php +++ b/module/Core/src/Config/Options/AppOptions.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Shlinkio\Shlink\Core\Options; +namespace Shlinkio\Shlink\Core\Config\Options; use function sprintf; diff --git a/module/Core/src/Config/Options/DeleteShortUrlsOptions.php b/module/Core/src/Config/Options/DeleteShortUrlsOptions.php new file mode 100644 index 00000000..97916d36 --- /dev/null +++ b/module/Core/src/Config/Options/DeleteShortUrlsOptions.php @@ -0,0 +1,28 @@ +loadFromEnv(); + + return new self( + visitsThreshold: (int) ($threshold ?? DEFAULT_DELETE_SHORT_URL_THRESHOLD), + checkVisitsThreshold: $threshold !== null, + ); + } +} diff --git a/module/Core/src/Options/NotFoundRedirectOptions.php b/module/Core/src/Config/Options/NotFoundRedirectOptions.php similarity index 54% rename from module/Core/src/Options/NotFoundRedirectOptions.php rename to module/Core/src/Config/Options/NotFoundRedirectOptions.php index fe99ac7e..e6ef6a24 100644 --- a/module/Core/src/Options/NotFoundRedirectOptions.php +++ b/module/Core/src/Config/Options/NotFoundRedirectOptions.php @@ -2,19 +2,29 @@ declare(strict_types=1); -namespace Shlinkio\Shlink\Core\Options; +namespace Shlinkio\Shlink\Core\Config\Options; +use Shlinkio\Shlink\Core\Config\EnvVars; use Shlinkio\Shlink\Core\Config\NotFoundRedirectConfigInterface; -final class NotFoundRedirectOptions implements NotFoundRedirectConfigInterface +final readonly class NotFoundRedirectOptions implements NotFoundRedirectConfigInterface { public function __construct( - public readonly ?string $invalidShortUrl = null, - public readonly ?string $regular404 = null, - public readonly ?string $baseUrl = null, + public ?string $invalidShortUrl = null, + public ?string $regular404 = null, + public ?string $baseUrl = null, ) { } + public static function fromEnv(): self + { + return new self( + invalidShortUrl: EnvVars::DEFAULT_INVALID_SHORT_URL_REDIRECT->loadFromEnv(), + regular404: EnvVars::DEFAULT_REGULAR_404_REDIRECT->loadFromEnv(), + baseUrl: EnvVars::DEFAULT_BASE_URL_REDIRECT->loadFromEnv(), + ); + } + public function invalidShortUrlRedirect(): ?string { return $this->invalidShortUrl; diff --git a/module/Core/src/Options/QrCodeOptions.php b/module/Core/src/Config/Options/QrCodeOptions.php similarity index 54% rename from module/Core/src/Options/QrCodeOptions.php rename to module/Core/src/Config/Options/QrCodeOptions.php index da130d17..4d85e6cc 100644 --- a/module/Core/src/Options/QrCodeOptions.php +++ b/module/Core/src/Config/Options/QrCodeOptions.php @@ -2,7 +2,9 @@ declare(strict_types=1); -namespace Shlinkio\Shlink\Core\Options; +namespace Shlinkio\Shlink\Core\Config\Options; + +use Shlinkio\Shlink\Core\Config\EnvVars; use const Shlinkio\Shlink\DEFAULT_QR_CODE_BG_COLOR; use const Shlinkio\Shlink\DEFAULT_QR_CODE_COLOR; @@ -13,7 +15,7 @@ use const Shlinkio\Shlink\DEFAULT_QR_CODE_MARGIN; use const Shlinkio\Shlink\DEFAULT_QR_CODE_ROUND_BLOCK_SIZE; use const Shlinkio\Shlink\DEFAULT_QR_CODE_SIZE; -readonly final class QrCodeOptions +final readonly class QrCodeOptions { public function __construct( public int $size = DEFAULT_QR_CODE_SIZE, @@ -27,4 +29,19 @@ readonly final class QrCodeOptions public ?string $logoUrl = null, ) { } + + public static function fromEnv(): self + { + return new self( + size: (int) EnvVars::DEFAULT_QR_CODE_SIZE->loadFromEnv(), + margin: (int) EnvVars::DEFAULT_QR_CODE_MARGIN->loadFromEnv(), + format: EnvVars::DEFAULT_QR_CODE_FORMAT->loadFromEnv(), + errorCorrection: EnvVars::DEFAULT_QR_CODE_ERROR_CORRECTION->loadFromEnv(), + roundBlockSize: (bool) EnvVars::DEFAULT_QR_CODE_ROUND_BLOCK_SIZE->loadFromEnv(), + enabledForDisabledShortUrls: (bool) EnvVars::QR_CODE_FOR_DISABLED_SHORT_URLS->loadFromEnv(), + color: EnvVars::DEFAULT_QR_CODE_COLOR->loadFromEnv(), + bgColor: EnvVars::DEFAULT_QR_CODE_BG_COLOR->loadFromEnv(), + logoUrl: EnvVars::DEFAULT_QR_CODE_LOGO_URL->loadFromEnv(), + ); + } } diff --git a/module/Core/src/Config/Options/RabbitMqOptions.php b/module/Core/src/Config/Options/RabbitMqOptions.php new file mode 100644 index 00000000..4bc86e34 --- /dev/null +++ b/module/Core/src/Config/Options/RabbitMqOptions.php @@ -0,0 +1,19 @@ +loadFromEnv()); + } +} diff --git a/module/Core/src/Options/RedirectOptions.php b/module/Core/src/Config/Options/RedirectOptions.php similarity index 59% rename from module/Core/src/Options/RedirectOptions.php rename to module/Core/src/Config/Options/RedirectOptions.php index dd9f0a6d..75faf097 100644 --- a/module/Core/src/Options/RedirectOptions.php +++ b/module/Core/src/Config/Options/RedirectOptions.php @@ -2,18 +2,19 @@ declare(strict_types=1); -namespace Shlinkio\Shlink\Core\Options; +namespace Shlinkio\Shlink\Core\Config\Options; use Fig\Http\Message\StatusCodeInterface; +use Shlinkio\Shlink\Core\Config\EnvVars; use Shlinkio\Shlink\Core\Util\RedirectStatus; use const Shlinkio\Shlink\DEFAULT_REDIRECT_CACHE_LIFETIME; use const Shlinkio\Shlink\DEFAULT_REDIRECT_STATUS_CODE; -final class RedirectOptions +final readonly class RedirectOptions { - public readonly RedirectStatus $redirectStatusCode; - public readonly int $redirectCacheLifetime; + public RedirectStatus $redirectStatusCode; + public int $redirectCacheLifetime; public function __construct( int $redirectStatusCode = StatusCodeInterface::STATUS_FOUND, @@ -24,4 +25,12 @@ final class RedirectOptions ? $redirectCacheLifetime : DEFAULT_REDIRECT_CACHE_LIFETIME; } + + public static function fromEnv(): self + { + return new self( + redirectStatusCode: (int) EnvVars::REDIRECT_STATUS_CODE->loadFromEnv(), + redirectCacheLifetime: (int) EnvVars::REDIRECT_CACHE_LIFETIME->loadFromEnv(), + ); + } } diff --git a/module/Core/src/Config/Options/RobotsOptions.php b/module/Core/src/Config/Options/RobotsOptions.php new file mode 100644 index 00000000..6a8c364c --- /dev/null +++ b/module/Core/src/Config/Options/RobotsOptions.php @@ -0,0 +1,33 @@ +loadFromEnv(), + userAgents: splitByComma(EnvVars::ROBOTS_USER_AGENTS->loadFromEnv()), + ); + } + + public function hasUserAgents(): bool + { + return count($this->userAgents) > 0; + } +} diff --git a/module/Core/src/Config/Options/TrackingOptions.php b/module/Core/src/Config/Options/TrackingOptions.php new file mode 100644 index 00000000..eddfba34 --- /dev/null +++ b/module/Core/src/Config/Options/TrackingOptions.php @@ -0,0 +1,62 @@ +loadFromEnv(), + trackOrphanVisits: (bool) EnvVars::TRACK_ORPHAN_VISITS->loadFromEnv(), + disableTrackParam: EnvVars::DISABLE_TRACK_PARAM->loadFromEnv(), + disableTracking: (bool) EnvVars::DISABLE_TRACKING->loadFromEnv(), + disableIpTracking: (bool) EnvVars::DISABLE_IP_TRACKING->loadFromEnv(), + disableReferrerTracking: (bool) EnvVars::DISABLE_REFERRER_TRACKING->loadFromEnv(), + disableUaTracking: (bool) EnvVars::DISABLE_UA_TRACKING->loadFromEnv(), + disableTrackingFrom: splitByComma(EnvVars::DISABLE_TRACKING_FROM->loadFromEnv()), + ); + } + + public function hasDisableTrackingFrom(): bool + { + return ! empty($this->disableTrackingFrom); + } + + public function queryHasDisableTrackParam(array $query): bool + { + return $this->disableTrackParam !== null && array_key_exists($this->disableTrackParam, $query); + } +} diff --git a/module/Core/src/Config/Options/UrlShortenerOptions.php b/module/Core/src/Config/Options/UrlShortenerOptions.php new file mode 100644 index 00000000..b815ac0f --- /dev/null +++ b/module/Core/src/Config/Options/UrlShortenerOptions.php @@ -0,0 +1,62 @@ + null, 'hostname' => null], + public int $defaultShortCodesLength = DEFAULT_SHORT_CODES_LENGTH, + public bool $autoResolveTitles = false, + public bool $appendExtraPath = false, + public bool $multiSegmentSlugsEnabled = false, + public bool $trailingSlashEnabled = false, + public ShortUrlMode $mode = ShortUrlMode::STRICT, + ) { + } + + public static function fromEnv(): self + { + $shortCodesLength = max( + (int) EnvVars::DEFAULT_SHORT_CODES_LENGTH->loadFromEnv(), + MIN_SHORT_CODES_LENGTH, + ); + $mode = EnvVars::SHORT_URL_MODE->loadFromEnv(); + + return new self( + domain: [ + 'schema' => ((bool) EnvVars::IS_HTTPS_ENABLED->loadFromEnv()) ? 'https' : 'http', + 'hostname' => EnvVars::DEFAULT_DOMAIN->loadFromEnv(), + ], + defaultShortCodesLength: $shortCodesLength, + autoResolveTitles: (bool) EnvVars::AUTO_RESOLVE_TITLES->loadFromEnv(), + appendExtraPath: (bool) EnvVars::REDIRECT_APPEND_EXTRA_PATH->loadFromEnv(), + multiSegmentSlugsEnabled: (bool) EnvVars::MULTI_SEGMENT_SLUGS_ENABLED->loadFromEnv(), + trailingSlashEnabled: (bool) EnvVars::SHORT_URL_TRAILING_SLASH->loadFromEnv(), + mode: ShortUrlMode::tryFrom($mode) ?? ShortUrlMode::STRICT, + ); + } + + public function isLooseMode(): bool + { + return $this->mode === ShortUrlMode::LOOSE; + } + + public function defaultDomain(): string + { + return $this->domain['hostname'] ?? ''; + } +} diff --git a/module/Core/src/ErrorHandler/NotFoundRedirectHandler.php b/module/Core/src/ErrorHandler/NotFoundRedirectHandler.php index 4138a72e..4e7360d5 100644 --- a/module/Core/src/ErrorHandler/NotFoundRedirectHandler.php +++ b/module/Core/src/ErrorHandler/NotFoundRedirectHandler.php @@ -9,15 +9,15 @@ use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\UriInterface; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; +use Shlinkio\Shlink\Core\Config; use Shlinkio\Shlink\Core\Config\NotFoundRedirectResolverInterface; use Shlinkio\Shlink\Core\Domain\DomainServiceInterface; use Shlinkio\Shlink\Core\ErrorHandler\Model\NotFoundType; -use Shlinkio\Shlink\Core\Options; -class NotFoundRedirectHandler implements MiddlewareInterface +readonly class NotFoundRedirectHandler implements MiddlewareInterface { public function __construct( - private Options\NotFoundRedirectOptions $redirectOptions, + private Config\Options\NotFoundRedirectOptions $redirectOptions, private NotFoundRedirectResolverInterface $redirectResolver, private DomainServiceInterface $domainService, ) { diff --git a/module/Core/src/EventDispatcher/Helper/EnabledListenerChecker.php b/module/Core/src/EventDispatcher/Helper/EnabledListenerChecker.php index ad4c8070..c348cbbd 100644 --- a/module/Core/src/EventDispatcher/Helper/EnabledListenerChecker.php +++ b/module/Core/src/EventDispatcher/Helper/EnabledListenerChecker.php @@ -5,9 +5,9 @@ declare(strict_types=1); namespace Shlinkio\Shlink\Core\EventDispatcher\Helper; use Shlinkio\Shlink\Common\Mercure\MercureOptions; +use Shlinkio\Shlink\Core\Config\Options\RabbitMqOptions; use Shlinkio\Shlink\Core\EventDispatcher; use Shlinkio\Shlink\Core\Matomo\MatomoOptions; -use Shlinkio\Shlink\Core\Options\RabbitMqOptions; use Shlinkio\Shlink\EventDispatcher\Listener\EnabledListenerCheckerInterface; use Shlinkio\Shlink\IpGeolocation\GeoLite2\GeoLite2Options; diff --git a/module/Core/src/EventDispatcher/RabbitMq/NotifyNewShortUrlToRabbitMq.php b/module/Core/src/EventDispatcher/RabbitMq/NotifyNewShortUrlToRabbitMq.php index daa7cafb..b8c18fb6 100644 --- a/module/Core/src/EventDispatcher/RabbitMq/NotifyNewShortUrlToRabbitMq.php +++ b/module/Core/src/EventDispatcher/RabbitMq/NotifyNewShortUrlToRabbitMq.php @@ -7,10 +7,10 @@ namespace Shlinkio\Shlink\Core\EventDispatcher\RabbitMq; use Doctrine\ORM\EntityManagerInterface; use Psr\Log\LoggerInterface; use Shlinkio\Shlink\Common\UpdatePublishing\PublishingHelperInterface; +use Shlinkio\Shlink\Core\Config\Options\RabbitMqOptions; use Shlinkio\Shlink\Core\EventDispatcher\Async\AbstractNotifyNewShortUrlListener; use Shlinkio\Shlink\Core\EventDispatcher\Async\RemoteSystem; use Shlinkio\Shlink\Core\EventDispatcher\PublishingUpdatesGeneratorInterface; -use Shlinkio\Shlink\Core\Options\RabbitMqOptions; class NotifyNewShortUrlToRabbitMq extends AbstractNotifyNewShortUrlListener { diff --git a/module/Core/src/EventDispatcher/RabbitMq/NotifyVisitToRabbitMq.php b/module/Core/src/EventDispatcher/RabbitMq/NotifyVisitToRabbitMq.php index ddc4221c..bb55f169 100644 --- a/module/Core/src/EventDispatcher/RabbitMq/NotifyVisitToRabbitMq.php +++ b/module/Core/src/EventDispatcher/RabbitMq/NotifyVisitToRabbitMq.php @@ -7,10 +7,10 @@ namespace Shlinkio\Shlink\Core\EventDispatcher\RabbitMq; use Doctrine\ORM\EntityManagerInterface; use Psr\Log\LoggerInterface; use Shlinkio\Shlink\Common\UpdatePublishing\PublishingHelperInterface; +use Shlinkio\Shlink\Core\Config\Options\RabbitMqOptions; use Shlinkio\Shlink\Core\EventDispatcher\Async\AbstractNotifyVisitListener; use Shlinkio\Shlink\Core\EventDispatcher\Async\RemoteSystem; use Shlinkio\Shlink\Core\EventDispatcher\PublishingUpdatesGeneratorInterface; -use Shlinkio\Shlink\Core\Options\RabbitMqOptions; class NotifyVisitToRabbitMq extends AbstractNotifyVisitListener { diff --git a/module/Core/src/Matomo/MatomoOptions.php b/module/Core/src/Matomo/MatomoOptions.php index 23599321..af0ace92 100644 --- a/module/Core/src/Matomo/MatomoOptions.php +++ b/module/Core/src/Matomo/MatomoOptions.php @@ -4,17 +4,31 @@ declare(strict_types=1); namespace Shlinkio\Shlink\Core\Matomo; -class MatomoOptions +use Shlinkio\Shlink\Core\Config\EnvVars; + +final readonly class MatomoOptions { + /** + * @param numeric-string|int|null $siteId + */ public function __construct( - public readonly bool $enabled = false, - public readonly ?string $baseUrl = null, - /** @var numeric-string|int|null */ - private readonly string|int|null $siteId = null, - public readonly ?string $apiToken = null, + public bool $enabled = false, + public ?string $baseUrl = null, + private string|int|null $siteId = null, + public ?string $apiToken = null, ) { } + public static function fromEnv(): self + { + return new self( + enabled: (bool) EnvVars::MATOMO_ENABLED->loadFromEnv(), + baseUrl: EnvVars::MATOMO_BASE_URL->loadFromEnv(), + siteId: EnvVars::MATOMO_SITE_ID->loadFromEnv(), + apiToken: EnvVars::MATOMO_API_TOKEN->loadFromEnv(), + ); + } + public function siteId(): ?int { if ($this->siteId === null) { diff --git a/module/Core/src/Options/DeleteShortUrlsOptions.php b/module/Core/src/Options/DeleteShortUrlsOptions.php deleted file mode 100644 index a645181b..00000000 --- a/module/Core/src/Options/DeleteShortUrlsOptions.php +++ /dev/null @@ -1,16 +0,0 @@ -userAgents) > 0; - } -} diff --git a/module/Core/src/Options/TrackingOptions.php b/module/Core/src/Options/TrackingOptions.php deleted file mode 100644 index d4272374..00000000 --- a/module/Core/src/Options/TrackingOptions.php +++ /dev/null @@ -1,33 +0,0 @@ -disableTrackingFrom); - } - - public function queryHasDisableTrackParam(array $query): bool - { - return $this->disableTrackParam !== null && array_key_exists($this->disableTrackParam, $query); - } -} diff --git a/module/Core/src/Options/UrlShortenerOptions.php b/module/Core/src/Options/UrlShortenerOptions.php deleted file mode 100644 index 03091d75..00000000 --- a/module/Core/src/Options/UrlShortenerOptions.php +++ /dev/null @@ -1,36 +0,0 @@ - null, 'hostname' => null], - public readonly int $defaultShortCodesLength = DEFAULT_SHORT_CODES_LENGTH, - public readonly bool $autoResolveTitles = false, - public readonly bool $appendExtraPath = false, - public readonly bool $multiSegmentSlugsEnabled = false, - public readonly bool $trailingSlashEnabled = false, - public readonly ShortUrlMode $mode = ShortUrlMode::STRICT, - ) { - } - - public function isLooseMode(): bool - { - return $this->mode === ShortUrlMode::LOOSE; - } - - public function defaultDomain(): string - { - return $this->domain['hostname'] ?? ''; - } -} diff --git a/module/Core/src/ShortUrl/DeleteShortUrlService.php b/module/Core/src/ShortUrl/DeleteShortUrlService.php index b65381aa..aeb08c47 100644 --- a/module/Core/src/ShortUrl/DeleteShortUrlService.php +++ b/module/Core/src/ShortUrl/DeleteShortUrlService.php @@ -5,8 +5,8 @@ declare(strict_types=1); namespace Shlinkio\Shlink\Core\ShortUrl; use Doctrine\ORM\EntityManagerInterface; +use Shlinkio\Shlink\Core\Config\Options\DeleteShortUrlsOptions; use Shlinkio\Shlink\Core\Exception; -use Shlinkio\Shlink\Core\Options\DeleteShortUrlsOptions; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Model\ExpiredShortUrlsConditions; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlIdentifier; diff --git a/module/Core/src/ShortUrl/Helper/ShortCodeUniquenessHelper.php b/module/Core/src/ShortUrl/Helper/ShortCodeUniquenessHelper.php index b428019e..7f863f6c 100644 --- a/module/Core/src/ShortUrl/Helper/ShortCodeUniquenessHelper.php +++ b/module/Core/src/ShortUrl/Helper/ShortCodeUniquenessHelper.php @@ -5,7 +5,7 @@ declare(strict_types=1); namespace Shlinkio\Shlink\Core\ShortUrl\Helper; use Doctrine\ORM\EntityManagerInterface; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlIdentifier; use Shlinkio\Shlink\Core\ShortUrl\Repository\ShortUrlRepository; diff --git a/module/Core/src/ShortUrl/Helper/ShortUrlRedirectionBuilder.php b/module/Core/src/ShortUrl/Helper/ShortUrlRedirectionBuilder.php index 4d801c6f..375d8837 100644 --- a/module/Core/src/ShortUrl/Helper/ShortUrlRedirectionBuilder.php +++ b/module/Core/src/ShortUrl/Helper/ShortUrlRedirectionBuilder.php @@ -8,7 +8,7 @@ use GuzzleHttp\Psr7\Query; use GuzzleHttp\Psr7\Uri; use Laminas\Stdlib\ArrayUtils; use Psr\Http\Message\ServerRequestInterface; -use Shlinkio\Shlink\Core\Options\TrackingOptions; +use Shlinkio\Shlink\Core\Config\Options\TrackingOptions; use Shlinkio\Shlink\Core\RedirectRule\ShortUrlRedirectionResolverInterface; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; diff --git a/module/Core/src/ShortUrl/Helper/ShortUrlTitleResolutionHelper.php b/module/Core/src/ShortUrl/Helper/ShortUrlTitleResolutionHelper.php index 3f9b6225..0950e042 100644 --- a/module/Core/src/ShortUrl/Helper/ShortUrlTitleResolutionHelper.php +++ b/module/Core/src/ShortUrl/Helper/ShortUrlTitleResolutionHelper.php @@ -8,7 +8,7 @@ use Fig\Http\Message\RequestMethodInterface; use GuzzleHttp\ClientInterface; use GuzzleHttp\RequestOptions; use Psr\Http\Message\ResponseInterface; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Throwable; use function html_entity_decode; diff --git a/module/Core/src/ShortUrl/Middleware/ExtraPathRedirectMiddleware.php b/module/Core/src/ShortUrl/Middleware/ExtraPathRedirectMiddleware.php index 7780f91b..b164ffd6 100644 --- a/module/Core/src/ShortUrl/Middleware/ExtraPathRedirectMiddleware.php +++ b/module/Core/src/ShortUrl/Middleware/ExtraPathRedirectMiddleware.php @@ -9,9 +9,9 @@ use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\UriInterface; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ErrorHandler\Model\NotFoundType; use Shlinkio\Shlink\Core\Exception\ShortUrlNotFoundException; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlRedirectionBuilderInterface; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlIdentifier; use Shlinkio\Shlink\Core\ShortUrl\ShortUrlResolverInterface; diff --git a/module/Core/src/ShortUrl/Middleware/TrimTrailingSlashMiddleware.php b/module/Core/src/ShortUrl/Middleware/TrimTrailingSlashMiddleware.php index d3823b27..0b70c3ae 100644 --- a/module/Core/src/ShortUrl/Middleware/TrimTrailingSlashMiddleware.php +++ b/module/Core/src/ShortUrl/Middleware/TrimTrailingSlashMiddleware.php @@ -8,7 +8,7 @@ use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use function rtrim; diff --git a/module/Core/src/ShortUrl/Model/ShortUrlCreation.php b/module/Core/src/ShortUrl/Model/ShortUrlCreation.php index f5336075..c9c85e1b 100644 --- a/module/Core/src/ShortUrl/Model/ShortUrlCreation.php +++ b/module/Core/src/ShortUrl/Model/ShortUrlCreation.php @@ -5,8 +5,8 @@ declare(strict_types=1); namespace Shlinkio\Shlink\Core\ShortUrl\Model; use Cake\Chronos\Chronos; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Exception\ValidationException; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Helper\TitleResolutionModelInterface; use Shlinkio\Shlink\Core\ShortUrl\Model\Validation\ShortUrlInputFilter; use Shlinkio\Shlink\Rest\Entity\ApiKey; diff --git a/module/Core/src/ShortUrl/Model/Validation/CustomSlugFilter.php b/module/Core/src/ShortUrl/Model/Validation/CustomSlugFilter.php index 2512fc44..ba4e3959 100644 --- a/module/Core/src/ShortUrl/Model/Validation/CustomSlugFilter.php +++ b/module/Core/src/ShortUrl/Model/Validation/CustomSlugFilter.php @@ -5,7 +5,7 @@ declare(strict_types=1); namespace Shlinkio\Shlink\Core\ShortUrl\Model\Validation; use Laminas\Filter\FilterInterface; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use function is_string; use function str_replace; diff --git a/module/Core/src/ShortUrl/Model/Validation/CustomSlugValidator.php b/module/Core/src/ShortUrl/Model/Validation/CustomSlugValidator.php index 24afc72e..2bc417b4 100644 --- a/module/Core/src/ShortUrl/Model/Validation/CustomSlugValidator.php +++ b/module/Core/src/ShortUrl/Model/Validation/CustomSlugValidator.php @@ -5,7 +5,7 @@ declare(strict_types=1); namespace Shlinkio\Shlink\Core\ShortUrl\Model\Validation; use Laminas\Validator\AbstractValidator; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use function is_string; use function strpbrk; diff --git a/module/Core/src/ShortUrl/Model/Validation/ShortUrlInputFilter.php b/module/Core/src/ShortUrl/Model/Validation/ShortUrlInputFilter.php index f4a35969..3cd7744a 100644 --- a/module/Core/src/ShortUrl/Model/Validation/ShortUrlInputFilter.php +++ b/module/Core/src/ShortUrl/Model/Validation/ShortUrlInputFilter.php @@ -10,7 +10,7 @@ use Laminas\InputFilter\InputFilter; use Laminas\Validator; use Shlinkio\Shlink\Common\Validation\HostAndPortValidator; use Shlinkio\Shlink\Common\Validation\InputFactory; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Rest\Entity\ApiKey; use function is_string; diff --git a/module/Core/src/ShortUrl/Resolver/PersistenceShortUrlRelationResolver.php b/module/Core/src/ShortUrl/Resolver/PersistenceShortUrlRelationResolver.php index 3aa6c887..0b79f97d 100644 --- a/module/Core/src/ShortUrl/Resolver/PersistenceShortUrlRelationResolver.php +++ b/module/Core/src/ShortUrl/Resolver/PersistenceShortUrlRelationResolver.php @@ -8,8 +8,8 @@ use Doctrine\Common\Collections; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Events; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Domain\Entity\Domain; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Tag\Entity\Tag; use Symfony\Component\Lock\Lock; use Symfony\Component\Lock\LockFactory; diff --git a/module/Core/src/ShortUrl/ShortUrlListService.php b/module/Core/src/ShortUrl/ShortUrlListService.php index f8441454..b6539256 100644 --- a/module/Core/src/ShortUrl/ShortUrlListService.php +++ b/module/Core/src/ShortUrl/ShortUrlListService.php @@ -5,7 +5,7 @@ declare(strict_types=1); namespace Shlinkio\Shlink\Core\ShortUrl; use Shlinkio\Shlink\Common\Paginator\Paginator; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlsParams; use Shlinkio\Shlink\Core\ShortUrl\Paginator\Adapter\ShortUrlRepositoryAdapter; use Shlinkio\Shlink\Core\ShortUrl\Repository\ShortUrlListRepositoryInterface; diff --git a/module/Core/src/ShortUrl/ShortUrlResolver.php b/module/Core/src/ShortUrl/ShortUrlResolver.php index 42d274c0..14727ff5 100644 --- a/module/Core/src/ShortUrl/ShortUrlResolver.php +++ b/module/Core/src/ShortUrl/ShortUrlResolver.php @@ -5,8 +5,8 @@ declare(strict_types=1); namespace Shlinkio\Shlink\Core\ShortUrl; use Doctrine\ORM\EntityManagerInterface; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Exception\ShortUrlNotFoundException; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlIdentifier; use Shlinkio\Shlink\Core\ShortUrl\Repository\ShortUrlRepository; diff --git a/module/Core/src/Util/RedirectResponseHelper.php b/module/Core/src/Util/RedirectResponseHelper.php index 01e581a7..edb04b8e 100644 --- a/module/Core/src/Util/RedirectResponseHelper.php +++ b/module/Core/src/Util/RedirectResponseHelper.php @@ -6,7 +6,7 @@ namespace Shlinkio\Shlink\Core\Util; use Laminas\Diactoros\Response\RedirectResponse; use Psr\Http\Message\ResponseInterface; -use Shlinkio\Shlink\Core\Options\RedirectOptions; +use Shlinkio\Shlink\Core\Config\Options\RedirectOptions; use function sprintf; diff --git a/module/Core/src/Visit/Model/Visitor.php b/module/Core/src/Visit/Model/Visitor.php index 9cf524bc..ca5d79b2 100644 --- a/module/Core/src/Visit/Model/Visitor.php +++ b/module/Core/src/Visit/Model/Visitor.php @@ -5,7 +5,7 @@ declare(strict_types=1); namespace Shlinkio\Shlink\Core\Visit\Model; use Psr\Http\Message\ServerRequestInterface; -use Shlinkio\Shlink\Core\Options\TrackingOptions; +use Shlinkio\Shlink\Core\Config\Options\TrackingOptions; use function Shlinkio\Shlink\Core\ipAddressFromRequest; use function Shlinkio\Shlink\Core\isCrawler; diff --git a/module/Core/src/Visit/RequestTracker.php b/module/Core/src/Visit/RequestTracker.php index 824e7c24..986e221c 100644 --- a/module/Core/src/Visit/RequestTracker.php +++ b/module/Core/src/Visit/RequestTracker.php @@ -7,9 +7,9 @@ namespace Shlinkio\Shlink\Core\Visit; use Fig\Http\Message\RequestMethodInterface; use Mezzio\Router\Middleware\ImplicitHeadMiddleware; use Psr\Http\Message\ServerRequestInterface; +use Shlinkio\Shlink\Core\Config\Options\TrackingOptions; use Shlinkio\Shlink\Core\ErrorHandler\Model\NotFoundType; use Shlinkio\Shlink\Core\Exception\InvalidIpFormatException; -use Shlinkio\Shlink\Core\Options\TrackingOptions; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\Util\IpAddressUtils; use Shlinkio\Shlink\Core\Visit\Model\Visitor; diff --git a/module/Core/src/Visit/VisitsTracker.php b/module/Core/src/Visit/VisitsTracker.php index f2e26493..85085220 100644 --- a/module/Core/src/Visit/VisitsTracker.php +++ b/module/Core/src/Visit/VisitsTracker.php @@ -6,8 +6,8 @@ namespace Shlinkio\Shlink\Core\Visit; use Doctrine\ORM; use Psr\EventDispatcher\EventDispatcherInterface; +use Shlinkio\Shlink\Core\Config\Options\TrackingOptions; use Shlinkio\Shlink\Core\EventDispatcher\Event\UrlVisited; -use Shlinkio\Shlink\Core\Options\TrackingOptions; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\Visit\Entity\Visit; use Shlinkio\Shlink\Core\Visit\Model\Visitor; diff --git a/module/Core/test/Action/QrCodeActionTest.php b/module/Core/test/Action/QrCodeActionTest.php index 08564bf9..9afae793 100644 --- a/module/Core/test/Action/QrCodeActionTest.php +++ b/module/Core/test/Action/QrCodeActionTest.php @@ -16,8 +16,8 @@ use Psr\Http\Server\RequestHandlerInterface; use Psr\Log\NullLogger; use Shlinkio\Shlink\Common\Response\QrCodeResponse; use Shlinkio\Shlink\Core\Action\QrCodeAction; +use Shlinkio\Shlink\Core\Config\Options\QrCodeOptions; use Shlinkio\Shlink\Core\Exception\ShortUrlNotFoundException; -use Shlinkio\Shlink\Core\Options\QrCodeOptions; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifier; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlIdentifier; diff --git a/module/Core/test/Action/RobotsActionTest.php b/module/Core/test/Action/RobotsActionTest.php index 1d83fb8c..38d80de3 100644 --- a/module/Core/test/Action/RobotsActionTest.php +++ b/module/Core/test/Action/RobotsActionTest.php @@ -10,8 +10,8 @@ use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Shlinkio\Shlink\Core\Action\RobotsAction; +use Shlinkio\Shlink\Core\Config\Options\RobotsOptions; use Shlinkio\Shlink\Core\Crawling\CrawlingHelperInterface; -use Shlinkio\Shlink\Core\Options\RobotsOptions; class RobotsActionTest extends TestCase { diff --git a/module/Core/test/Config/NotFoundRedirectResolverTest.php b/module/Core/test/Config/NotFoundRedirectResolverTest.php index 5ef5db2b..75df0948 100644 --- a/module/Core/test/Config/NotFoundRedirectResolverTest.php +++ b/module/Core/test/Config/NotFoundRedirectResolverTest.php @@ -18,8 +18,8 @@ use Psr\Http\Message\UriInterface; use Psr\Log\NullLogger; use Shlinkio\Shlink\Core\Action\RedirectAction; use Shlinkio\Shlink\Core\Config\NotFoundRedirectResolver; +use Shlinkio\Shlink\Core\Config\Options\NotFoundRedirectOptions; use Shlinkio\Shlink\Core\ErrorHandler\Model\NotFoundType; -use Shlinkio\Shlink\Core\Options\NotFoundRedirectOptions; use Shlinkio\Shlink\Core\Util\RedirectResponseHelperInterface; use function Laminas\Stratigility\middleware; diff --git a/module/Core/test/ErrorHandler/NotFoundRedirectHandlerTest.php b/module/Core/test/ErrorHandler/NotFoundRedirectHandlerTest.php index 53642f3a..25d9952f 100644 --- a/module/Core/test/ErrorHandler/NotFoundRedirectHandlerTest.php +++ b/module/Core/test/ErrorHandler/NotFoundRedirectHandlerTest.php @@ -16,11 +16,11 @@ use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Message\UriInterface; use Psr\Http\Server\RequestHandlerInterface; use Shlinkio\Shlink\Core\Config\NotFoundRedirectResolverInterface; +use Shlinkio\Shlink\Core\Config\Options\NotFoundRedirectOptions; use Shlinkio\Shlink\Core\Domain\DomainServiceInterface; use Shlinkio\Shlink\Core\Domain\Entity\Domain; use Shlinkio\Shlink\Core\ErrorHandler\Model\NotFoundType; use Shlinkio\Shlink\Core\ErrorHandler\NotFoundRedirectHandler; -use Shlinkio\Shlink\Core\Options\NotFoundRedirectOptions; class NotFoundRedirectHandlerTest extends TestCase { diff --git a/module/Core/test/EventDispatcher/Helper/EnabledListenerCheckerTest.php b/module/Core/test/EventDispatcher/Helper/EnabledListenerCheckerTest.php index cebde437..180ab48d 100644 --- a/module/Core/test/EventDispatcher/Helper/EnabledListenerCheckerTest.php +++ b/module/Core/test/EventDispatcher/Helper/EnabledListenerCheckerTest.php @@ -8,6 +8,7 @@ use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; use Shlinkio\Shlink\Common\Mercure\MercureOptions; +use Shlinkio\Shlink\Core\Config\Options\RabbitMqOptions; use Shlinkio\Shlink\Core\EventDispatcher\Helper\EnabledListenerChecker; use Shlinkio\Shlink\Core\EventDispatcher\Matomo\SendVisitToMatomo; use Shlinkio\Shlink\Core\EventDispatcher\Mercure\NotifyNewShortUrlToMercure; @@ -18,7 +19,6 @@ use Shlinkio\Shlink\Core\EventDispatcher\RedisPubSub\NotifyNewShortUrlToRedis; use Shlinkio\Shlink\Core\EventDispatcher\RedisPubSub\NotifyVisitToRedis; use Shlinkio\Shlink\Core\EventDispatcher\UpdateGeoLiteDb; use Shlinkio\Shlink\Core\Matomo\MatomoOptions; -use Shlinkio\Shlink\Core\Options\RabbitMqOptions; use Shlinkio\Shlink\IpGeolocation\GeoLite2\GeoLite2Options; class EnabledListenerCheckerTest extends TestCase diff --git a/module/Core/test/EventDispatcher/RabbitMq/NotifyNewShortUrlToRabbitMqTest.php b/module/Core/test/EventDispatcher/RabbitMq/NotifyNewShortUrlToRabbitMqTest.php index fc44fd87..b0c5a0e0 100644 --- a/module/Core/test/EventDispatcher/RabbitMq/NotifyNewShortUrlToRabbitMqTest.php +++ b/module/Core/test/EventDispatcher/RabbitMq/NotifyNewShortUrlToRabbitMqTest.php @@ -15,11 +15,11 @@ use Psr\Log\LoggerInterface; use RuntimeException; use Shlinkio\Shlink\Common\UpdatePublishing\PublishingHelperInterface; use Shlinkio\Shlink\Common\UpdatePublishing\Update; +use Shlinkio\Shlink\Core\Config\Options\RabbitMqOptions; use Shlinkio\Shlink\Core\EventDispatcher\Event\ShortUrlCreated; use Shlinkio\Shlink\Core\EventDispatcher\PublishingUpdatesGeneratorInterface; use Shlinkio\Shlink\Core\EventDispatcher\RabbitMq\NotifyNewShortUrlToRabbitMq; use Shlinkio\Shlink\Core\EventDispatcher\Topic; -use Shlinkio\Shlink\Core\Options\RabbitMqOptions; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Throwable; diff --git a/module/Core/test/EventDispatcher/RabbitMq/NotifyVisitToRabbitMqTest.php b/module/Core/test/EventDispatcher/RabbitMq/NotifyVisitToRabbitMqTest.php index 2251d301..ac744824 100644 --- a/module/Core/test/EventDispatcher/RabbitMq/NotifyVisitToRabbitMqTest.php +++ b/module/Core/test/EventDispatcher/RabbitMq/NotifyVisitToRabbitMqTest.php @@ -16,10 +16,10 @@ use Psr\Log\LoggerInterface; use RuntimeException; use Shlinkio\Shlink\Common\UpdatePublishing\PublishingHelperInterface; use Shlinkio\Shlink\Common\UpdatePublishing\Update; +use Shlinkio\Shlink\Core\Config\Options\RabbitMqOptions; use Shlinkio\Shlink\Core\EventDispatcher\Event\VisitLocated; use Shlinkio\Shlink\Core\EventDispatcher\PublishingUpdatesGeneratorInterface; use Shlinkio\Shlink\Core\EventDispatcher\RabbitMq\NotifyVisitToRabbitMq; -use Shlinkio\Shlink\Core\Options\RabbitMqOptions; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlCreation; use Shlinkio\Shlink\Core\Visit\Entity\Visit; diff --git a/module/Core/test/ShortUrl/DeleteShortUrlServiceTest.php b/module/Core/test/ShortUrl/DeleteShortUrlServiceTest.php index 4788818e..faddafeb 100644 --- a/module/Core/test/ShortUrl/DeleteShortUrlServiceTest.php +++ b/module/Core/test/ShortUrl/DeleteShortUrlServiceTest.php @@ -9,8 +9,8 @@ use Doctrine\ORM\EntityManagerInterface; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; +use Shlinkio\Shlink\Core\Config\Options\DeleteShortUrlsOptions; use Shlinkio\Shlink\Core\Exception\DeleteShortUrlException; -use Shlinkio\Shlink\Core\Options\DeleteShortUrlsOptions; use Shlinkio\Shlink\Core\ShortUrl\DeleteShortUrlService; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Model\ExpiredShortUrlsConditions; diff --git a/module/Core/test/ShortUrl/Entity/ShortUrlTest.php b/module/Core/test/ShortUrl/Entity/ShortUrlTest.php index be5b4101..b3720254 100644 --- a/module/Core/test/ShortUrl/Entity/ShortUrlTest.php +++ b/module/Core/test/ShortUrl/Entity/ShortUrlTest.php @@ -9,8 +9,8 @@ use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\Attributes\TestWith; use PHPUnit\Framework\TestCase; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Exception\ShortCodeCannotBeRegeneratedException; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlCreation; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlMode; diff --git a/module/Core/test/ShortUrl/Helper/ShortCodeUniquenessHelperTest.php b/module/Core/test/ShortUrl/Helper/ShortCodeUniquenessHelperTest.php index efbbde21..de1402f6 100644 --- a/module/Core/test/ShortUrl/Helper/ShortCodeUniquenessHelperTest.php +++ b/module/Core/test/ShortUrl/Helper/ShortCodeUniquenessHelperTest.php @@ -9,8 +9,8 @@ use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Domain\Entity\Domain; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortCodeUniquenessHelper; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlIdentifier; diff --git a/module/Core/test/ShortUrl/Helper/ShortUrlRedirectionBuilderTest.php b/module/Core/test/ShortUrl/Helper/ShortUrlRedirectionBuilderTest.php index afe83c8d..d1283a78 100644 --- a/module/Core/test/ShortUrl/Helper/ShortUrlRedirectionBuilderTest.php +++ b/module/Core/test/ShortUrl/Helper/ShortUrlRedirectionBuilderTest.php @@ -12,7 +12,7 @@ use PHPUnit\Framework\Attributes\TestWith; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Psr\Http\Message\ServerRequestInterface; -use Shlinkio\Shlink\Core\Options\TrackingOptions; +use Shlinkio\Shlink\Core\Config\Options\TrackingOptions; use Shlinkio\Shlink\Core\RedirectRule\ShortUrlRedirectionResolverInterface; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlRedirectionBuilder; diff --git a/module/Core/test/ShortUrl/Helper/ShortUrlTitleResolutionHelperTest.php b/module/Core/test/ShortUrl/Helper/ShortUrlTitleResolutionHelperTest.php index 4e3056b3..b5b8e00c 100644 --- a/module/Core/test/ShortUrl/Helper/ShortUrlTitleResolutionHelperTest.php +++ b/module/Core/test/ShortUrl/Helper/ShortUrlTitleResolutionHelperTest.php @@ -16,7 +16,7 @@ use PHPUnit\Framework\Attributes\TestWith; use PHPUnit\Framework\MockObject\Builder\InvocationMocker; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlTitleResolutionHelper; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlCreation; diff --git a/module/Core/test/ShortUrl/Middleware/ExtraPathRedirectMiddlewareTest.php b/module/Core/test/ShortUrl/Middleware/ExtraPathRedirectMiddlewareTest.php index 3965fe18..6815acb6 100644 --- a/module/Core/test/ShortUrl/Middleware/ExtraPathRedirectMiddlewareTest.php +++ b/module/Core/test/ShortUrl/Middleware/ExtraPathRedirectMiddlewareTest.php @@ -16,9 +16,9 @@ use PHPUnit\Framework\TestCase; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; use Shlinkio\Shlink\Core\Action\RedirectAction; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ErrorHandler\Model\NotFoundType; use Shlinkio\Shlink\Core\Exception\ShortUrlNotFoundException; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlRedirectionBuilderInterface; use Shlinkio\Shlink\Core\ShortUrl\Middleware\ExtraPathRedirectMiddleware; diff --git a/module/Core/test/ShortUrl/Middleware/TrimTrailingSlashMiddlewareTest.php b/module/Core/test/ShortUrl/Middleware/TrimTrailingSlashMiddlewareTest.php index b05ab7d9..c37c81a5 100644 --- a/module/Core/test/ShortUrl/Middleware/TrimTrailingSlashMiddlewareTest.php +++ b/module/Core/test/ShortUrl/Middleware/TrimTrailingSlashMiddlewareTest.php @@ -13,7 +13,7 @@ use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Middleware\TrimTrailingSlashMiddleware; class TrimTrailingSlashMiddlewareTest extends TestCase diff --git a/module/Core/test/ShortUrl/Model/ShortUrlCreationTest.php b/module/Core/test/ShortUrl/Model/ShortUrlCreationTest.php index b84b5b27..e963923b 100644 --- a/module/Core/test/ShortUrl/Model/ShortUrlCreationTest.php +++ b/module/Core/test/ShortUrl/Model/ShortUrlCreationTest.php @@ -8,8 +8,8 @@ use Cake\Chronos\Chronos; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Exception\ValidationException; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlCreation; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlMode; use Shlinkio\Shlink\Core\ShortUrl\Model\Validation\ShortUrlInputFilter; diff --git a/module/Core/test/ShortUrl/Model/Validation/CustomSlugValidatorTest.php b/module/Core/test/ShortUrl/Model/Validation/CustomSlugValidatorTest.php index 290fe63d..86f695c7 100644 --- a/module/Core/test/ShortUrl/Model/Validation/CustomSlugValidatorTest.php +++ b/module/Core/test/ShortUrl/Model/Validation/CustomSlugValidatorTest.php @@ -7,7 +7,7 @@ namespace ShlinkioTest\Shlink\Core\ShortUrl\Model\Validation; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Model\Validation\CustomSlugValidator; use stdClass; diff --git a/module/Core/test/ShortUrl/Resolver/PersistenceShortUrlRelationResolverTest.php b/module/Core/test/ShortUrl/Resolver/PersistenceShortUrlRelationResolverTest.php index 43860909..d6223734 100644 --- a/module/Core/test/ShortUrl/Resolver/PersistenceShortUrlRelationResolverTest.php +++ b/module/Core/test/ShortUrl/Resolver/PersistenceShortUrlRelationResolverTest.php @@ -10,9 +10,9 @@ use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Domain\Entity\Domain; use Shlinkio\Shlink\Core\Domain\Repository\DomainRepository; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Resolver\PersistenceShortUrlRelationResolver; use Shlinkio\Shlink\Core\Tag\Entity\Tag; use Shlinkio\Shlink\Core\Tag\Repository\TagRepository; diff --git a/module/Core/test/ShortUrl/ShortUrlListServiceTest.php b/module/Core/test/ShortUrl/ShortUrlListServiceTest.php index a469ed60..d8663761 100644 --- a/module/Core/test/ShortUrl/ShortUrlListServiceTest.php +++ b/module/Core/test/ShortUrl/ShortUrlListServiceTest.php @@ -8,7 +8,7 @@ use PHPUnit\Framework\Attributes\DataProviderExternal; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlsParams; use Shlinkio\Shlink\Core\ShortUrl\Repository\ShortUrlListRepositoryInterface; diff --git a/module/Core/test/ShortUrl/ShortUrlResolverTest.php b/module/Core/test/ShortUrl/ShortUrlResolverTest.php index 1b3aa564..e8443a13 100644 --- a/module/Core/test/ShortUrl/ShortUrlResolverTest.php +++ b/module/Core/test/ShortUrl/ShortUrlResolverTest.php @@ -12,8 +12,8 @@ use PHPUnit\Framework\Attributes\DataProviderExternal; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Exception\ShortUrlNotFoundException; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlCreation; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlIdentifier; diff --git a/module/Core/test/Util/RedirectResponseHelperTest.php b/module/Core/test/Util/RedirectResponseHelperTest.php index 63e55e2e..89d3fa5a 100644 --- a/module/Core/test/Util/RedirectResponseHelperTest.php +++ b/module/Core/test/Util/RedirectResponseHelperTest.php @@ -8,7 +8,7 @@ use Laminas\Diactoros\Response\RedirectResponse; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; -use Shlinkio\Shlink\Core\Options\RedirectOptions; +use Shlinkio\Shlink\Core\Config\Options\RedirectOptions; use Shlinkio\Shlink\Core\Util\RedirectResponseHelper; class RedirectResponseHelperTest extends TestCase diff --git a/module/Core/test/Visit/Model/VisitorTest.php b/module/Core/test/Visit/Model/VisitorTest.php index 74065d7d..04e57179 100644 --- a/module/Core/test/Visit/Model/VisitorTest.php +++ b/module/Core/test/Visit/Model/VisitorTest.php @@ -7,7 +7,7 @@ namespace ShlinkioTest\Shlink\Core\Visit\Model; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; -use Shlinkio\Shlink\Core\Options\TrackingOptions; +use Shlinkio\Shlink\Core\Config\Options\TrackingOptions; use Shlinkio\Shlink\Core\Visit\Model\Visitor; use function random_int; diff --git a/module/Core/test/Visit/RequestTrackerTest.php b/module/Core/test/Visit/RequestTrackerTest.php index c8746f91..ae0a74c4 100644 --- a/module/Core/test/Visit/RequestTrackerTest.php +++ b/module/Core/test/Visit/RequestTrackerTest.php @@ -13,8 +13,8 @@ use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Psr\Http\Message\ServerRequestInterface; use Shlinkio\Shlink\Common\Middleware\IpAddressMiddlewareFactory; +use Shlinkio\Shlink\Core\Config\Options\TrackingOptions; use Shlinkio\Shlink\Core\ErrorHandler\Model\NotFoundType; -use Shlinkio\Shlink\Core\Options\TrackingOptions; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\Visit\Model\Visitor; use Shlinkio\Shlink\Core\Visit\RequestTracker; diff --git a/module/Core/test/Visit/VisitsTrackerTest.php b/module/Core/test/Visit/VisitsTrackerTest.php index d6cbf4bf..414f5254 100644 --- a/module/Core/test/Visit/VisitsTrackerTest.php +++ b/module/Core/test/Visit/VisitsTrackerTest.php @@ -10,8 +10,8 @@ use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Psr\EventDispatcher\EventDispatcherInterface; +use Shlinkio\Shlink\Core\Config\Options\TrackingOptions; use Shlinkio\Shlink\Core\EventDispatcher\Event\UrlVisited; -use Shlinkio\Shlink\Core\Options\TrackingOptions; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\Visit\Entity\Visit; use Shlinkio\Shlink\Core\Visit\Model\Visitor; diff --git a/module/Rest/config/dependencies.config.php b/module/Rest/config/dependencies.config.php index d334d5b0..3d7a66a4 100644 --- a/module/Rest/config/dependencies.config.php +++ b/module/Rest/config/dependencies.config.php @@ -10,8 +10,8 @@ use Mezzio\ProblemDetails\ProblemDetailsResponseFactory; use Mezzio\Router\Middleware\ImplicitOptionsMiddleware; use Psr\Log\LoggerInterface; use Shlinkio\Shlink\Common\Mercure\LcobucciJwtProvider; +use Shlinkio\Shlink\Core\Config; use Shlinkio\Shlink\Core\Domain\DomainService; -use Shlinkio\Shlink\Core\Options; use Shlinkio\Shlink\Core\RedirectRule; use Shlinkio\Shlink\Core\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Transformer\ShortUrlDataTransformer; @@ -64,17 +64,17 @@ return [ ConfigAbstractFactory::class => [ ApiKeyService::class => ['em'], - Action\HealthAction::class => ['em', Options\AppOptions::class], + Action\HealthAction::class => ['em', Config\Options\AppOptions::class], Action\MercureInfoAction::class => [LcobucciJwtProvider::class, 'config.mercure'], Action\ShortUrl\CreateShortUrlAction::class => [ ShortUrl\UrlShortener::class, ShortUrlDataTransformer::class, - Options\UrlShortenerOptions::class, + Config\Options\UrlShortenerOptions::class, ], Action\ShortUrl\SingleStepCreateShortUrlAction::class => [ ShortUrl\UrlShortener::class, ShortUrlDataTransformer::class, - Options\UrlShortenerOptions::class, + Config\Options\UrlShortenerOptions::class, ], Action\ShortUrl\EditShortUrlAction::class => [ShortUrl\ShortUrlService::class, ShortUrlDataTransformer::class], Action\ShortUrl\DeleteShortUrlAction::class => [ShortUrl\DeleteShortUrlService::class], @@ -101,7 +101,7 @@ return [ Action\Tag\TagsStatsAction::class => [TagService::class], Action\Tag\DeleteTagsAction::class => [TagService::class], Action\Tag\UpdateTagAction::class => [TagService::class], - Action\Domain\ListDomainsAction::class => [DomainService::class, Options\NotFoundRedirectOptions::class], + Action\Domain\ListDomainsAction::class => [DomainService::class, Config\Options\NotFoundRedirectOptions::class], Action\Domain\DomainRedirectsAction::class => [DomainService::class], Action\RedirectRule\ListRedirectRulesAction::class => [ ShortUrl\ShortUrlResolver::class, diff --git a/module/Rest/src/Action/Domain/ListDomainsAction.php b/module/Rest/src/Action/Domain/ListDomainsAction.php index e50ada16..d156a720 100644 --- a/module/Rest/src/Action/Domain/ListDomainsAction.php +++ b/module/Rest/src/Action/Domain/ListDomainsAction.php @@ -8,8 +8,8 @@ use Laminas\Diactoros\Response\JsonResponse; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Shlinkio\Shlink\Core\Config\NotFoundRedirects; +use Shlinkio\Shlink\Core\Config\Options\NotFoundRedirectOptions; use Shlinkio\Shlink\Core\Domain\DomainServiceInterface; -use Shlinkio\Shlink\Core\Options\NotFoundRedirectOptions; use Shlinkio\Shlink\Rest\Action\AbstractRestAction; use Shlinkio\Shlink\Rest\Middleware\AuthenticationMiddleware; diff --git a/module/Rest/src/Action/HealthAction.php b/module/Rest/src/Action/HealthAction.php index 809bf4d1..ce731330 100644 --- a/module/Rest/src/Action/HealthAction.php +++ b/module/Rest/src/Action/HealthAction.php @@ -8,7 +8,7 @@ use Doctrine\ORM\EntityManagerInterface; use Laminas\Diactoros\Response\JsonResponse; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; -use Shlinkio\Shlink\Core\Options\AppOptions; +use Shlinkio\Shlink\Core\Config\Options\AppOptions; use Throwable; class HealthAction extends AbstractRestAction diff --git a/module/Rest/src/Action/ShortUrl/AbstractCreateShortUrlAction.php b/module/Rest/src/Action/ShortUrl/AbstractCreateShortUrlAction.php index 65f47d95..75d5480b 100644 --- a/module/Rest/src/Action/ShortUrl/AbstractCreateShortUrlAction.php +++ b/module/Rest/src/Action/ShortUrl/AbstractCreateShortUrlAction.php @@ -7,8 +7,8 @@ namespace Shlinkio\Shlink\Rest\Action\ShortUrl; use Laminas\Diactoros\Response\JsonResponse; use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Exception\ValidationException; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlCreation; use Shlinkio\Shlink\Core\ShortUrl\Transformer\ShortUrlDataTransformerInterface; use Shlinkio\Shlink\Core\ShortUrl\UrlShortenerInterface; diff --git a/module/Rest/test/Action/Domain/ListDomainsActionTest.php b/module/Rest/test/Action/Domain/ListDomainsActionTest.php index 41906539..4864048c 100644 --- a/module/Rest/test/Action/Domain/ListDomainsActionTest.php +++ b/module/Rest/test/Action/Domain/ListDomainsActionTest.php @@ -10,10 +10,10 @@ use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Shlinkio\Shlink\Core\Config\NotFoundRedirects; +use Shlinkio\Shlink\Core\Config\Options\NotFoundRedirectOptions; use Shlinkio\Shlink\Core\Domain\DomainServiceInterface; use Shlinkio\Shlink\Core\Domain\Entity\Domain; use Shlinkio\Shlink\Core\Domain\Model\DomainItem; -use Shlinkio\Shlink\Core\Options\NotFoundRedirectOptions; use Shlinkio\Shlink\Rest\Action\Domain\ListDomainsAction; use Shlinkio\Shlink\Rest\Entity\ApiKey; diff --git a/module/Rest/test/Action/Domain/Request/DomainRedirectsRequestTest.php b/module/Rest/test/Action/Domain/Request/DomainRedirectsRequestTest.php index 89b93b80..292c1748 100644 --- a/module/Rest/test/Action/Domain/Request/DomainRedirectsRequestTest.php +++ b/module/Rest/test/Action/Domain/Request/DomainRedirectsRequestTest.php @@ -8,8 +8,8 @@ use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; use Shlinkio\Shlink\Core\Config\NotFoundRedirectConfigInterface; +use Shlinkio\Shlink\Core\Config\Options\NotFoundRedirectOptions; use Shlinkio\Shlink\Core\Exception\ValidationException; -use Shlinkio\Shlink\Core\Options\NotFoundRedirectOptions; use Shlinkio\Shlink\Rest\Action\Domain\Request\DomainRedirectsRequest; class DomainRedirectsRequestTest extends TestCase diff --git a/module/Rest/test/Action/HealthActionTest.php b/module/Rest/test/Action/HealthActionTest.php index 59075646..aae90f49 100644 --- a/module/Rest/test/Action/HealthActionTest.php +++ b/module/Rest/test/Action/HealthActionTest.php @@ -14,7 +14,7 @@ use Laminas\Diactoros\ServerRequest; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Shlinkio\Shlink\Core\Options\AppOptions; +use Shlinkio\Shlink\Core\Config\Options\AppOptions; use Shlinkio\Shlink\Rest\Action\HealthAction; class HealthActionTest extends TestCase diff --git a/module/Rest/test/Action/ShortUrl/CreateShortUrlActionTest.php b/module/Rest/test/Action/ShortUrl/CreateShortUrlActionTest.php index 7361df5c..06cd5554 100644 --- a/module/Rest/test/Action/ShortUrl/CreateShortUrlActionTest.php +++ b/module/Rest/test/Action/ShortUrl/CreateShortUrlActionTest.php @@ -12,8 +12,8 @@ use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Exception\ValidationException; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlCreation; use Shlinkio\Shlink\Core\ShortUrl\Model\UrlShorteningResult; diff --git a/module/Rest/test/Action/ShortUrl/SingleStepCreateShortUrlActionTest.php b/module/Rest/test/Action/ShortUrl/SingleStepCreateShortUrlActionTest.php index 69914f0e..d28c3b49 100644 --- a/module/Rest/test/Action/ShortUrl/SingleStepCreateShortUrlActionTest.php +++ b/module/Rest/test/Action/ShortUrl/SingleStepCreateShortUrlActionTest.php @@ -8,7 +8,7 @@ use Laminas\Diactoros\ServerRequest; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlCreation; use Shlinkio\Shlink\Core\ShortUrl\Model\UrlShorteningResult; diff --git a/shlink-dev.env b/shlink-dev.env new file mode 100644 index 00000000..02c2aa92 --- /dev/null +++ b/shlink-dev.env @@ -0,0 +1,65 @@ +LC_ALL=C +#APP_ENV=dev + +# URL shortener +IS_HTTPS_ENABLED=false + +# Database - MySQL +DB_DRIVER=mysql +DB_USER=root +DB_PASSWORD=root +DB_NAME=shlink +# DB_NAME=shlink_foo +DB_HOST=shlink_db_mysql + +# Database - Maria +#DB_DRIVER=maria +#DB_USER=root +#DB_PASSWORD=root +#DB_NAME=shlink_foo +#DB_HOST=shlink_db_maria + +# Database - Postgres +#DB_DRIVER=postgres +#DB_USER=postgres +#DB_PASSWORD=root +#DB_NAME=shlink_foo +#DB_HOST=shlink_db_postgres + +# Database - MSSQL +#DB_DRIVER=mssql +#DB_USER=sa +#DB_PASSWORD=Passw0rd! +#DB_NAME=shlink_foo +#DB_HOST=shlink_db_ms + +# Matomo +# Dev matomo instance needs to be manually configured once before enabling the configuration below: +# 1. Go to http://localhost:8003 and follow the installation instructions. +# 2. Open data/infra/matomo/config/config.ini.php and replace `trusted_hosts[] = "localhost"` with +# `trusted_hosts[] = "localhost:8003"` (see https://github.com/matomo-org/matomo/issues/9549) +# 3. Go to http://localhost:8003/index.php?module=SitesManager&action=index and paste the ID for the site you just +# created into the `MATOMO_SITE_ID` var below. +# 4. Go to http://localhost:8003/index.php?module=UsersManager&action=userSecurity, scroll down, click +# "Create new token" and once generated, paste the token into the `MATOMO_API_TOKEN` var below. +# 5. Copy the config below and paste it in a new shlink-dev.local.env file. +#MATOMO_ENABLED=false +#MATOMO_BASE_URL=http://shlink_matomo +#MATOMO_SITE_ID= +#MATOMO_API_TOKEN= + +# Mercure +MERCURE_PUBLIC_HUB_URL=http://localhost:8002 +MERCURE_INTERNAL_HUB_URL=http://shlink_mercure_proxy +MERCURE_JWT_SECRET=mercure_jwt_key_long_enough_to_avoid_error + +# RabbitMQ +RABBITMQ_ENABLED=true +RABBITMQ_HOST=shlink_rabbitmq +RABBITMQ_PORT=5672 +RABBITMQ_USER=rabbit +RABBITMQ_PASSWORD=rabbit + +# Redis +REDIS_PUB_SUB_ENABLED=true +REDIS_SERVERS=tcp://shlink_redis:6379 From b991b1699eff8ef585ec283e61585bcd09eda598 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sun, 20 Oct 2024 12:35:03 +0200 Subject: [PATCH 06/25] Define unique dev .env file --- .gitignore | 2 +- docker-compose.yml | 2 -- shlink-dev.env => shlink-dev.env.dist | 7 ++++--- 3 files changed, 5 insertions(+), 6 deletions(-) rename shlink-dev.env => shlink-dev.env.dist (94%) diff --git a/.gitignore b/.gitignore index e539b609..96ac6719 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,4 @@ docs/mercure.html docker-compose.override.yml .phpunit.result.cache docs/swagger/swagger-inlined.json -shlink-dev.local.env +shlink-dev.env diff --git a/docker-compose.yml b/docker-compose.yml index 718ba4c1..b9fbd210 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -36,7 +36,6 @@ services: DEFAULT_DOMAIN: localhost:8000 env_file: - path: shlink-dev.env - - path: shlink-dev.local.env required: false extra_hosts: - 'host.docker.internal:host-gateway' @@ -66,7 +65,6 @@ services: DEFAULT_DOMAIN: localhost:8800 env_file: - path: shlink-dev.env - - path: shlink-dev.local.env required: false extra_hosts: - 'host.docker.internal:host-gateway' diff --git a/shlink-dev.env b/shlink-dev.env.dist similarity index 94% rename from shlink-dev.env rename to shlink-dev.env.dist index 02c2aa92..5ab21ea1 100644 --- a/shlink-dev.env +++ b/shlink-dev.env.dist @@ -1,5 +1,6 @@ LC_ALL=C #APP_ENV=dev +#GEOLITE_LICENSE_KEY= # URL shortener IS_HTTPS_ENABLED=false @@ -9,7 +10,7 @@ DB_DRIVER=mysql DB_USER=root DB_PASSWORD=root DB_NAME=shlink -# DB_NAME=shlink_foo +#DB_NAME=shlink_foo DB_HOST=shlink_db_mysql # Database - Maria @@ -43,8 +44,8 @@ DB_HOST=shlink_db_mysql # 4. Go to http://localhost:8003/index.php?module=UsersManager&action=userSecurity, scroll down, click # "Create new token" and once generated, paste the token into the `MATOMO_API_TOKEN` var below. # 5. Copy the config below and paste it in a new shlink-dev.local.env file. -#MATOMO_ENABLED=false -#MATOMO_BASE_URL=http://shlink_matomo +MATOMO_ENABLED=false +MATOMO_BASE_URL=http://shlink_matomo #MATOMO_SITE_ID= #MATOMO_API_TOKEN= From c8e5196aabbbdd8e8189a542d7a864fdee3ca1fc Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sun, 20 Oct 2024 12:52:00 +0200 Subject: [PATCH 07/25] Remove dependencies on url_shortener raw config --- config/autoload/routes.config.php | 2 -- config/autoload/url-shortener.global.php | 34 ------------------- module/CLI/config/dependencies.config.php | 2 +- module/CLI/src/ApiKey/RoleResolver.php | 9 ++--- module/CLI/test/ApiKey/RoleResolverTest.php | 6 +++- .../ShortUrl/ListShortUrlsCommandTest.php | 2 +- module/Core/config/dependencies.config.php | 7 ++-- .../MultiSegmentSlugProcessor.php | 4 ++- module/Core/src/Domain/DomainService.php | 12 ++++--- .../ShortUrl/Helper/ShortUrlStringifier.php | 18 +++++----- module/Core/test/Action/QrCodeActionTest.php | 3 +- module/Core/test/Domain/DomainServiceTest.php | 6 +++- .../PublishingUpdatesGeneratorTest.php | 2 +- .../test/Matomo/MatomoVisitSenderTest.php | 3 +- .../Helper/ShortUrlStringifierTest.php | 5 +-- .../ShortUrlDataTransformerTest.php | 2 +- module/Rest/config/dependencies.config.php | 8 ++--- .../src/Action/Visit/DomainVisitsAction.php | 5 +-- .../DefaultShortCodesLengthMiddleware.php | 7 ++-- ...DropDefaultDomainFromRequestMiddleware.php | 7 ++-- .../ShortUrl/EditShortUrlActionTest.php | 2 +- .../ShortUrl/ListShortUrlsActionTest.php | 5 +-- .../ShortUrl/ResolveShortUrlActionTest.php | 2 +- .../Action/Visit/DomainVisitsActionTest.php | 6 +++- .../DefaultShortCodesLengthMiddlewareTest.php | 3 +- ...DefaultDomainFromRequestMiddlewareTest.php | 5 ++- 26 files changed, 83 insertions(+), 84 deletions(-) delete mode 100644 config/autoload/url-shortener.global.php diff --git a/config/autoload/routes.config.php b/config/autoload/routes.config.php index f7c6ce07..da3d1778 100644 --- a/config/autoload/routes.config.php +++ b/config/autoload/routes.config.php @@ -19,8 +19,6 @@ use function sprintf; return (static function (): array { $dropDomainMiddleware = Middleware\ShortUrl\DropDefaultDomainFromRequestMiddleware::class; $overrideDomainMiddleware = Middleware\ShortUrl\OverrideDomainMiddleware::class; - - // TODO This should be based on config, not the env var $shortUrlRouteSuffix = EnvVars::SHORT_URL_TRAILING_SLASH->loadFromEnv() ? '[/]' : ''; return [ diff --git a/config/autoload/url-shortener.global.php b/config/autoload/url-shortener.global.php deleted file mode 100644 index 98b07ea2..00000000 --- a/config/autoload/url-shortener.global.php +++ /dev/null @@ -1,34 +0,0 @@ -loadFromEnv(), - MIN_SHORT_CODES_LENGTH, - ); - $modeFromEnv = EnvVars::SHORT_URL_MODE->loadFromEnv(); - $mode = ShortUrlMode::tryFrom($modeFromEnv) ?? ShortUrlMode::STRICT; - - return [ - - 'url_shortener' => [ - 'domain' => [ // TODO Refactor this structure to url_shortener.schema and url_shortener.default_domain - 'schema' => ((bool) EnvVars::IS_HTTPS_ENABLED->loadFromEnv()) ? 'https' : 'http', - 'hostname' => EnvVars::DEFAULT_DOMAIN->loadFromEnv(), - ], - 'default_short_codes_length' => $shortCodesLength, - 'auto_resolve_titles' => (bool) EnvVars::AUTO_RESOLVE_TITLES->loadFromEnv(), - 'append_extra_path' => (bool) EnvVars::REDIRECT_APPEND_EXTRA_PATH->loadFromEnv(), - 'multi_segment_slugs_enabled' => (bool) EnvVars::MULTI_SEGMENT_SLUGS_ENABLED->loadFromEnv(), - 'trailing_slash_enabled' => (bool) EnvVars::SHORT_URL_TRAILING_SLASH->loadFromEnv(), - 'mode' => $mode, - ], - - ]; -})(); diff --git a/module/CLI/config/dependencies.config.php b/module/CLI/config/dependencies.config.php index fd26a5bc..2f098998 100644 --- a/module/CLI/config/dependencies.config.php +++ b/module/CLI/config/dependencies.config.php @@ -88,7 +88,7 @@ return [ TrackingOptions::class, ], Util\ProcessRunner::class => [SymfonyCli\Helper\ProcessHelper::class], - ApiKey\RoleResolver::class => [DomainService::class, 'config.url_shortener.domain.hostname'], + ApiKey\RoleResolver::class => [DomainService::class, UrlShortenerOptions::class], Command\ShortUrl\CreateShortUrlCommand::class => [ ShortUrl\UrlShortener::class, diff --git a/module/CLI/src/ApiKey/RoleResolver.php b/module/CLI/src/ApiKey/RoleResolver.php index 76787451..bfcf11a3 100644 --- a/module/CLI/src/ApiKey/RoleResolver.php +++ b/module/CLI/src/ApiKey/RoleResolver.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace Shlinkio\Shlink\CLI\ApiKey; use Shlinkio\Shlink\CLI\Exception\InvalidRoleConfigException; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Domain\DomainServiceInterface; use Shlinkio\Shlink\Rest\ApiKey\Model\RoleDefinition; use Shlinkio\Shlink\Rest\ApiKey\Role; @@ -12,11 +13,11 @@ use Symfony\Component\Console\Input\InputInterface; use function is_string; -class RoleResolver implements RoleResolverInterface +readonly class RoleResolver implements RoleResolverInterface { public function __construct( - private readonly DomainServiceInterface $domainService, - private readonly string $defaultDomain, + private DomainServiceInterface $domainService, + private UrlShortenerOptions $urlShortenerOptions, ) { } @@ -39,7 +40,7 @@ class RoleResolver implements RoleResolverInterface private function resolveRoleForAuthority(string $domainAuthority): RoleDefinition { - if ($domainAuthority === $this->defaultDomain) { + if ($domainAuthority === $this->urlShortenerOptions->defaultDomain()) { throw InvalidRoleConfigException::forDomainOnlyWithDefaultDomain(); } diff --git a/module/CLI/test/ApiKey/RoleResolverTest.php b/module/CLI/test/ApiKey/RoleResolverTest.php index cbd4f0fa..d1f696f5 100644 --- a/module/CLI/test/ApiKey/RoleResolverTest.php +++ b/module/CLI/test/ApiKey/RoleResolverTest.php @@ -10,6 +10,7 @@ use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Shlinkio\Shlink\CLI\ApiKey\RoleResolver; use Shlinkio\Shlink\CLI\Exception\InvalidRoleConfigException; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Domain\DomainServiceInterface; use Shlinkio\Shlink\Core\Domain\Entity\Domain; use Shlinkio\Shlink\Rest\ApiKey\Model\RoleDefinition; @@ -24,7 +25,10 @@ class RoleResolverTest extends TestCase protected function setUp(): void { $this->domainService = $this->createMock(DomainServiceInterface::class); - $this->resolver = new RoleResolver($this->domainService, 'default.com'); + $this->resolver = new RoleResolver( + $this->domainService, + new UrlShortenerOptions(domain: ['hostname' => 'default.com']), + ); } #[Test, DataProvider('provideRoles')] diff --git a/module/CLI/test/Command/ShortUrl/ListShortUrlsCommandTest.php b/module/CLI/test/Command/ShortUrl/ListShortUrlsCommandTest.php index 6016da1c..176800ab 100644 --- a/module/CLI/test/Command/ShortUrl/ListShortUrlsCommandTest.php +++ b/module/CLI/test/Command/ShortUrl/ListShortUrlsCommandTest.php @@ -37,7 +37,7 @@ class ListShortUrlsCommandTest extends TestCase { $this->shortUrlService = $this->createMock(ShortUrlListServiceInterface::class); $command = new ListShortUrlsCommand($this->shortUrlService, new ShortUrlDataTransformer( - new ShortUrlStringifier([]), + new ShortUrlStringifier(), )); $this->commandTester = CliTestUtils::testerForCommand($command); } diff --git a/module/Core/config/dependencies.config.php b/module/Core/config/dependencies.config.php index f50469c0..420d8a39 100644 --- a/module/Core/config/dependencies.config.php +++ b/module/Core/config/dependencies.config.php @@ -167,7 +167,7 @@ return [ ShortUrl\ShortUrlResolver::class, ], ShortUrl\Helper\ShortCodeUniquenessHelper::class => ['em', Config\Options\UrlShortenerOptions::class], - Domain\DomainService::class => ['em', 'config.url_shortener.domain.hostname'], + Domain\DomainService::class => ['em', Config\Options\UrlShortenerOptions::class], Util\DoctrineBatchHelper::class => ['em'], Util\RedirectResponseHelper::class => [Config\Options\RedirectOptions::class], @@ -197,7 +197,10 @@ return [ Config\Options\UrlShortenerOptions::class, Lock\LockFactory::class, ], - ShortUrl\Helper\ShortUrlStringifier::class => ['config.url_shortener.domain', 'config.router.base_path'], + ShortUrl\Helper\ShortUrlStringifier::class => [ + Config\Options\UrlShortenerOptions::class, + 'config.router.base_path', + ], ShortUrl\Helper\ShortUrlTitleResolutionHelper::class => [ 'httpClient', Config\Options\UrlShortenerOptions::class, diff --git a/module/Core/src/Config/PostProcessor/MultiSegmentSlugProcessor.php b/module/Core/src/Config/PostProcessor/MultiSegmentSlugProcessor.php index 585f78b6..c4078890 100644 --- a/module/Core/src/Config/PostProcessor/MultiSegmentSlugProcessor.php +++ b/module/Core/src/Config/PostProcessor/MultiSegmentSlugProcessor.php @@ -4,6 +4,8 @@ declare(strict_types=1); namespace Shlinkio\Shlink\Core\Config\PostProcessor; +use Shlinkio\Shlink\Core\Config\EnvVars; + use function array_map; use function str_replace; @@ -14,7 +16,7 @@ class MultiSegmentSlugProcessor public function __invoke(array $config): array { - $multiSegmentEnabled = $config['url_shortener']['multi_segment_slugs_enabled'] ?? false; + $multiSegmentEnabled = (bool) EnvVars::MULTI_SEGMENT_SLUGS_ENABLED->loadFromEnv(); if (! $multiSegmentEnabled) { return $config; } diff --git a/module/Core/src/Domain/DomainService.php b/module/Core/src/Domain/DomainService.php index 93adbf5f..382ca08e 100644 --- a/module/Core/src/Domain/DomainService.php +++ b/module/Core/src/Domain/DomainService.php @@ -7,6 +7,7 @@ namespace Shlinkio\Shlink\Core\Domain; use Doctrine\ORM\EntityManagerInterface; use Shlinkio\Shlink\Core\Config\EmptyNotFoundRedirectConfig; use Shlinkio\Shlink\Core\Config\NotFoundRedirects; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Domain\Entity\Domain; use Shlinkio\Shlink\Core\Domain\Model\DomainItem; use Shlinkio\Shlink\Core\Domain\Repository\DomainRepositoryInterface; @@ -16,9 +17,9 @@ use Shlinkio\Shlink\Rest\Entity\ApiKey; use function array_map; -class DomainService implements DomainServiceInterface +readonly class DomainService implements DomainServiceInterface { - public function __construct(private readonly EntityManagerInterface $em, private readonly string $defaultDomain) + public function __construct(private EntityManagerInterface $em, private UrlShortenerOptions $urlShortenerOptions) { } @@ -35,7 +36,10 @@ class DomainService implements DomainServiceInterface } return [ - DomainItem::forDefaultDomain($this->defaultDomain, $default ?? new EmptyNotFoundRedirectConfig()), + DomainItem::forDefaultDomain( + $this->urlShortenerOptions->defaultDomain(), + $default ?? new EmptyNotFoundRedirectConfig(), + ), ...$mappedDomains, ]; } @@ -52,7 +56,7 @@ class DomainService implements DomainServiceInterface $restOfDomains = []; foreach ($allDomains as $domain) { - if ($domain->authority === $this->defaultDomain) { + if ($domain->authority === $this->urlShortenerOptions->defaultDomain()) { $defaultDomain = $domain; } else { $restOfDomains[] = $domain; diff --git a/module/Core/src/ShortUrl/Helper/ShortUrlStringifier.php b/module/Core/src/ShortUrl/Helper/ShortUrlStringifier.php index 886a4d25..540b444c 100644 --- a/module/Core/src/ShortUrl/Helper/ShortUrlStringifier.php +++ b/module/Core/src/ShortUrl/Helper/ShortUrlStringifier.php @@ -5,22 +5,23 @@ declare(strict_types=1); namespace Shlinkio\Shlink\Core\ShortUrl\Helper; use Laminas\Diactoros\Uri; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use function sprintf; -class ShortUrlStringifier implements ShortUrlStringifierInterface +readonly class ShortUrlStringifier implements ShortUrlStringifierInterface { - /** - * @param array{schema?: string, hostname?: string} $domainConfig - */ - public function __construct(private readonly array $domainConfig, private readonly string $basePath = '') - { + public function __construct( + private UrlShortenerOptions $urlShortenerOptions = new UrlShortenerOptions(), + private string $basePath = '', + ) { } public function stringify(ShortUrl $shortUrl): string { - $uriWithoutShortCode = (new Uri())->withScheme($this->domainConfig['schema'] ?? 'http') + $domainConfig = $this->urlShortenerOptions->domain; + $uriWithoutShortCode = (new Uri())->withScheme($domainConfig['schema'] ?? 'http') ->withHost($this->resolveDomain($shortUrl)) ->withPath($this->basePath) ->__toString(); @@ -31,6 +32,7 @@ class ShortUrlStringifier implements ShortUrlStringifierInterface private function resolveDomain(ShortUrl $shortUrl): string { - return $shortUrl->getDomain()?->authority ?? $this->domainConfig['hostname'] ?? ''; + $domainConfig = $this->urlShortenerOptions->domain; + return $shortUrl->getDomain()?->authority ?? $domainConfig['hostname'] ?? ''; } } diff --git a/module/Core/test/Action/QrCodeActionTest.php b/module/Core/test/Action/QrCodeActionTest.php index 9afae793..d040b7d7 100644 --- a/module/Core/test/Action/QrCodeActionTest.php +++ b/module/Core/test/Action/QrCodeActionTest.php @@ -17,6 +17,7 @@ use Psr\Log\NullLogger; use Shlinkio\Shlink\Common\Response\QrCodeResponse; use Shlinkio\Shlink\Core\Action\QrCodeAction; use Shlinkio\Shlink\Core\Config\Options\QrCodeOptions; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Exception\ShortUrlNotFoundException; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifier; @@ -324,7 +325,7 @@ class QrCodeActionTest extends TestCase { return new QrCodeAction( $this->urlResolver, - new ShortUrlStringifier(['domain' => 's.test']), + new ShortUrlStringifier(), new NullLogger(), $options ?? new QrCodeOptions(enabledForDisabledShortUrls: false), ); diff --git a/module/Core/test/Domain/DomainServiceTest.php b/module/Core/test/Domain/DomainServiceTest.php index c67597ec..82a25654 100644 --- a/module/Core/test/Domain/DomainServiceTest.php +++ b/module/Core/test/Domain/DomainServiceTest.php @@ -11,6 +11,7 @@ use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Shlinkio\Shlink\Core\Config\EmptyNotFoundRedirectConfig; use Shlinkio\Shlink\Core\Config\NotFoundRedirects; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Domain\DomainService; use Shlinkio\Shlink\Core\Domain\Entity\Domain; use Shlinkio\Shlink\Core\Domain\Model\DomainItem; @@ -28,7 +29,10 @@ class DomainServiceTest extends TestCase protected function setUp(): void { $this->em = $this->createMock(EntityManagerInterface::class); - $this->domainService = new DomainService($this->em, 'default.com'); + $this->domainService = new DomainService( + $this->em, + new UrlShortenerOptions(domain: ['hostname' => 'default.com']), + ); } #[Test, DataProvider('provideExcludedDomains')] diff --git a/module/Core/test/EventDispatcher/PublishingUpdatesGeneratorTest.php b/module/Core/test/EventDispatcher/PublishingUpdatesGeneratorTest.php index bd3c82c8..1ea76bf6 100644 --- a/module/Core/test/EventDispatcher/PublishingUpdatesGeneratorTest.php +++ b/module/Core/test/EventDispatcher/PublishingUpdatesGeneratorTest.php @@ -31,7 +31,7 @@ class PublishingUpdatesGeneratorTest extends TestCase Chronos::setTestNow($this->now); $this->generator = new PublishingUpdatesGenerator( - new ShortUrlDataTransformer(new ShortUrlStringifier([])), + new ShortUrlDataTransformer(new ShortUrlStringifier()), ); } diff --git a/module/Core/test/Matomo/MatomoVisitSenderTest.php b/module/Core/test/Matomo/MatomoVisitSenderTest.php index 816c8eea..1794973b 100644 --- a/module/Core/test/Matomo/MatomoVisitSenderTest.php +++ b/module/Core/test/Matomo/MatomoVisitSenderTest.php @@ -11,6 +11,7 @@ use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Shlinkio\Shlink\Common\Util\DateRange; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Matomo\MatomoTrackerBuilderInterface; use Shlinkio\Shlink\Core\Matomo\MatomoVisitSender; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; @@ -36,7 +37,7 @@ class MatomoVisitSenderTest extends TestCase $this->visitSender = new MatomoVisitSender( $this->trackerBuilder, - new ShortUrlStringifier(['hostname' => 's2.test']), + new ShortUrlStringifier(new UrlShortenerOptions(domain: ['hostname' => 's2.test'])), $this->visitIterationRepository, ); } diff --git a/module/Core/test/ShortUrl/Helper/ShortUrlStringifierTest.php b/module/Core/test/ShortUrl/Helper/ShortUrlStringifierTest.php index e74d6182..42379531 100644 --- a/module/Core/test/ShortUrl/Helper/ShortUrlStringifierTest.php +++ b/module/Core/test/ShortUrl/Helper/ShortUrlStringifierTest.php @@ -7,6 +7,7 @@ namespace ShlinkioTest\Shlink\Core\ShortUrl\Helper; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifier; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlCreation; @@ -15,12 +16,12 @@ class ShortUrlStringifierTest extends TestCase { #[Test, DataProvider('provideConfigAndShortUrls')] public function generatesExpectedOutputBasedOnConfigAndShortUrl( - array $config, + array $domainConfig, string $basePath, ShortUrl $shortUrl, string $expected, ): void { - $stringifier = new ShortUrlStringifier($config, $basePath); + $stringifier = new ShortUrlStringifier(new UrlShortenerOptions($domainConfig), $basePath); self::assertEquals($expected, $stringifier->stringify($shortUrl)); } diff --git a/module/Core/test/ShortUrl/Transformer/ShortUrlDataTransformerTest.php b/module/Core/test/ShortUrl/Transformer/ShortUrlDataTransformerTest.php index 349392db..9ff01475 100644 --- a/module/Core/test/ShortUrl/Transformer/ShortUrlDataTransformerTest.php +++ b/module/Core/test/ShortUrl/Transformer/ShortUrlDataTransformerTest.php @@ -21,7 +21,7 @@ class ShortUrlDataTransformerTest extends TestCase protected function setUp(): void { - $this->transformer = new ShortUrlDataTransformer(new ShortUrlStringifier([])); + $this->transformer = new ShortUrlDataTransformer(new ShortUrlStringifier()); } #[Test, DataProvider('provideShortUrls')] diff --git a/module/Rest/config/dependencies.config.php b/module/Rest/config/dependencies.config.php index 3d7a66a4..b69cf36d 100644 --- a/module/Rest/config/dependencies.config.php +++ b/module/Rest/config/dependencies.config.php @@ -86,7 +86,7 @@ return [ Action\Visit\TagVisitsAction::class => [Visit\VisitsStatsHelper::class], Action\Visit\DomainVisitsAction::class => [ Visit\VisitsStatsHelper::class, - 'config.url_shortener.domain.hostname', + Config\Options\UrlShortenerOptions::class, ], Action\Visit\GlobalVisitsAction::class => [Visit\VisitsStatsHelper::class], Action\Visit\OrphanVisitsAction::class => [Visit\VisitsStatsHelper::class], @@ -113,10 +113,10 @@ return [ ], Middleware\CrossDomainMiddleware::class => ['config.cors'], - Middleware\ShortUrl\DropDefaultDomainFromRequestMiddleware::class => ['config.url_shortener.domain.hostname'], - Middleware\ShortUrl\DefaultShortCodesLengthMiddleware::class => [ - 'config.url_shortener.default_short_codes_length', + Middleware\ShortUrl\DropDefaultDomainFromRequestMiddleware::class => [ + Config\Options\UrlShortenerOptions::class, ], + Middleware\ShortUrl\DefaultShortCodesLengthMiddleware::class => [Config\Options\UrlShortenerOptions::class], Middleware\ShortUrl\OverrideDomainMiddleware::class => [DomainService::class], Middleware\Mercure\NotConfiguredMercureErrorHandler::class => [ ProblemDetailsResponseFactory::class, diff --git a/module/Rest/src/Action/Visit/DomainVisitsAction.php b/module/Rest/src/Action/Visit/DomainVisitsAction.php index 91f071b5..31b8add4 100644 --- a/module/Rest/src/Action/Visit/DomainVisitsAction.php +++ b/module/Rest/src/Action/Visit/DomainVisitsAction.php @@ -8,6 +8,7 @@ use Laminas\Diactoros\Response\JsonResponse; use Psr\Http\Message\ResponseInterface as Response; use Psr\Http\Message\ServerRequestInterface as Request; use Shlinkio\Shlink\Common\Paginator\Util\PagerfantaUtils; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Visit\Model\VisitsParams; use Shlinkio\Shlink\Core\Visit\VisitsStatsHelperInterface; use Shlinkio\Shlink\Rest\Action\AbstractRestAction; @@ -20,7 +21,7 @@ class DomainVisitsAction extends AbstractRestAction public function __construct( private readonly VisitsStatsHelperInterface $visitsHelper, - private readonly string $defaultDomain, + private readonly UrlShortenerOptions $urlShortenerOptions, ) { } @@ -37,7 +38,7 @@ class DomainVisitsAction extends AbstractRestAction private function resolveDomainParam(Request $request): string { $domainParam = $request->getAttribute('domain', ''); - if ($domainParam === $this->defaultDomain) { + if ($domainParam === $this->urlShortenerOptions->defaultDomain()) { return 'DEFAULT'; } diff --git a/module/Rest/src/Middleware/ShortUrl/DefaultShortCodesLengthMiddleware.php b/module/Rest/src/Middleware/ShortUrl/DefaultShortCodesLengthMiddleware.php index 4b7779c2..0d7d947e 100644 --- a/module/Rest/src/Middleware/ShortUrl/DefaultShortCodesLengthMiddleware.php +++ b/module/Rest/src/Middleware/ShortUrl/DefaultShortCodesLengthMiddleware.php @@ -8,11 +8,12 @@ use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Model\Validation\ShortUrlInputFilter; -class DefaultShortCodesLengthMiddleware implements MiddlewareInterface +readonly class DefaultShortCodesLengthMiddleware implements MiddlewareInterface { - public function __construct(private int $defaultShortCodesLength) + public function __construct(private UrlShortenerOptions $urlShortenerOptions) { } @@ -21,7 +22,7 @@ class DefaultShortCodesLengthMiddleware implements MiddlewareInterface /** @var array $body */ $body = $request->getParsedBody(); if (! isset($body[ShortUrlInputFilter::SHORT_CODE_LENGTH])) { - $body[ShortUrlInputFilter::SHORT_CODE_LENGTH] = $this->defaultShortCodesLength; + $body[ShortUrlInputFilter::SHORT_CODE_LENGTH] = $this->urlShortenerOptions->defaultShortCodesLength; } return $handler->handle($request->withParsedBody($body)); diff --git a/module/Rest/src/Middleware/ShortUrl/DropDefaultDomainFromRequestMiddleware.php b/module/Rest/src/Middleware/ShortUrl/DropDefaultDomainFromRequestMiddleware.php index 73a6ac69..f50341d2 100644 --- a/module/Rest/src/Middleware/ShortUrl/DropDefaultDomainFromRequestMiddleware.php +++ b/module/Rest/src/Middleware/ShortUrl/DropDefaultDomainFromRequestMiddleware.php @@ -8,10 +8,11 @@ use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\MiddlewareInterface; use Psr\Http\Server\RequestHandlerInterface; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; -class DropDefaultDomainFromRequestMiddleware implements MiddlewareInterface +readonly class DropDefaultDomainFromRequestMiddleware implements MiddlewareInterface { - public function __construct(private readonly string $defaultDomain) + public function __construct(private UrlShortenerOptions $urlShortenerOptions) { } @@ -27,7 +28,7 @@ class DropDefaultDomainFromRequestMiddleware implements MiddlewareInterface private function sanitizeDomainFromPayload(array $payload): array { - if (isset($payload['domain']) && $payload['domain'] === $this->defaultDomain) { + if (isset($payload['domain']) && $payload['domain'] === $this->urlShortenerOptions->defaultDomain()) { unset($payload['domain']); } diff --git a/module/Rest/test/Action/ShortUrl/EditShortUrlActionTest.php b/module/Rest/test/Action/ShortUrl/EditShortUrlActionTest.php index dd7a0e14..669037d1 100644 --- a/module/Rest/test/Action/ShortUrl/EditShortUrlActionTest.php +++ b/module/Rest/test/Action/ShortUrl/EditShortUrlActionTest.php @@ -25,7 +25,7 @@ class EditShortUrlActionTest extends TestCase { $this->shortUrlService = $this->createMock(ShortUrlServiceInterface::class); $this->action = new EditShortUrlAction($this->shortUrlService, new ShortUrlDataTransformer( - new ShortUrlStringifier([]), + new ShortUrlStringifier(), )); } diff --git a/module/Rest/test/Action/ShortUrl/ListShortUrlsActionTest.php b/module/Rest/test/Action/ShortUrl/ListShortUrlsActionTest.php index d372d692..071fb844 100644 --- a/module/Rest/test/Action/ShortUrl/ListShortUrlsActionTest.php +++ b/module/Rest/test/Action/ShortUrl/ListShortUrlsActionTest.php @@ -13,6 +13,7 @@ use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Shlinkio\Shlink\Common\Paginator\Paginator; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifier; use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlsParams; use Shlinkio\Shlink\Core\ShortUrl\ShortUrlListServiceInterface; @@ -30,10 +31,10 @@ class ListShortUrlsActionTest extends TestCase $this->service = $this->createMock(ShortUrlListServiceInterface::class); $this->action = new ListShortUrlsAction($this->service, new ShortUrlDataTransformer( - new ShortUrlStringifier([ + new ShortUrlStringifier(new UrlShortenerOptions(domain: [ 'hostname' => 's.test', 'schema' => 'https', - ]), + ])), )); } diff --git a/module/Rest/test/Action/ShortUrl/ResolveShortUrlActionTest.php b/module/Rest/test/Action/ShortUrl/ResolveShortUrlActionTest.php index 0576fd52..13b9e35d 100644 --- a/module/Rest/test/Action/ShortUrl/ResolveShortUrlActionTest.php +++ b/module/Rest/test/Action/ShortUrl/ResolveShortUrlActionTest.php @@ -25,7 +25,7 @@ class ResolveShortUrlActionTest extends TestCase { $this->urlResolver = $this->createMock(ShortUrlResolverInterface::class); $this->action = new ResolveShortUrlAction($this->urlResolver, new ShortUrlDataTransformer( - new ShortUrlStringifier([]), + new ShortUrlStringifier(), )); } diff --git a/module/Rest/test/Action/Visit/DomainVisitsActionTest.php b/module/Rest/test/Action/Visit/DomainVisitsActionTest.php index a6d64bd2..3fb4c498 100644 --- a/module/Rest/test/Action/Visit/DomainVisitsActionTest.php +++ b/module/Rest/test/Action/Visit/DomainVisitsActionTest.php @@ -11,6 +11,7 @@ use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Shlinkio\Shlink\Common\Paginator\Paginator; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Visit\Model\VisitsParams; use Shlinkio\Shlink\Core\Visit\VisitsStatsHelperInterface; use Shlinkio\Shlink\Rest\Action\Visit\DomainVisitsAction; @@ -24,7 +25,10 @@ class DomainVisitsActionTest extends TestCase protected function setUp(): void { $this->visitsHelper = $this->createMock(VisitsStatsHelperInterface::class); - $this->action = new DomainVisitsAction($this->visitsHelper, 'the_default.com'); + $this->action = new DomainVisitsAction( + $this->visitsHelper, + new UrlShortenerOptions(domain: ['hostname' => 'the_default.com']), + ); } #[Test, DataProvider('provideDomainAuthorities')] diff --git a/module/Rest/test/Middleware/ShortUrl/DefaultShortCodesLengthMiddlewareTest.php b/module/Rest/test/Middleware/ShortUrl/DefaultShortCodesLengthMiddlewareTest.php index e6b0c6ec..db579451 100644 --- a/module/Rest/test/Middleware/ShortUrl/DefaultShortCodesLengthMiddlewareTest.php +++ b/module/Rest/test/Middleware/ShortUrl/DefaultShortCodesLengthMiddlewareTest.php @@ -13,6 +13,7 @@ use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\ShortUrl\Model\Validation\ShortUrlInputFilter; use Shlinkio\Shlink\Rest\Middleware\ShortUrl\DefaultShortCodesLengthMiddleware; @@ -24,7 +25,7 @@ class DefaultShortCodesLengthMiddlewareTest extends TestCase protected function setUp(): void { $this->handler = $this->createMock(RequestHandlerInterface::class); - $this->middleware = new DefaultShortCodesLengthMiddleware(8); + $this->middleware = new DefaultShortCodesLengthMiddleware(new UrlShortenerOptions(defaultShortCodesLength: 8)); } #[Test, DataProvider('provideBodies')] diff --git a/module/Rest/test/Middleware/ShortUrl/DropDefaultDomainFromRequestMiddlewareTest.php b/module/Rest/test/Middleware/ShortUrl/DropDefaultDomainFromRequestMiddlewareTest.php index e9fceea5..530e28ca 100644 --- a/module/Rest/test/Middleware/ShortUrl/DropDefaultDomainFromRequestMiddlewareTest.php +++ b/module/Rest/test/Middleware/ShortUrl/DropDefaultDomainFromRequestMiddlewareTest.php @@ -13,6 +13,7 @@ use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Psr\Http\Message\ServerRequestInterface; use Psr\Http\Server\RequestHandlerInterface; +use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Rest\Middleware\ShortUrl\DropDefaultDomainFromRequestMiddleware; class DropDefaultDomainFromRequestMiddlewareTest extends TestCase @@ -23,7 +24,9 @@ class DropDefaultDomainFromRequestMiddlewareTest extends TestCase protected function setUp(): void { $this->next = $this->createMock(RequestHandlerInterface::class); - $this->middleware = new DropDefaultDomainFromRequestMiddleware('s.test'); + $this->middleware = new DropDefaultDomainFromRequestMiddleware( + new UrlShortenerOptions(domain: ['hostname' => 's.test']), + ); } #[Test, DataProvider('provideQueryParams')] From c0200317ddb9e55722377c1802a3e13e6a2b2b83 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Tue, 22 Oct 2024 15:31:47 +0200 Subject: [PATCH 08/25] Load dev env vars via roadrunner instead of docker compose --- data/infra/roadrunner.Dockerfile | 2 +- docker-compose.yml | 3 -- .../MultiSegmentSlugProcessorTest.php | 40 ++++++++++--------- 3 files changed, 22 insertions(+), 23 deletions(-) diff --git a/data/infra/roadrunner.Dockerfile b/data/infra/roadrunner.Dockerfile index 2cb660d2..8c412350 100644 --- a/data/infra/roadrunner.Dockerfile +++ b/data/infra/roadrunner.Dockerfile @@ -74,4 +74,4 @@ CMD \ # Download roadrunner binary if [[ ! -f "./bin/rr" ]]; then ./vendor/bin/rr get --no-interaction --no-config --location bin/ && chmod +x bin/rr ; fi && \ # Run with `exec` so that signals are properly handled - exec ./bin/rr serve -c config/roadrunner/.rr.dev.yml + exec ./bin/rr serve --dotenv /home/shlink/shlink-dev.env -c config/roadrunner/.rr.dev.yml diff --git a/docker-compose.yml b/docker-compose.yml index b9fbd210..2052b1cf 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -63,9 +63,6 @@ services: - shlink_matomo environment: DEFAULT_DOMAIN: localhost:8800 - env_file: - - path: shlink-dev.env - required: false extra_hosts: - 'host.docker.internal:host-gateway' diff --git a/module/Core/test/Config/PostProcessor/MultiSegmentSlugProcessorTest.php b/module/Core/test/Config/PostProcessor/MultiSegmentSlugProcessorTest.php index d811a2e2..487228ec 100644 --- a/module/Core/test/Config/PostProcessor/MultiSegmentSlugProcessorTest.php +++ b/module/Core/test/Config/PostProcessor/MultiSegmentSlugProcessorTest.php @@ -7,8 +7,11 @@ namespace ShlinkioTest\Shlink\Core\Config\PostProcessor; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; use PHPUnit\Framework\TestCase; +use Shlinkio\Shlink\Core\Config\EnvVars; use Shlinkio\Shlink\Core\Config\PostProcessor\MultiSegmentSlugProcessor; +use function putenv; + class MultiSegmentSlugProcessorTest extends TestCase { private MultiSegmentSlugProcessor $processor; @@ -18,36 +21,35 @@ class MultiSegmentSlugProcessorTest extends TestCase $this->processor = new MultiSegmentSlugProcessor(); } - #[Test, DataProvider('provideConfigs')] - public function parsesRoutesAsExpected(array $config, array $expectedRoutes): void + protected function tearDown(): void { - self::assertEquals($expectedRoutes, ($this->processor)($config)['routes'] ?? []); + putenv(EnvVars::MULTI_SEGMENT_SLUGS_ENABLED->value); + } + + #[Test, DataProvider('provideConfigs')] + public function parsesRoutesAsExpected(bool $multiSegmentEnabled, array $routes, array $expectedRoutes): void + { + putenv(EnvVars::MULTI_SEGMENT_SLUGS_ENABLED->value . '=' . ($multiSegmentEnabled ? 'true' : 'false')); + self::assertEquals($expectedRoutes, ($this->processor)(['routes' => $routes])['routes'] ?? []); } public static function provideConfigs(): iterable { - yield [[], []]; - yield [['url_shortener' => []], []]; - yield [['url_shortener' => ['multi_segment_slugs_enabled' => false]], []]; yield [ - [ - 'url_shortener' => ['multi_segment_slugs_enabled' => false], - 'routes' => $routes = [ - ['path' => '/foo'], - ['path' => '/bar/{shortCode}'], - ['path' => '/baz/{shortCode}/foo'], - ], + false, + $routes = [ + ['path' => '/foo'], + ['path' => '/bar/{shortCode}'], + ['path' => '/baz/{shortCode}/foo'], ], $routes, ]; yield [ + true, [ - 'url_shortener' => ['multi_segment_slugs_enabled' => true], - 'routes' => [ - ['path' => '/foo'], - ['path' => '/bar/{shortCode}'], - ['path' => '/baz/{shortCode}/foo'], - ], + ['path' => '/foo'], + ['path' => '/bar/{shortCode}'], + ['path' => '/baz/{shortCode}/foo'], ], [ ['path' => '/foo'], From 46601443f5b4d6a24cf3a2ab4a45d4ec0a4f6d67 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Wed, 23 Oct 2024 09:16:52 +0200 Subject: [PATCH 09/25] Load specific env file when running API tests --- .dockerignore | 1 + bin/test/run-api-tests.sh | 4 ++ config/test/shlink-test.env | 8 ++++ config/test/test_config.global.php | 14 ------- data/infra/roadrunner.Dockerfile | 2 + docker-compose.yml | 2 - module/CLI/src/ApiKey/RoleResolver.php | 2 +- module/CLI/test/ApiKey/RoleResolverTest.php | 5 +-- .../ShortUrl/CreateShortUrlCommandTest.php | 5 +-- .../Config/Options/UrlShortenerOptions.php | 16 +++----- module/Core/src/Domain/DomainService.php | 4 +- .../ShortUrl/Helper/ShortUrlStringifier.php | 6 +-- .../PersistenceShortUrlRelationResolver.php | 2 +- .../Core/src/ShortUrl/ShortUrlListService.php | 2 +- module/Core/test/Action/QrCodeActionTest.php | 1 - module/Core/test/Domain/DomainServiceTest.php | 5 +-- .../test/Matomo/MatomoVisitSenderTest.php | 2 +- .../Helper/ShortUrlStringifierTest.php | 38 ++++++++++--------- ...ersistenceShortUrlRelationResolverTest.php | 4 +- .../src/Action/Visit/DomainVisitsAction.php | 2 +- ...DropDefaultDomainFromRequestMiddleware.php | 2 +- .../ShortUrl/ListShortUrlsActionTest.php | 5 +-- .../Action/Visit/DomainVisitsActionTest.php | 5 +-- ...DefaultDomainFromRequestMiddlewareTest.php | 4 +- shlink-dev.env.dist | 1 + 25 files changed, 59 insertions(+), 83 deletions(-) create mode 100644 config/test/shlink-test.env diff --git a/.dockerignore b/.dockerignore index e3aff686..db2d21f0 100644 --- a/.dockerignore +++ b/.dockerignore @@ -23,3 +23,4 @@ php*xml* build* **/.* !config/roadrunner/.rr.yml +*.env* diff --git a/bin/test/run-api-tests.sh b/bin/test/run-api-tests.sh index ffc152b7..4c519a9b 100755 --- a/bin/test/run-api-tests.sh +++ b/bin/test/run-api-tests.sh @@ -17,8 +17,12 @@ touch $OUTPUT_LOGS # Try to stop server just in case it hanged in last execution [ "$TEST_RUNTIME" = 'rr' ] && bin/rr stop -f -w . +# Resolve .env file absolute path, as it fails to load with relative paths +TESTS_DOTENV="${PWD}/config/test/shlink-test.env" + echo 'Starting server...' [ "$TEST_RUNTIME" = 'rr' ] && bin/rr serve -p -w . -c=config/roadrunner/.rr.test.yml \ + --dotenv "$TESTS_DOTENV" \ -o=logs.output="${PWD}/${OUTPUT_LOGS}" \ -o=logs.channels.http.output="${PWD}/${OUTPUT_LOGS}" \ -o=logs.channels.server.output="${PWD}/${OUTPUT_LOGS}" & diff --git a/config/test/shlink-test.env b/config/test/shlink-test.env new file mode 100644 index 00000000..d0eac32a --- /dev/null +++ b/config/test/shlink-test.env @@ -0,0 +1,8 @@ +# URL shortener +DEFAULT_DOMAIN=s.test +IS_HTTPS_ENABLED=false + +# Disable mercure integration during E2E tests +MERCURE_PUBLIC_HUB_URL=null +MERCURE_INTERNAL_HUB_URL=null +MERCURE_JWT_SECRET=null diff --git a/config/test/test_config.global.php b/config/test/test_config.global.php index aad5e9d0..bd6771e6 100644 --- a/config/test/test_config.global.php +++ b/config/test/test_config.global.php @@ -93,13 +93,6 @@ return [ ConfigAggregator::ENABLE_CACHE => false, FastRouteRouter::CONFIG_CACHE_ENABLED => false, - 'url_shortener' => [ - 'domain' => [ - 'schema' => 'http', - 'hostname' => 's.test', - ], - ], - 'routes' => [ // This route is used to test that title resolution is skipped if the long URL times out [ @@ -120,13 +113,6 @@ return [ ], ], - // Disable mercure integration during E2E tests - 'mercure' => [ - 'public_hub_url' => null, - 'internal_hub_url' => null, - 'jwt_secret' => null, - ], - 'dependencies' => [ 'services' => [ 'shlink_test_api_client' => new Client([ diff --git a/data/infra/roadrunner.Dockerfile b/data/infra/roadrunner.Dockerfile index 8c412350..28d2d343 100644 --- a/data/infra/roadrunner.Dockerfile +++ b/data/infra/roadrunner.Dockerfile @@ -73,5 +73,7 @@ CMD \ if [[ ! -d "./vendor" ]]; then /usr/local/bin/composer install ; fi && \ # Download roadrunner binary if [[ ! -f "./bin/rr" ]]; then ./vendor/bin/rr get --no-interaction --no-config --location bin/ && chmod +x bin/rr ; fi && \ + # Create .env file if it does not exist yet + if [[ ! -f "./shlink-dev.env" ]]; then cp ./shlink-dev.env.dist ./shlink-dev.env ; fi && \ # Run with `exec` so that signals are properly handled exec ./bin/rr serve --dotenv /home/shlink/shlink-dev.env -c config/roadrunner/.rr.dev.yml diff --git a/docker-compose.yml b/docker-compose.yml index 2052b1cf..05132dfb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -61,8 +61,6 @@ services: - shlink_mercure_proxy - shlink_rabbitmq - shlink_matomo - environment: - DEFAULT_DOMAIN: localhost:8800 extra_hosts: - 'host.docker.internal:host-gateway' diff --git a/module/CLI/src/ApiKey/RoleResolver.php b/module/CLI/src/ApiKey/RoleResolver.php index bfcf11a3..ece56c77 100644 --- a/module/CLI/src/ApiKey/RoleResolver.php +++ b/module/CLI/src/ApiKey/RoleResolver.php @@ -40,7 +40,7 @@ readonly class RoleResolver implements RoleResolverInterface private function resolveRoleForAuthority(string $domainAuthority): RoleDefinition { - if ($domainAuthority === $this->urlShortenerOptions->defaultDomain()) { + if ($domainAuthority === $this->urlShortenerOptions->defaultDomain) { throw InvalidRoleConfigException::forDomainOnlyWithDefaultDomain(); } diff --git a/module/CLI/test/ApiKey/RoleResolverTest.php b/module/CLI/test/ApiKey/RoleResolverTest.php index d1f696f5..184780d7 100644 --- a/module/CLI/test/ApiKey/RoleResolverTest.php +++ b/module/CLI/test/ApiKey/RoleResolverTest.php @@ -25,10 +25,7 @@ class RoleResolverTest extends TestCase protected function setUp(): void { $this->domainService = $this->createMock(DomainServiceInterface::class); - $this->resolver = new RoleResolver( - $this->domainService, - new UrlShortenerOptions(domain: ['hostname' => 'default.com']), - ); + $this->resolver = new RoleResolver($this->domainService, new UrlShortenerOptions('default.com')); } #[Test, DataProvider('provideRoles')] diff --git a/module/CLI/test/Command/ShortUrl/CreateShortUrlCommandTest.php b/module/CLI/test/Command/ShortUrl/CreateShortUrlCommandTest.php index 86bd87fb..19c57481 100644 --- a/module/CLI/test/Command/ShortUrl/CreateShortUrlCommandTest.php +++ b/module/CLI/test/Command/ShortUrl/CreateShortUrlCommandTest.php @@ -37,10 +37,7 @@ class CreateShortUrlCommandTest extends TestCase $command = new CreateShortUrlCommand( $this->urlShortener, $this->stringifier, - new UrlShortenerOptions( - domain: ['hostname' => 'example.com', 'schema' => ''], - defaultShortCodesLength: 5, - ), + new UrlShortenerOptions(defaultDomain: 'example.com', defaultShortCodesLength: 5), ); $this->commandTester = CliTestUtils::testerForCommand($command); } diff --git a/module/Core/src/Config/Options/UrlShortenerOptions.php b/module/Core/src/Config/Options/UrlShortenerOptions.php index b815ac0f..98b450b4 100644 --- a/module/Core/src/Config/Options/UrlShortenerOptions.php +++ b/module/Core/src/Config/Options/UrlShortenerOptions.php @@ -15,10 +15,11 @@ use const Shlinkio\Shlink\MIN_SHORT_CODES_LENGTH; final readonly class UrlShortenerOptions { /** - * @param array{schema: ?string, hostname: ?string} $domain + * @param 'http'|'https' $schema */ public function __construct( - public array $domain = ['schema' => null, 'hostname' => null], + public string $defaultDomain = '', + public string $schema = 'http', public int $defaultShortCodesLength = DEFAULT_SHORT_CODES_LENGTH, public bool $autoResolveTitles = false, public bool $appendExtraPath = false, @@ -37,10 +38,8 @@ final readonly class UrlShortenerOptions $mode = EnvVars::SHORT_URL_MODE->loadFromEnv(); return new self( - domain: [ - 'schema' => ((bool) EnvVars::IS_HTTPS_ENABLED->loadFromEnv()) ? 'https' : 'http', - 'hostname' => EnvVars::DEFAULT_DOMAIN->loadFromEnv(), - ], + defaultDomain: EnvVars::DEFAULT_DOMAIN->loadFromEnv(), + schema: ((bool) EnvVars::IS_HTTPS_ENABLED->loadFromEnv()) ? 'https' : 'http', defaultShortCodesLength: $shortCodesLength, autoResolveTitles: (bool) EnvVars::AUTO_RESOLVE_TITLES->loadFromEnv(), appendExtraPath: (bool) EnvVars::REDIRECT_APPEND_EXTRA_PATH->loadFromEnv(), @@ -54,9 +53,4 @@ final readonly class UrlShortenerOptions { return $this->mode === ShortUrlMode::LOOSE; } - - public function defaultDomain(): string - { - return $this->domain['hostname'] ?? ''; - } } diff --git a/module/Core/src/Domain/DomainService.php b/module/Core/src/Domain/DomainService.php index 382ca08e..e514af55 100644 --- a/module/Core/src/Domain/DomainService.php +++ b/module/Core/src/Domain/DomainService.php @@ -37,7 +37,7 @@ readonly class DomainService implements DomainServiceInterface return [ DomainItem::forDefaultDomain( - $this->urlShortenerOptions->defaultDomain(), + $this->urlShortenerOptions->defaultDomain, $default ?? new EmptyNotFoundRedirectConfig(), ), ...$mappedDomains, @@ -56,7 +56,7 @@ readonly class DomainService implements DomainServiceInterface $restOfDomains = []; foreach ($allDomains as $domain) { - if ($domain->authority === $this->urlShortenerOptions->defaultDomain()) { + if ($domain->authority === $this->urlShortenerOptions->defaultDomain) { $defaultDomain = $domain; } else { $restOfDomains[] = $domain; diff --git a/module/Core/src/ShortUrl/Helper/ShortUrlStringifier.php b/module/Core/src/ShortUrl/Helper/ShortUrlStringifier.php index 540b444c..6659bc0c 100644 --- a/module/Core/src/ShortUrl/Helper/ShortUrlStringifier.php +++ b/module/Core/src/ShortUrl/Helper/ShortUrlStringifier.php @@ -20,8 +20,7 @@ readonly class ShortUrlStringifier implements ShortUrlStringifierInterface public function stringify(ShortUrl $shortUrl): string { - $domainConfig = $this->urlShortenerOptions->domain; - $uriWithoutShortCode = (new Uri())->withScheme($domainConfig['schema'] ?? 'http') + $uriWithoutShortCode = (new Uri())->withScheme($this->urlShortenerOptions->schema) ->withHost($this->resolveDomain($shortUrl)) ->withPath($this->basePath) ->__toString(); @@ -32,7 +31,6 @@ readonly class ShortUrlStringifier implements ShortUrlStringifierInterface private function resolveDomain(ShortUrl $shortUrl): string { - $domainConfig = $this->urlShortenerOptions->domain; - return $shortUrl->getDomain()?->authority ?? $domainConfig['hostname'] ?? ''; + return $shortUrl->getDomain()?->authority ?? $this->urlShortenerOptions->defaultDomain; } } diff --git a/module/Core/src/ShortUrl/Resolver/PersistenceShortUrlRelationResolver.php b/module/Core/src/ShortUrl/Resolver/PersistenceShortUrlRelationResolver.php index 0b79f97d..94fb314a 100644 --- a/module/Core/src/ShortUrl/Resolver/PersistenceShortUrlRelationResolver.php +++ b/module/Core/src/ShortUrl/Resolver/PersistenceShortUrlRelationResolver.php @@ -40,7 +40,7 @@ class PersistenceShortUrlRelationResolver implements ShortUrlRelationResolverInt public function resolveDomain(?string $domain): ?Domain { - if ($domain === null || $domain === $this->options->defaultDomain()) { + if ($domain === null || $domain === $this->options->defaultDomain) { return null; } diff --git a/module/Core/src/ShortUrl/ShortUrlListService.php b/module/Core/src/ShortUrl/ShortUrlListService.php index b6539256..853a40b9 100644 --- a/module/Core/src/ShortUrl/ShortUrlListService.php +++ b/module/Core/src/ShortUrl/ShortUrlListService.php @@ -24,7 +24,7 @@ readonly class ShortUrlListService implements ShortUrlListServiceInterface */ public function listShortUrls(ShortUrlsParams $params, ?ApiKey $apiKey = null): Paginator { - $defaultDomain = $this->urlShortenerOptions->defaultDomain(); + $defaultDomain = $this->urlShortenerOptions->defaultDomain; $paginator = new Paginator(new ShortUrlRepositoryAdapter($this->repo, $params, $apiKey, $defaultDomain)); $paginator->setMaxPerPage($params->itemsPerPage) ->setCurrentPage($params->page); diff --git a/module/Core/test/Action/QrCodeActionTest.php b/module/Core/test/Action/QrCodeActionTest.php index d040b7d7..5e499403 100644 --- a/module/Core/test/Action/QrCodeActionTest.php +++ b/module/Core/test/Action/QrCodeActionTest.php @@ -17,7 +17,6 @@ use Psr\Log\NullLogger; use Shlinkio\Shlink\Common\Response\QrCodeResponse; use Shlinkio\Shlink\Core\Action\QrCodeAction; use Shlinkio\Shlink\Core\Config\Options\QrCodeOptions; -use Shlinkio\Shlink\Core\Config\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Exception\ShortUrlNotFoundException; use Shlinkio\Shlink\Core\ShortUrl\Entity\ShortUrl; use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifier; diff --git a/module/Core/test/Domain/DomainServiceTest.php b/module/Core/test/Domain/DomainServiceTest.php index 82a25654..7e2fea18 100644 --- a/module/Core/test/Domain/DomainServiceTest.php +++ b/module/Core/test/Domain/DomainServiceTest.php @@ -29,10 +29,7 @@ class DomainServiceTest extends TestCase protected function setUp(): void { $this->em = $this->createMock(EntityManagerInterface::class); - $this->domainService = new DomainService( - $this->em, - new UrlShortenerOptions(domain: ['hostname' => 'default.com']), - ); + $this->domainService = new DomainService($this->em, new UrlShortenerOptions(defaultDomain: 'default.com')); } #[Test, DataProvider('provideExcludedDomains')] diff --git a/module/Core/test/Matomo/MatomoVisitSenderTest.php b/module/Core/test/Matomo/MatomoVisitSenderTest.php index 1794973b..6a4659f1 100644 --- a/module/Core/test/Matomo/MatomoVisitSenderTest.php +++ b/module/Core/test/Matomo/MatomoVisitSenderTest.php @@ -37,7 +37,7 @@ class MatomoVisitSenderTest extends TestCase $this->visitSender = new MatomoVisitSender( $this->trackerBuilder, - new ShortUrlStringifier(new UrlShortenerOptions(domain: ['hostname' => 's2.test'])), + new ShortUrlStringifier(new UrlShortenerOptions(defaultDomain: 's2.test')), $this->visitIterationRepository, ); } diff --git a/module/Core/test/ShortUrl/Helper/ShortUrlStringifierTest.php b/module/Core/test/ShortUrl/Helper/ShortUrlStringifierTest.php index 42379531..d28fdf0e 100644 --- a/module/Core/test/ShortUrl/Helper/ShortUrlStringifierTest.php +++ b/module/Core/test/ShortUrl/Helper/ShortUrlStringifierTest.php @@ -14,14 +14,18 @@ use Shlinkio\Shlink\Core\ShortUrl\Model\ShortUrlCreation; class ShortUrlStringifierTest extends TestCase { + /** + * @param 'http'|'https' $schema + */ #[Test, DataProvider('provideConfigAndShortUrls')] public function generatesExpectedOutputBasedOnConfigAndShortUrl( - array $domainConfig, + string $defaultDomain, + string $schema, string $basePath, ShortUrl $shortUrl, string $expected, ): void { - $stringifier = new ShortUrlStringifier(new UrlShortenerOptions($domainConfig), $basePath); + $stringifier = new ShortUrlStringifier(new UrlShortenerOptions($defaultDomain, $schema), $basePath); self::assertEquals($expected, $stringifier->stringify($shortUrl)); } @@ -36,45 +40,45 @@ class ShortUrlStringifierTest extends TestCase ]), ); - yield 'no config' => [[], '', $shortUrlWithShortCode('foo'), 'http:/foo']; - yield 'hostname in config' => [ - ['hostname' => 'example.com'], + yield 'no default domain' => ['', 'http', '', $shortUrlWithShortCode('foo'), 'http:/foo']; + yield 'default domain' => [ + 'example.com', + 'http', '', $shortUrlWithShortCode('bar'), 'http://example.com/bar', ]; yield 'special chars in short code' => [ - ['hostname' => 'example.com'], + 'example.com', + 'http', '', $shortUrlWithShortCode('グーグル'), 'http://example.com/グーグル', ]; yield 'emojis in short code' => [ - ['hostname' => 'example.com'], + 'example.com', + 'http', '', $shortUrlWithShortCode('🦣-🍅'), 'http://example.com/🦣-🍅', ]; - yield 'hostname with base path in config' => [ - ['hostname' => 'example.com/foo/bar'], + yield 'default domain with base path' => [ + 'example.com/foo/bar', + 'http', '', $shortUrlWithShortCode('abc'), 'http://example.com/foo/bar/abc', ]; - yield 'full config' => [ - ['schema' => 'https', 'hostname' => 'foo.com'], - '', - $shortUrlWithShortCode('baz'), - 'https://foo.com/baz', - ]; yield 'custom domain' => [ - ['schema' => 'https', 'hostname' => 'foo.com'], + 'foo.com', + 'https', '', $shortUrlWithShortCode('baz', 'mydom.es'), 'https://mydom.es/baz', ]; yield 'custom domain with base path' => [ - ['schema' => 'https', 'hostname' => 'foo.com'], + 'foo.com', + 'https', '/foo/bar', $shortUrlWithShortCode('baz', 'mydom.es'), 'https://mydom.es/foo/bar/baz', diff --git a/module/Core/test/ShortUrl/Resolver/PersistenceShortUrlRelationResolverTest.php b/module/Core/test/ShortUrl/Resolver/PersistenceShortUrlRelationResolverTest.php index d6223734..722ac347 100644 --- a/module/Core/test/ShortUrl/Resolver/PersistenceShortUrlRelationResolverTest.php +++ b/module/Core/test/ShortUrl/Resolver/PersistenceShortUrlRelationResolverTest.php @@ -29,9 +29,7 @@ class PersistenceShortUrlRelationResolverTest extends TestCase $this->em = $this->createMock(EntityManagerInterface::class); $this->em->method('getEventManager')->willReturn(new EventManager()); - $this->resolver = new PersistenceShortUrlRelationResolver($this->em, new UrlShortenerOptions( - domain: ['schema' => 'https', 'hostname' => 'default.com'], - )); + $this->resolver = new PersistenceShortUrlRelationResolver($this->em, new UrlShortenerOptions('default.com')); } #[Test, DataProvider('provideDomainsThatEmpty')] diff --git a/module/Rest/src/Action/Visit/DomainVisitsAction.php b/module/Rest/src/Action/Visit/DomainVisitsAction.php index 31b8add4..4d534202 100644 --- a/module/Rest/src/Action/Visit/DomainVisitsAction.php +++ b/module/Rest/src/Action/Visit/DomainVisitsAction.php @@ -38,7 +38,7 @@ class DomainVisitsAction extends AbstractRestAction private function resolveDomainParam(Request $request): string { $domainParam = $request->getAttribute('domain', ''); - if ($domainParam === $this->urlShortenerOptions->defaultDomain()) { + if ($domainParam === $this->urlShortenerOptions->defaultDomain) { return 'DEFAULT'; } diff --git a/module/Rest/src/Middleware/ShortUrl/DropDefaultDomainFromRequestMiddleware.php b/module/Rest/src/Middleware/ShortUrl/DropDefaultDomainFromRequestMiddleware.php index f50341d2..acf33603 100644 --- a/module/Rest/src/Middleware/ShortUrl/DropDefaultDomainFromRequestMiddleware.php +++ b/module/Rest/src/Middleware/ShortUrl/DropDefaultDomainFromRequestMiddleware.php @@ -28,7 +28,7 @@ readonly class DropDefaultDomainFromRequestMiddleware implements MiddlewareInter private function sanitizeDomainFromPayload(array $payload): array { - if (isset($payload['domain']) && $payload['domain'] === $this->urlShortenerOptions->defaultDomain()) { + if (isset($payload['domain']) && $payload['domain'] === $this->urlShortenerOptions->defaultDomain) { unset($payload['domain']); } diff --git a/module/Rest/test/Action/ShortUrl/ListShortUrlsActionTest.php b/module/Rest/test/Action/ShortUrl/ListShortUrlsActionTest.php index 071fb844..a3beba1b 100644 --- a/module/Rest/test/Action/ShortUrl/ListShortUrlsActionTest.php +++ b/module/Rest/test/Action/ShortUrl/ListShortUrlsActionTest.php @@ -31,10 +31,7 @@ class ListShortUrlsActionTest extends TestCase $this->service = $this->createMock(ShortUrlListServiceInterface::class); $this->action = new ListShortUrlsAction($this->service, new ShortUrlDataTransformer( - new ShortUrlStringifier(new UrlShortenerOptions(domain: [ - 'hostname' => 's.test', - 'schema' => 'https', - ])), + new ShortUrlStringifier(new UrlShortenerOptions('s.test')), )); } diff --git a/module/Rest/test/Action/Visit/DomainVisitsActionTest.php b/module/Rest/test/Action/Visit/DomainVisitsActionTest.php index 3fb4c498..d60dae2e 100644 --- a/module/Rest/test/Action/Visit/DomainVisitsActionTest.php +++ b/module/Rest/test/Action/Visit/DomainVisitsActionTest.php @@ -25,10 +25,7 @@ class DomainVisitsActionTest extends TestCase protected function setUp(): void { $this->visitsHelper = $this->createMock(VisitsStatsHelperInterface::class); - $this->action = new DomainVisitsAction( - $this->visitsHelper, - new UrlShortenerOptions(domain: ['hostname' => 'the_default.com']), - ); + $this->action = new DomainVisitsAction($this->visitsHelper, new UrlShortenerOptions('the_default.com')); } #[Test, DataProvider('provideDomainAuthorities')] diff --git a/module/Rest/test/Middleware/ShortUrl/DropDefaultDomainFromRequestMiddlewareTest.php b/module/Rest/test/Middleware/ShortUrl/DropDefaultDomainFromRequestMiddlewareTest.php index 530e28ca..b8235498 100644 --- a/module/Rest/test/Middleware/ShortUrl/DropDefaultDomainFromRequestMiddlewareTest.php +++ b/module/Rest/test/Middleware/ShortUrl/DropDefaultDomainFromRequestMiddlewareTest.php @@ -24,9 +24,7 @@ class DropDefaultDomainFromRequestMiddlewareTest extends TestCase protected function setUp(): void { $this->next = $this->createMock(RequestHandlerInterface::class); - $this->middleware = new DropDefaultDomainFromRequestMiddleware( - new UrlShortenerOptions(domain: ['hostname' => 's.test']), - ); + $this->middleware = new DropDefaultDomainFromRequestMiddleware(new UrlShortenerOptions('s.test')); } #[Test, DataProvider('provideQueryParams')] diff --git a/shlink-dev.env.dist b/shlink-dev.env.dist index 5ab21ea1..1bd58636 100644 --- a/shlink-dev.env.dist +++ b/shlink-dev.env.dist @@ -3,6 +3,7 @@ LC_ALL=C #GEOLITE_LICENSE_KEY= # URL shortener +DEFAULT_DOMAIN=localhost:8800 IS_HTTPS_ENABLED=false # Database - MySQL From 442eea0ea71d7a416fc161692f0d1a915b51f81b Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Wed, 23 Oct 2024 10:04:37 +0200 Subject: [PATCH 10/25] Add script to run CLI tests that loads and exports test env vars --- bin/test/run-cli-tests.sh | 16 ++++++++++++++++ composer.json | 4 ++-- config/test/shlink-test.env | 5 ----- 3 files changed, 18 insertions(+), 7 deletions(-) create mode 100755 bin/test/run-cli-tests.sh diff --git a/bin/test/run-cli-tests.sh b/bin/test/run-cli-tests.sh new file mode 100755 index 00000000..c97f8705 --- /dev/null +++ b/bin/test/run-cli-tests.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env sh + +export APP_ENV=test +export TEST_ENV=cli +export DB_DRIVER=maria + +# Load and export test env vars +set -a +. ./config/test/shlink-test.env +set +a + +vendor/bin/phpunit --order-by=random --testdox --testdox-summary -c phpunit-cli.xml $* +TESTS_EXIT_CODE=$? + +# Exit this script with the same code as the tests. If tests failed, this script has to fail +exit $TESTS_EXIT_CODE diff --git a/composer.json b/composer.json index 6f8feba4..2fe653e0 100644 --- a/composer.json +++ b/composer.json @@ -73,7 +73,7 @@ "phpunit/phpunit": "^11.3", "roave/security-advisories": "dev-master", "shlinkio/php-coding-standard": "~2.3.0", - "shlinkio/shlink-test-utils": "^4.1", + "shlinkio/shlink-test-utils": "^4.1.1", "symfony/var-dumper": "^7.0", "veewee/composer-run-parallel": "^1.3" }, @@ -136,7 +136,7 @@ "test:api:mssql": "DB_DRIVER=mssql composer test:api -- $*", "test:api:ci": "GENERATE_COVERAGE=yes composer test:api && vendor/bin/phpcov merge build/coverage-api --php build/coverage-api.cov && rm build/coverage-api/*.cov", "test:api:pretty": "GENERATE_COVERAGE=yes composer test:api && vendor/bin/phpcov merge build/coverage-api --html build/coverage-api/coverage-html && rm build/coverage-api/*.cov", - "test:cli": "APP_ENV=test DB_DRIVER=maria TEST_ENV=cli php vendor/bin/phpunit --order-by=random --testdox --testdox-summary -c phpunit-cli.xml", + "test:cli": "bin/test/run-cli-tests.sh", "test:cli:ci": "XDEBUG_MODE=coverage GENERATE_COVERAGE=yes composer test:cli && vendor/bin/phpcov merge build/coverage-cli --php build/coverage-cli.cov && rm build/coverage-cli/*.cov", "test:cli:pretty": "XDEBUG_MODE=coverage GENERATE_COVERAGE=yes composer test:cli && vendor/bin/phpcov merge build/coverage-cli --html build/coverage-cli/coverage-html && rm build/coverage-cli/*.cov", "swagger:validate": "php-openapi validate docs/swagger/swagger.json", diff --git a/config/test/shlink-test.env b/config/test/shlink-test.env index d0eac32a..a7126e10 100644 --- a/config/test/shlink-test.env +++ b/config/test/shlink-test.env @@ -1,8 +1,3 @@ # URL shortener DEFAULT_DOMAIN=s.test IS_HTTPS_ENABLED=false - -# Disable mercure integration during E2E tests -MERCURE_PUBLIC_HUB_URL=null -MERCURE_INTERNAL_HUB_URL=null -MERCURE_JWT_SECRET=null From f3244b35e3500b1c43e56dc8873ab57d11a911d8 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Wed, 23 Oct 2024 10:53:09 +0200 Subject: [PATCH 11/25] Remove remaining local config files --- Dockerfile | 1 - config/autoload/.gitignore | 2 -- config/autoload/app_options.global.php | 4 +++- config/autoload/app_options.local.php.dist | 11 ---------- config/autoload/common.global.php | 17 +++++++++------ config/autoload/common.local.php.dist | 12 ----------- config/autoload/dependencies.global.php | 3 ++- config/autoload/dependencies.local.php.dist | 24 --------------------- config/autoload/logger.global.php | 14 ++++++++++-- config/autoload/logger.local.php.dist | 18 ---------------- config/autoload/router.global.php | 2 +- config/autoload/router.local.php.dist | 15 ------------- config/config.php | 13 +++++------ docker/config/shlink_in_docker.local.php | 18 ---------------- module/Core/src/Config/EnvVars.php | 17 +++++++++++++++ shlink-dev.env.dist | 2 +- 16 files changed, 52 insertions(+), 121 deletions(-) delete mode 100644 config/autoload/.gitignore delete mode 100644 config/autoload/app_options.local.php.dist delete mode 100644 config/autoload/common.local.php.dist delete mode 100644 config/autoload/dependencies.local.php.dist delete mode 100644 config/autoload/logger.local.php.dist delete mode 100644 config/autoload/router.local.php.dist delete mode 100644 docker/config/shlink_in_docker.local.php diff --git a/Dockerfile b/Dockerfile index 4251b3e4..3a1d5bcc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -61,7 +61,6 @@ EXPOSE 8080 # Copy config specific for the image COPY docker/docker-entrypoint.sh docker-entrypoint.sh -COPY docker/config/shlink_in_docker.local.php config/autoload/shlink_in_docker.local.php COPY docker/config/php.ini ${PHP_INI_DIR}/conf.d/ USER ${USER_ID} diff --git a/config/autoload/.gitignore b/config/autoload/.gitignore deleted file mode 100644 index 1a83fda6..00000000 --- a/config/autoload/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -local.php -*.local.php diff --git a/config/autoload/app_options.global.php b/config/autoload/app_options.global.php index 0b7ec937..9590e544 100644 --- a/config/autoload/app_options.global.php +++ b/config/autoload/app_options.global.php @@ -2,11 +2,13 @@ declare(strict_types=1); +use Shlinkio\Shlink\Core\Config\EnvVars; + return [ 'app_options' => [ 'name' => 'Shlink', - 'version' => '%SHLINK_VERSION%', + 'version' => EnvVars::isDevEnv() ? 'latest' : '%SHLINK_VERSION%', ], ]; diff --git a/config/autoload/app_options.local.php.dist b/config/autoload/app_options.local.php.dist deleted file mode 100644 index 14633a61..00000000 --- a/config/autoload/app_options.local.php.dist +++ /dev/null @@ -1,11 +0,0 @@ - [ - 'version' => 'latest', - ], - -]; diff --git a/config/autoload/common.global.php b/config/autoload/common.global.php index c7db57f1..0ae55ce3 100644 --- a/config/autoload/common.global.php +++ b/config/autoload/common.global.php @@ -3,13 +3,18 @@ declare(strict_types=1); use Laminas\ConfigAggregator\ConfigAggregator; +use Shlinkio\Shlink\Core\Config\EnvVars; -return [ +return (function () { + $isDev = EnvVars::isDevEnv(); - 'debug' => false, + return [ - // Disabling config cache for cli, ensures it's never used for RoadRunner, and also that console - // commands don't generate a cache file that's then used by php-fpm web executions - ConfigAggregator::ENABLE_CACHE => PHP_SAPI !== 'cli', + 'debug' => $isDev, -]; + // Disabling config cache for cli, ensures it's never used for RoadRunner, and also that console + // commands don't generate a cache file that's then used by php-fpm web executions + ConfigAggregator::ENABLE_CACHE => ! $isDev && PHP_SAPI !== 'cli', + + ]; +})(); diff --git a/config/autoload/common.local.php.dist b/config/autoload/common.local.php.dist deleted file mode 100644 index f29c74b0..00000000 --- a/config/autoload/common.local.php.dist +++ /dev/null @@ -1,12 +0,0 @@ - true, - ConfigAggregator::ENABLE_CACHE => false, - -]; diff --git a/config/autoload/dependencies.global.php b/config/autoload/dependencies.global.php index 469171ca..0a99d323 100644 --- a/config/autoload/dependencies.global.php +++ b/config/autoload/dependencies.global.php @@ -11,6 +11,7 @@ use Psr\Http\Client\ClientInterface; use Psr\Http\Message\ServerRequestFactoryInterface; use Psr\Http\Message\StreamFactoryInterface; use Psr\Http\Message\UploadedFileFactoryInterface; +use Shlinkio\Shlink\Core\Config\EnvVars; use Spiral\RoadRunner\Http\PSR7Worker; use Spiral\RoadRunner\WorkerInterface; use Symfony\Component\Filesystem\Filesystem; @@ -36,7 +37,7 @@ return [ 'lazy_services' => [ 'proxies_target_dir' => 'data/proxies', 'proxies_namespace' => 'ShlinkProxy', - 'write_proxy_files' => true, + 'write_proxy_files' => EnvVars::isProdEnv(), ], ], diff --git a/config/autoload/dependencies.local.php.dist b/config/autoload/dependencies.local.php.dist deleted file mode 100644 index d9569029..00000000 --- a/config/autoload/dependencies.local.php.dist +++ /dev/null @@ -1,24 +0,0 @@ - [ - 'lazy_services' => [ - 'write_proxy_files' => false, - ], - - 'initializers' => [ - function (ContainerInterface $container, $instance): void { - if ($instance instanceof Log\LoggerAwareInterface) { - $instance->setLogger($container->get(Log\LoggerInterface::class)); - } - }, - ], - ], - -]; diff --git a/config/autoload/logger.global.php b/config/autoload/logger.global.php index c7d7d757..56ba42bb 100644 --- a/config/autoload/logger.global.php +++ b/config/autoload/logger.global.php @@ -14,23 +14,33 @@ use Shlinkio\Shlink\Common\Logger\LoggerFactory; use Shlinkio\Shlink\Common\Logger\LoggerType; use Shlinkio\Shlink\Common\Middleware\AccessLogMiddleware; use Shlinkio\Shlink\Common\Middleware\RequestIdMiddleware; +use Shlinkio\Shlink\Core\Config\EnvVars; use Shlinkio\Shlink\Core\EventDispatcher\Helper\RequestIdProvider; use Shlinkio\Shlink\EventDispatcher\Util\RequestIdProviderInterface; +use function Shlinkio\Shlink\Config\env; use function Shlinkio\Shlink\Config\runningInRoadRunner; return (static function (): array { + $isDev = EnvVars::isDevEnv(); $common = [ - 'level' => Level::Info->value, + 'level' => $isDev ? Level::Debug->value : Level::Info->value, 'processors' => [RequestIdMiddleware::class], 'line_format' => '[%datetime%] [%extra.' . RequestIdMiddleware::ATTRIBUTE . '%] %channel%.%level_name% - %message%', ]; + // In dev env or the docker container, stream Shlink logs to stderr, otherwise send them to a file + $useStreamForShlinkLogger = $isDev || env('SHLINK_RUNTIME') !== null; + return [ 'logger' => [ - 'Shlink' => [ + 'Shlink' => $useStreamForShlinkLogger ? [ + 'type' => LoggerType::STREAM->value, + 'destination' => 'php://stderr', + ...$common, + ] : [ 'type' => LoggerType::FILE->value, ...$common, ], diff --git a/config/autoload/logger.local.php.dist b/config/autoload/logger.local.php.dist deleted file mode 100644 index fe2c8c54..00000000 --- a/config/autoload/logger.local.php.dist +++ /dev/null @@ -1,18 +0,0 @@ - [ - 'Shlink' => [ - 'type' => LoggerType::STREAM->value, - 'destination' => 'php://stderr', - 'level' => Level::Debug->value, - ], - ], - -]; diff --git a/config/autoload/router.global.php b/config/autoload/router.global.php index db389f3a..0464ca83 100644 --- a/config/autoload/router.global.php +++ b/config/autoload/router.global.php @@ -13,7 +13,7 @@ return [ 'fastroute' => [ // Disabling config cache for cli, ensures it's never used for RoadRunner, and also that console // commands don't generate a cache file that's then used by php-fpm web executions - FastRouteRouter::CONFIG_CACHE_ENABLED => PHP_SAPI !== 'cli', + FastRouteRouter::CONFIG_CACHE_ENABLED => EnvVars::isProdEnv() && PHP_SAPI !== 'cli', FastRouteRouter::CONFIG_CACHE_FILE => 'data/cache/fastroute_cached_routes.php', ], ], diff --git a/config/autoload/router.local.php.dist b/config/autoload/router.local.php.dist deleted file mode 100644 index dc67b587..00000000 --- a/config/autoload/router.local.php.dist +++ /dev/null @@ -1,15 +0,0 @@ - [ - 'fastroute' => [ - FastRouteRouter::CONFIG_CACHE_ENABLED => false, - ], - ], - -]; diff --git a/config/config.php b/config/config.php index 40f88ea3..92c69ba0 100644 --- a/config/config.php +++ b/config/config.php @@ -8,10 +8,7 @@ use Laminas\ConfigAggregator; use Laminas\Diactoros; use Mezzio; use Mezzio\ProblemDetails; - -use function Shlinkio\Shlink\Config\env; - -$isTestEnv = env('APP_ENV') === 'test'; +use Shlinkio\Shlink\Core\Config\EnvVars; return (new ConfigAggregator\ConfigAggregator( providers: [ @@ -29,10 +26,10 @@ return (new ConfigAggregator\ConfigAggregator( CLI\ConfigProvider::class, Rest\ConfigProvider::class, new ConfigAggregator\PhpFileProvider('config/autoload/{,*.}global.php'), - // Local config should not be loaded during tests, whereas test config should be loaded ONLY during tests - new ConfigAggregator\PhpFileProvider( - $isTestEnv ? 'config/test/*.global.php' : 'config/autoload/{,*.}local.php', - ), + // Test config should be loaded ONLY during tests + EnvVars::isTestEnv() + ? new ConfigAggregator\PhpFileProvider('config/test/*.global.php') + : new ConfigAggregator\ArrayProvider([]), // Routes have to be loaded last new ConfigAggregator\PhpFileProvider('config/autoload/routes.config.php'), ], diff --git a/docker/config/shlink_in_docker.local.php b/docker/config/shlink_in_docker.local.php deleted file mode 100644 index 2d5d6a06..00000000 --- a/docker/config/shlink_in_docker.local.php +++ /dev/null @@ -1,18 +0,0 @@ - [ - 'Shlink' => [ - 'type' => LoggerType::STREAM->value, - 'destination' => 'php://stderr', - ], - ], - -]; diff --git a/module/Core/src/Config/EnvVars.php b/module/Core/src/Config/EnvVars.php index 2fe5dfd9..6cdf6297 100644 --- a/module/Core/src/Config/EnvVars.php +++ b/module/Core/src/Config/EnvVars.php @@ -27,6 +27,7 @@ use const Shlinkio\Shlink\DEFAULT_SHORT_CODES_LENGTH; enum EnvVars: string { + case APP_ENV = 'APP_ENV'; case DELETE_SHORT_URL_THRESHOLD = 'DELETE_SHORT_URL_THRESHOLD'; case DB_DRIVER = 'DB_DRIVER'; case DB_NAME = 'DB_NAME'; @@ -117,6 +118,7 @@ enum EnvVars: string private function defaultValue(): string|int|bool|null { return match ($this) { + self::APP_ENV => 'prod', self::MEMORY_LIMIT => '512M', self::TIMEZONE => date_default_timezone_get(), @@ -174,4 +176,19 @@ enum EnvVars: string { return $this->loadFromEnv() !== null; } + + public static function isProdEnv(): bool + { + return self::APP_ENV->loadFromEnv() === 'prod'; + } + + public static function isDevEnv(): bool + { + return self::APP_ENV->loadFromEnv() === 'dev'; + } + + public static function isTestEnv(): bool + { + return self::APP_ENV->loadFromEnv() === 'test'; + } } diff --git a/shlink-dev.env.dist b/shlink-dev.env.dist index 1bd58636..ee6173f2 100644 --- a/shlink-dev.env.dist +++ b/shlink-dev.env.dist @@ -1,5 +1,5 @@ LC_ALL=C -#APP_ENV=dev +APP_ENV=dev #GEOLITE_LICENSE_KEY= # URL shortener From a3ea8f56dd81b9f6acc88817b1bf001bd8bfa873 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Thu, 24 Oct 2024 08:49:58 +0200 Subject: [PATCH 12/25] Remove app_options config --- Dockerfile | 2 +- build.sh | 4 ++-- config/autoload/app_options.global.php | 14 -------------- module/Core/config/dependencies.config.php | 3 +-- module/Core/src/Config/Options/AppOptions.php | 10 +++++++++- 5 files changed, 13 insertions(+), 20 deletions(-) delete mode 100644 config/autoload/app_options.global.php diff --git a/Dockerfile b/Dockerfile index 3a1d5bcc..e6e94734 100644 --- a/Dockerfile +++ b/Dockerfile @@ -43,7 +43,7 @@ RUN apk add --no-cache git && \ php composer.phar install --no-dev --prefer-dist --optimize-autoloader --no-progress --no-interaction && \ php composer.phar clear-cache && \ rm -r docker composer.* && \ - sed -i "s/%SHLINK_VERSION%/${SHLINK_VERSION}/g" config/autoload/app_options.global.php + sed -i "s/%SHLINK_VERSION%/${SHLINK_VERSION}/g" module/Core/src/Config/Options/AppOptions.php # Prepare final image diff --git a/build.sh b/build.sh index 7b77295f..6786c492 100755 --- a/build.sh +++ b/build.sh @@ -35,8 +35,8 @@ ${composerBin} install --no-dev --prefer-dist --optimize-autoloader --no-progres echo 'Deleting dev files...' rm composer.* -# Update Shlink version in config -sed -i "s/%SHLINK_VERSION%/${version}/g" config/autoload/app_options.global.php +# Update Shlink version +sed -i "s/%SHLINK_VERSION%/${version}/g" module/Core/src/Config/Options/AppOptions.php # Compressing file echo 'Compressing files...' diff --git a/config/autoload/app_options.global.php b/config/autoload/app_options.global.php deleted file mode 100644 index 9590e544..00000000 --- a/config/autoload/app_options.global.php +++ /dev/null @@ -1,14 +0,0 @@ - [ - 'name' => 'Shlink', - 'version' => EnvVars::isDevEnv() ? 'latest' : '%SHLINK_VERSION%', - ], - -]; diff --git a/module/Core/config/dependencies.config.php b/module/Core/config/dependencies.config.php index 420d8a39..552d5e2a 100644 --- a/module/Core/config/dependencies.config.php +++ b/module/Core/config/dependencies.config.php @@ -8,7 +8,6 @@ use Laminas\ServiceManager\AbstractFactory\ConfigAbstractFactory; use Laminas\ServiceManager\Factory\InvokableFactory; use Psr\EventDispatcher\EventDispatcherInterface; use Shlinkio\Shlink\Common\Doctrine\EntityRepositoryFactory; -use Shlinkio\Shlink\Config\Factory\ValinorConfigFactory; use Shlinkio\Shlink\Core\Config\Options\NotFoundRedirectOptions; use Shlinkio\Shlink\Core\ShortUrl\Helper\ShortUrlStringifier; use Shlinkio\Shlink\Importer\ImportedLinksProcessorInterface; @@ -24,7 +23,7 @@ return [ ErrorHandler\NotFoundRedirectHandler::class => ConfigAbstractFactory::class, ErrorHandler\NotFoundTemplateHandler::class => InvokableFactory::class, - Config\Options\AppOptions::class => [ValinorConfigFactory::class, 'config.app_options'], + Config\Options\AppOptions::class => [Config\Options\AppOptions::class, 'fromEnv'], Config\Options\DeleteShortUrlsOptions::class => [Config\Options\DeleteShortUrlsOptions::class, 'fromEnv'], Config\Options\NotFoundRedirectOptions::class => [Config\Options\NotFoundRedirectOptions::class, 'fromEnv'], Config\Options\RedirectOptions::class => [Config\Options\RedirectOptions::class, 'fromEnv'], diff --git a/module/Core/src/Config/Options/AppOptions.php b/module/Core/src/Config/Options/AppOptions.php index 42f07642..71e3f507 100644 --- a/module/Core/src/Config/Options/AppOptions.php +++ b/module/Core/src/Config/Options/AppOptions.php @@ -4,14 +4,22 @@ declare(strict_types=1); namespace Shlinkio\Shlink\Core\Config\Options; +use Shlinkio\Shlink\Core\Config\EnvVars; + use function sprintf; final class AppOptions { - public function __construct(public string $name = 'Shlink', public string $version = '3.0.0') + public function __construct(public string $name = 'Shlink', public string $version = '4.0.0') { } + public static function fromEnv(): self + { + $version = EnvVars::isDevEnv() ? 'latest' : '%SHLINK_VERSION%'; + return new self(version: $version); + } + public function __toString(): string { return sprintf('%s:v%s', $this->name, $this->version); From f4da9c1fccdb4b3282ba159c7a04685db83dd524 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Thu, 24 Oct 2024 09:22:44 +0200 Subject: [PATCH 13/25] Update dependencies to stop using cuyz/valinor --- composer.json | 56 +++++++++++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/composer.json b/composer.json index 2fe653e0..4c66c67f 100644 --- a/composer.json +++ b/composer.json @@ -18,64 +18,64 @@ "ext-json": "*", "ext-mbstring": "*", "ext-pdo": "*", - "akrabat/ip-address-middleware": "^2.1", - "cakephp/chronos": "^3.0.2", - "doctrine/dbal": "^4.1", - "doctrine/migrations": "^3.6", - "doctrine/orm": "^3.2", + "akrabat/ip-address-middleware": "^2.3", + "cakephp/chronos": "^3.1", + "doctrine/dbal": "^4.2", + "doctrine/migrations": "^3.8", + "doctrine/orm": "^3.3", "endroid/qr-code": "^6.0", "friendsofphp/proxy-manager-lts": "^1.0", "geoip2/geoip2": "^3.0", - "guzzlehttp/guzzle": "^7.5", + "guzzlehttp/guzzle": "^7.9", "hidehalo/nanoid-php": "^1.1", "jaybizzle/crawler-detect": "^1.2.116", - "laminas/laminas-config": "^3.8", + "laminas/laminas-config": "^3.9", "laminas/laminas-config-aggregator": "^1.15", - "laminas/laminas-diactoros": "^3.3", - "laminas/laminas-inputfilter": "^2.27", - "laminas/laminas-servicemanager": "^3.21", - "laminas/laminas-stdlib": "^3.17", - "matomo/matomo-php-tracker": "^3.2", - "mezzio/mezzio": "^3.17", - "mezzio/mezzio-fastroute": "^3.11", - "mezzio/mezzio-problem-details": "^1.13", + "laminas/laminas-diactoros": "^3.5", + "laminas/laminas-inputfilter": "^2.30", + "laminas/laminas-servicemanager": "^3.22", + "laminas/laminas-stdlib": "^3.19", + "matomo/matomo-php-tracker": "^3.3", + "mezzio/mezzio": "^3.20", + "mezzio/mezzio-fastroute": "^3.12", + "mezzio/mezzio-problem-details": "^1.15", "mlocati/ip-lib": "^1.18", "mobiledetect/mobiledetectlib": "^4.8", "pagerfanta/core": "^3.8", "ramsey/uuid": "^4.7", "shlinkio/doctrine-specification": "^2.1.1", - "shlinkio/shlink-common": "dev-main#e0c872c as 6.4", - "shlinkio/shlink-config": "^3.2.1", + "shlinkio/shlink-common": "dev-main#16d19b7 as 6.4", + "shlinkio/shlink-config": "^3.3", "shlinkio/shlink-event-dispatcher": "^4.1", "shlinkio/shlink-importer": "^5.3.2", "shlinkio/shlink-installer": "^9.2", - "shlinkio/shlink-ip-geolocation": "^4.0", + "shlinkio/shlink-ip-geolocation": "dev-main#0619112 as 4.1", "shlinkio/shlink-json": "^1.1", "spiral/roadrunner": "^2024.1", "spiral/roadrunner-cli": "^2.6", "spiral/roadrunner-http": "^3.5", "spiral/roadrunner-jobs": "^4.5", - "symfony/console": "^7.0", - "symfony/filesystem": "^7.0", - "symfony/lock": "^7.0", - "symfony/process": "^7.0", - "symfony/string": "^7.0" + "symfony/console": "^7.1", + "symfony/filesystem": "^7.1", + "symfony/lock": "^7.1", + "symfony/process": "^7.1", + "symfony/string": "^7.1" }, "require-dev": { "devizzent/cebe-php-openapi": "^1.0.1", "devster/ubench": "^2.1", - "phpstan/phpstan": "^1.11", - "phpstan/phpstan-doctrine": "^1.4", + "phpstan/phpstan": "^1.12", + "phpstan/phpstan-doctrine": "^1.5", "phpstan/phpstan-phpunit": "^1.4", "phpstan/phpstan-symfony": "^1.4", "phpunit/php-code-coverage": "^11.0", "phpunit/phpcov": "^10.0", - "phpunit/phpunit": "^11.3", + "phpunit/phpunit": "^11.4", "roave/security-advisories": "dev-master", "shlinkio/php-coding-standard": "~2.3.0", "shlinkio/shlink-test-utils": "^4.1.1", - "symfony/var-dumper": "^7.0", - "veewee/composer-run-parallel": "^1.3" + "symfony/var-dumper": "^7.1", + "veewee/composer-run-parallel": "^1.4" }, "conflict": { "symfony/var-exporter": ">=6.3.9,<=6.4.0" From 45e108d21e0d9302d7cf8128b19bec94c379e680 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Thu, 24 Oct 2024 09:59:13 +0200 Subject: [PATCH 14/25] Load dev env as a PHP array instead of an env file --- .dockerignore | 2 +- .gitignore | 1 - composer.json | 2 +- config/container.php | 4 +- config/params/.gitignore | 1 + config/params/shlink_dev_env.php.dist | 76 +++++++++++++++++++++++++++ data/infra/roadrunner.Dockerfile | 6 +-- docker-compose.yml | 3 -- shlink-dev.env.dist | 67 ----------------------- 9 files changed, 84 insertions(+), 78 deletions(-) create mode 100644 config/params/shlink_dev_env.php.dist delete mode 100644 shlink-dev.env.dist diff --git a/.dockerignore b/.dockerignore index db2d21f0..893aa659 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,5 +1,6 @@ bin/rr config/autoload/*local* +config/params/*.dist data/infra data/cache/* data/log/* @@ -23,4 +24,3 @@ php*xml* build* **/.* !config/roadrunner/.rr.yml -*.env* diff --git a/.gitignore b/.gitignore index 96ac6719..04c8ed56 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,3 @@ docs/mercure.html docker-compose.override.yml .phpunit.result.cache docs/swagger/swagger-inlined.json -shlink-dev.env diff --git a/composer.json b/composer.json index 4c66c67f..50633ca1 100644 --- a/composer.json +++ b/composer.json @@ -49,7 +49,7 @@ "shlinkio/shlink-event-dispatcher": "^4.1", "shlinkio/shlink-importer": "^5.3.2", "shlinkio/shlink-installer": "^9.2", - "shlinkio/shlink-ip-geolocation": "dev-main#0619112 as 4.1", + "shlinkio/shlink-ip-geolocation": "dev-main#6f35bd4 as 4.1", "shlinkio/shlink-json": "^1.1", "spiral/roadrunner": "^2024.1", "spiral/roadrunner-cli": "^2.6", diff --git a/config/container.php b/config/container.php index 3a1e3355..e207fe16 100644 --- a/config/container.php +++ b/config/container.php @@ -15,8 +15,8 @@ chdir(dirname(__DIR__)); require 'vendor/autoload.php'; -// Promote env vars from installer config -loadEnvVarsFromConfig('config/params/generated_config.php', enumValues(EnvVars::class)); +// Promote env vars from installer or dev config +loadEnvVarsFromConfig('config/params/*.php', enumValues(EnvVars::class)); // This is one of the first files loaded. Configure the timezone and memory limit here ini_set('memory_limit', EnvVars::MEMORY_LIMIT->loadFromEnv()); diff --git a/config/params/.gitignore b/config/params/.gitignore index d6b7ef32..36565258 100644 --- a/config/params/.gitignore +++ b/config/params/.gitignore @@ -1,2 +1,3 @@ * !.gitignore +!*.dist diff --git a/config/params/shlink_dev_env.php.dist b/config/params/shlink_dev_env.php.dist new file mode 100644 index 00000000..2a3905dc --- /dev/null +++ b/config/params/shlink_dev_env.php.dist @@ -0,0 +1,76 @@ +value => 'dev', +// EnvVars::GEOLITE_LICENSE_KEY->value => '', + + // URL shortener + EnvVars::DEFAULT_DOMAIN->value => 'localhost:8800', + EnvVars::IS_HTTPS_ENABLED->value => false, + + // Database - MySQL + EnvVars::DB_DRIVER->value => 'mysql', + EnvVars::DB_USER->value => 'root', + EnvVars::DB_PASSWORD->value => 'root', + EnvVars::DB_NAME->value => 'shlink', +// EnvVars::DB_NAME->value => 'shlink_foo', + EnvVars::DB_HOST->value => 'shlink_db_mysql', + + // Database - Maria +// EnvVars::DB_DRIVER->value => 'maria', +// EnvVars::DB_USER->value => 'root', +// EnvVars::DB_PASSWORD->value => 'root', +// EnvVars::DB_NAME->value => 'shlink_foo', +// EnvVars::DB_HOST->value => 'shlink_db_maria', + + // Database - Postgres +// EnvVars::DB_DRIVER->value => 'postgres', +// EnvVars::DB_USER->value => 'postgres', +// EnvVars::DB_PASSWORD->value => 'root', +// EnvVars::DB_NAME->value => 'shlink_foo', +// EnvVars::DB_HOST->value => 'shlink_db_postgres', + + // Database - MSSQL +// EnvVars::DB_DRIVER->value => 'mssql', +// EnvVars::DB_USER->value => 'sa', +// EnvVars::DB_PASSWORD->value => 'Passw0rd!', +// EnvVars::DB_NAME->value => 'shlink_foo', +// EnvVars::DB_HOST->value => 'shlink_db_ms', + + // Matomo + // Dev matomo instance needs to be manually configured once before enabling the configuration below: + // 1. Go to http://localhost:8003 and follow the installation instructions. + // 2. Open data/infra/matomo/config/config.ini.php and replace `trusted_hosts[] = "localhost"` with + // `trusted_hosts[] = "localhost:8003"` (see https://github.com/matomo-org/matomo/issues/9549) + // 3. Go to http://localhost:8003/index.php?module=SitesManager&action=index and paste the ID for the site you just + // created into the `MATOMO_SITE_ID` var below. + // 4. Go to http://localhost:8003/index.php?module=UsersManager&action=userSecurity, scroll down, click + // "Create new token" and once generated, paste the token into the `MATOMO_API_TOKEN` var below. + // 5. Copy the config below and paste it in a new shlink-dev.local.env file. + EnvVars::MATOMO_ENABLED->value => false, + EnvVars::MATOMO_BASE_URL->value => 'http://shlink_matomo', +// EnvVars::MATOMO_SITE_ID->value => , +// EnvVars::MATOMO_API_TOKEN->value => , + + // Mercure + EnvVars::MERCURE_PUBLIC_HUB_URL->value => 'http://localhost:8002', + EnvVars::MERCURE_INTERNAL_HUB_URL->value => 'http://shlink_mercure_proxy', + EnvVars::MERCURE_JWT_SECRET->value => 'mercure_jwt_key_long_enough_to_avoid_error', + + // RabbitMQ + EnvVars::RABBITMQ_ENABLED->value => true, + EnvVars::RABBITMQ_HOST->value => 'shlink_rabbitmq', + EnvVars::RABBITMQ_PORT->value => 5672, + EnvVars::RABBITMQ_USER->value => 'rabbit', + EnvVars::RABBITMQ_PASSWORD->value => 'rabbit', + + // Redis + EnvVars::REDIS_PUB_SUB_ENABLED->value => true, + EnvVars::REDIS_SERVERS->value => 'tcp://shlink_redis:6379', + +]; diff --git a/data/infra/roadrunner.Dockerfile b/data/infra/roadrunner.Dockerfile index 28d2d343..0bf251f6 100644 --- a/data/infra/roadrunner.Dockerfile +++ b/data/infra/roadrunner.Dockerfile @@ -73,7 +73,7 @@ CMD \ if [[ ! -d "./vendor" ]]; then /usr/local/bin/composer install ; fi && \ # Download roadrunner binary if [[ ! -f "./bin/rr" ]]; then ./vendor/bin/rr get --no-interaction --no-config --location bin/ && chmod +x bin/rr ; fi && \ - # Create .env file if it does not exist yet - if [[ ! -f "./shlink-dev.env" ]]; then cp ./shlink-dev.env.dist ./shlink-dev.env ; fi && \ + # Create env file if it does not exist yet + if [[ ! -f "./config/params/shlink_dev_env.php" ]]; then cp ./config/params/shlink_dev_env.php.dist ./config/params/shlink_dev_env.php ; fi && \ # Run with `exec` so that signals are properly handled - exec ./bin/rr serve --dotenv /home/shlink/shlink-dev.env -c config/roadrunner/.rr.dev.yml + exec ./bin/rr serve -c config/roadrunner/.rr.dev.yml diff --git a/docker-compose.yml b/docker-compose.yml index 05132dfb..e5a34e80 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -34,9 +34,6 @@ services: - shlink_matomo environment: DEFAULT_DOMAIN: localhost:8000 - env_file: - - path: shlink-dev.env - required: false extra_hosts: - 'host.docker.internal:host-gateway' diff --git a/shlink-dev.env.dist b/shlink-dev.env.dist deleted file mode 100644 index ee6173f2..00000000 --- a/shlink-dev.env.dist +++ /dev/null @@ -1,67 +0,0 @@ -LC_ALL=C -APP_ENV=dev -#GEOLITE_LICENSE_KEY= - -# URL shortener -DEFAULT_DOMAIN=localhost:8800 -IS_HTTPS_ENABLED=false - -# Database - MySQL -DB_DRIVER=mysql -DB_USER=root -DB_PASSWORD=root -DB_NAME=shlink -#DB_NAME=shlink_foo -DB_HOST=shlink_db_mysql - -# Database - Maria -#DB_DRIVER=maria -#DB_USER=root -#DB_PASSWORD=root -#DB_NAME=shlink_foo -#DB_HOST=shlink_db_maria - -# Database - Postgres -#DB_DRIVER=postgres -#DB_USER=postgres -#DB_PASSWORD=root -#DB_NAME=shlink_foo -#DB_HOST=shlink_db_postgres - -# Database - MSSQL -#DB_DRIVER=mssql -#DB_USER=sa -#DB_PASSWORD=Passw0rd! -#DB_NAME=shlink_foo -#DB_HOST=shlink_db_ms - -# Matomo -# Dev matomo instance needs to be manually configured once before enabling the configuration below: -# 1. Go to http://localhost:8003 and follow the installation instructions. -# 2. Open data/infra/matomo/config/config.ini.php and replace `trusted_hosts[] = "localhost"` with -# `trusted_hosts[] = "localhost:8003"` (see https://github.com/matomo-org/matomo/issues/9549) -# 3. Go to http://localhost:8003/index.php?module=SitesManager&action=index and paste the ID for the site you just -# created into the `MATOMO_SITE_ID` var below. -# 4. Go to http://localhost:8003/index.php?module=UsersManager&action=userSecurity, scroll down, click -# "Create new token" and once generated, paste the token into the `MATOMO_API_TOKEN` var below. -# 5. Copy the config below and paste it in a new shlink-dev.local.env file. -MATOMO_ENABLED=false -MATOMO_BASE_URL=http://shlink_matomo -#MATOMO_SITE_ID= -#MATOMO_API_TOKEN= - -# Mercure -MERCURE_PUBLIC_HUB_URL=http://localhost:8002 -MERCURE_INTERNAL_HUB_URL=http://shlink_mercure_proxy -MERCURE_JWT_SECRET=mercure_jwt_key_long_enough_to_avoid_error - -# RabbitMQ -RABBITMQ_ENABLED=true -RABBITMQ_HOST=shlink_rabbitmq -RABBITMQ_PORT=5672 -RABBITMQ_USER=rabbit -RABBITMQ_PASSWORD=rabbit - -# Redis -REDIS_PUB_SUB_ENABLED=true -REDIS_SERVERS=tcp://shlink_redis:6379 From 9a5874858123971bd0b2da7381a9f45910e1bbf7 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Thu, 24 Oct 2024 10:00:57 +0200 Subject: [PATCH 15/25] Get LC_ALL env var back to docker compose --- docker-compose.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docker-compose.yml b/docker-compose.yml index e5a34e80..050e6803 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -33,6 +33,7 @@ services: - shlink_rabbitmq - shlink_matomo environment: + LC_ALL: C DEFAULT_DOMAIN: localhost:8000 extra_hosts: - 'host.docker.internal:host-gateway' @@ -58,6 +59,8 @@ services: - shlink_mercure_proxy - shlink_rabbitmq - shlink_matomo + environment: + LC_ALL: C extra_hosts: - 'host.docker.internal:host-gateway' From fc1f35ad59a503e086e9d98b87deb89b3f0a6b52 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Thu, 24 Oct 2024 10:12:34 +0200 Subject: [PATCH 16/25] Update CONTRIBUTING file removing references to old local config files --- CONTRIBUTING.md | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4ee94c70..e44c5fee 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -16,11 +16,20 @@ The first thing you need to do is fork the repository, and clone it in your loca Then you will have to follow these steps: -* Copy all files with `.local.php.dist` extension from `config/autoload` by removing the dist extension. +* Copy the `config/params/shlink_dev_env.php.dist` in the same directory, but removing the `.dist` extension: - For example the `common.local.php.dist` file should be copied as `common.local.php`. + ``` + cp config/params/shlink_dev_env.php.dist config/params/shlink_dev_env.php + ``` + + The `shlink_dev_env.php` file is gitignored, so you can customize it as you want. For example, by adding your own GeoLite license key. + +* Do the same with the `docker-compose.override.yml.dist`: + + ``` + cp docker-compose.override.yml.dist docker-compose.override.yml + ``` -* Copy the file `docker-compose.override.yml.dist` by also removing the `dist` extension. * Start-up the project by running `docker compose up`. The first time this command is run, it will create several containers that are used during development, so it may take some time. From f9694333c5f231de8d0af87c8779ed40b4e7bda2 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Thu, 24 Oct 2024 11:42:53 +0200 Subject: [PATCH 17/25] Add ADR for transition to env vars for dev configs --- ...-env-vars-instead-of-local-config-files.md | 61 +++++++++++++++++++ docs/adr/README.md | 3 +- 2 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 docs/adr/2024-10-24-handle-dev-and-tests-config-via-env-vars-instead-of-local-config-files.md diff --git a/docs/adr/2024-10-24-handle-dev-and-tests-config-via-env-vars-instead-of-local-config-files.md b/docs/adr/2024-10-24-handle-dev-and-tests-config-via-env-vars-instead-of-local-config-files.md new file mode 100644 index 00000000..004c2fce --- /dev/null +++ b/docs/adr/2024-10-24-handle-dev-and-tests-config-via-env-vars-instead-of-local-config-files.md @@ -0,0 +1,61 @@ +# Handle dev and tests config via env vars instead of local config files + +* Status: Accepted +* Date: 2024-10-24 + +## Context and problem statement + +Due to the tools used by Shlink (Zend Expressive first and Mezzio later), configuration has always been handled via the config aggregator, which is a package that continues with Zend Framework 2 config management philosophy: + +1. Define multiple config files, scoped to their own context, that are merge at runtime. +2. Overwrite with so-called "local" config files, which define values used only during development, and should not be shipped to production. + +However, since Shlink started to support other runtimes and added an official docker image, env vars have started to become a central part of the config definition system. + +That has evolved into a system where production config can be read from env vars, but dev config is expected to be defined via local config files, forcing to maintain two approaches to load config that need to coexist. + +On top of that, keeping dev configs in multiple files makes it harder to keep track of everything. + +Because of that, I'm proposing to switch to an env-var-based approach for dev custom configs, and get rid of local config files. + +## Considered options + +1. Define dev env vars in a single `.env` file which is loaded to containers via docker compose `env-file` option. +2. Define dev env vars in a single `.env` file which is loaded via RoadRunner config. +3. Define dev env vars in a single PHP file returning a map that's then loaded with `loadEnvVarsFromConfig`. +4. Keep local config files and don't change anything. + +## Decision outcome + +Defining env vars in a PHP file has the benefit that any change will take effect immediately, so the decision is to go with option 3. + +## Pros and Cons of the Options + +### 1 - .env file via docker compose + +* Good: because it does not require any special mechanism to feed the env vars into the app. +* Good: because it's a standard format known by many. +* Bad: because dev config gets leaked to tests when run inside the container, breaking some existing ones, and forcing to remember this for future tests. +* Bad: because any change to the env file requires the containers to be manually restarted, or putting some new mechanism in place to restart them automatically. + +### 2 - .env file via RoadRunner + +* Good: because it does not require any special mechanism to feed the env vars into the app. +* Good: because it's a standard format known by many. +* Good: because dev config does not get leaked into tests. +* Bad: because any change to the env file requires the containers to be manually restarted, or putting some new mechanism in place to restart them automatically. + +### 3 - PHP file via `loadEnvVarsFromConfig` + +* Good: because the existing call to `loadEnvVarsFromConfig` can be reused by tweaking a bit the glob pattern, so no new dependencies are needed. +* Good: because dev config does not get leaked into tests, and test-specific env vars can be fed using the same mechanism. +* Good: because changes are picked up instantly by both RoadRunner and php-fpm. +* Good: because env vars can be imported from `EnvVars` class, removing the chances of human mistakes and typos. +* Bad: because people not familiar with the project may not expect env vars to be defined in that format. + +### 4 - keep local config + +* Good: because no changes are needed in the project. +* Bad: because managing multiple local config files makes things harder to maintain. +* Bad: because setting-up the project from scratch requires more steps, or an external package to handle config files. +* Bad: because the project needs to keep two ways to load dev configs, and reading an env var does not warranty you are getting the single source of truth. diff --git a/docs/adr/README.md b/docs/adr/README.md index bafb80b5..fa0e7215 100644 --- a/docs/adr/README.md +++ b/docs/adr/README.md @@ -2,7 +2,8 @@ Here listed you will find the different architectural decisions taken in the project, including all the reasoning behind it, options considered, and final outcome. -* [2023-07-09Build `latest` docker image only for actual releases](2023-07-09-build-latest-docker-image-only-for-actual-releases.md) +* [2024-10-24 Handle dev and tests config via env vars instead of local config files](2024-10-24-handle-dev-and-tests-config-via-env-vars-instead-of-local-config-files.md) +* [2023-07-09 Build `latest` docker image only for actual releases](2023-07-09-build-latest-docker-image-only-for-actual-releases.md) * [2023-01-06 Support any HTTP method in short URLs](2023-01-06-support-any-http-method-in-short-urls.md) * [2022-08-05 Support multi-segment custom slugs](2022-08-05-support-multi-segment-custom-slugs.md) * [2022-01-15 Update env vars behavior to have precedence over installer options](2022-01-15-update-env-vars-behavior-to-have-precedence-over-installer-options.md) From 2901fe8b7b5bb803d6a1bf87197d518a2935f55f Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Thu, 24 Oct 2024 11:50:06 +0200 Subject: [PATCH 18/25] Reduce duplication in CLI tests --- bin/test/run-cli-tests.sh | 5 ++++- composer.json | 4 ++-- config/test/shlink-test.env | 2 ++ 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/bin/test/run-cli-tests.sh b/bin/test/run-cli-tests.sh index c97f8705..96ea6cfa 100755 --- a/bin/test/run-cli-tests.sh +++ b/bin/test/run-cli-tests.sh @@ -2,7 +2,10 @@ export APP_ENV=test export TEST_ENV=cli -export DB_DRIVER=maria +export DB_DRIVER="${DB_DRIVER:-"maria"}" +export GENERATE_COVERAGE="${GENERATE_COVERAGE:-"no"}" + +[ "$GENERATE_COVERAGE" != 'no' ] && export XDEBUG_MODE=coverage # Load and export test env vars set -a diff --git a/composer.json b/composer.json index 50633ca1..3e2a089d 100644 --- a/composer.json +++ b/composer.json @@ -137,8 +137,8 @@ "test:api:ci": "GENERATE_COVERAGE=yes composer test:api && vendor/bin/phpcov merge build/coverage-api --php build/coverage-api.cov && rm build/coverage-api/*.cov", "test:api:pretty": "GENERATE_COVERAGE=yes composer test:api && vendor/bin/phpcov merge build/coverage-api --html build/coverage-api/coverage-html && rm build/coverage-api/*.cov", "test:cli": "bin/test/run-cli-tests.sh", - "test:cli:ci": "XDEBUG_MODE=coverage GENERATE_COVERAGE=yes composer test:cli && vendor/bin/phpcov merge build/coverage-cli --php build/coverage-cli.cov && rm build/coverage-cli/*.cov", - "test:cli:pretty": "XDEBUG_MODE=coverage GENERATE_COVERAGE=yes composer test:cli && vendor/bin/phpcov merge build/coverage-cli --html build/coverage-cli/coverage-html && rm build/coverage-cli/*.cov", + "test:cli:ci": "GENERATE_COVERAGE=yes composer test:cli && vendor/bin/phpcov merge build/coverage-cli --php build/coverage-cli.cov && rm build/coverage-cli/*.cov", + "test:cli:pretty": "GENERATE_COVERAGE=yes composer test:cli && vendor/bin/phpcov merge build/coverage-cli --html build/coverage-cli/coverage-html && rm build/coverage-cli/*.cov", "swagger:validate": "php-openapi validate docs/swagger/swagger.json", "swagger:inline": "php-openapi inline docs/swagger/swagger.json docs/swagger/swagger-inlined.json", "clean:dev": "rm -f data/database.sqlite && rm -f config/params/generated_config.php" diff --git a/config/test/shlink-test.env b/config/test/shlink-test.env index a7126e10..48af9b6e 100644 --- a/config/test/shlink-test.env +++ b/config/test/shlink-test.env @@ -1,3 +1,5 @@ +APP_ENV=test + # URL shortener DEFAULT_DOMAIN=s.test IS_HTTPS_ENABLED=false From 84860539cee9c4157bb408d297dbfd29af5f569b Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Thu, 24 Oct 2024 11:58:04 +0200 Subject: [PATCH 19/25] Ensure dev env files are not accidentally leaked to locally built docker images --- .dockerignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.dockerignore b/.dockerignore index 893aa659..dff3cea1 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,6 +1,6 @@ bin/rr config/autoload/*local* -config/params/*.dist +config/params/shlink_dev_env.* data/infra data/cache/* data/log/* From 4520afb271816bfe464e4dbdd1e3c829027d4f8b Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Thu, 24 Oct 2024 14:08:48 +0200 Subject: [PATCH 20/25] Normalize composer.json scripts with composer capabilities --- composer.json | 52 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/composer.json b/composer.json index 3e2a089d..e1e072a2 100644 --- a/composer.json +++ b/composer.json @@ -114,31 +114,47 @@ ], "cs": "phpcs -s", "cs:fix": "phpcbf", - "stan": "APP_ENV=test php vendor/bin/phpstan analyse", + "stan": ["@putenv APP_ENV=test", "phpstan analyse"], "test": [ "@parallel test:unit test:db", "@parallel test:api test:cli" ], - "test:unit": "COLUMNS=120 vendor/bin/phpunit --order-by=random --testdox --testdox-summary", - "test:unit:ci": "XDEBUG_MODE=coverage composer test:unit -- --coverage-php=build/coverage-unit.cov", - "test:unit:pretty": "XDEBUG_MODE=coverage composer test:unit -- --coverage-html build/coverage-unit/coverage-html", + "test:unit": ["@putenv COLUMNS=120", "phpunit --order-by=random --testdox --testdox-summary"], + "test:unit:ci": ["@putenv XDEBUG_MODE=coverage", "@test:unit --coverage-php=build/coverage-unit.cov"], + "test:unit:pretty": ["@putenv XDEBUG_MODE=coverage", "@test:unit --coverage-html build/coverage-unit/coverage-html"], "test:db": "@parallel test:db:sqlite:ci test:db:mysql test:db:maria test:db:postgres test:db:ms", - "test:db:sqlite": "APP_ENV=test php vendor/bin/phpunit --order-by=random --testdox --testdox-summary -c phpunit-db.xml", - "test:db:sqlite:ci": "XDEBUG_MODE=coverage composer test:db:sqlite -- --coverage-php build/coverage-db.cov", - "test:db:mysql": "DB_DRIVER=mysql composer test:db:sqlite -- $*", - "test:db:maria": "DB_DRIVER=maria composer test:db:sqlite -- $*", - "test:db:postgres": "DB_DRIVER=postgres composer test:db:sqlite -- $*", - "test:db:ms": "DB_DRIVER=mssql composer test:db:sqlite -- $*", + "test:db:sqlite": ["@putenv APP_ENV=test", "phpunit --order-by=random --testdox --testdox-summary -c phpunit-db.xml"], + "test:db:sqlite:ci": ["@putenv XDEBUG_MODE=coverage", "@test:db:sqlite --coverage-php build/coverage-db.cov"], + "test:db:mysql": ["@putenv DB_DRIVER=mysql", "@test:db:sqlite"], + "test:db:maria": ["@putenv DB_DRIVER=maria", "@test:db:sqlite"], + "test:db:postgres": ["@putenv DB_DRIVER=postgres", "@test:db:sqlite"], + "test:db:ms": ["@putenv DB_DRIVER=mssql", "@test:db:sqlite"], "test:api": "bin/test/run-api-tests.sh", - "test:api:sqlite": "DB_DRIVER=sqlite composer test:api -- $*", - "test:api:mysql": "DB_DRIVER=mysql composer test:api -- $*", - "test:api:maria": "DB_DRIVER=maria composer test:api -- $*", - "test:api:mssql": "DB_DRIVER=mssql composer test:api -- $*", - "test:api:ci": "GENERATE_COVERAGE=yes composer test:api && vendor/bin/phpcov merge build/coverage-api --php build/coverage-api.cov && rm build/coverage-api/*.cov", - "test:api:pretty": "GENERATE_COVERAGE=yes composer test:api && vendor/bin/phpcov merge build/coverage-api --html build/coverage-api/coverage-html && rm build/coverage-api/*.cov", + "test:api:sqlite": ["@putenv DB_DRIVER=sqlite", "@test:api"], + "test:api:mysql": ["@putenv DB_DRIVER=mysql", "@test:api"], + "test:api:maria": ["@putenv DB_DRIVER=maria", "@test:api"], + "test:api:mssql": ["@putenv DB_DRIVER=mssql", "@test:api"], + "test:api:ci": [ + "@putenv GENERATE_COVERAGE=yes", + "@test:api", + "phpcov merge build/coverage-api --php build/coverage-api.cov && rm build/coverage-api/*.cov" + ], + "test:api:pretty": [ + "@putenv GENERATE_COVERAGE=yes", + "@test:api", + "phpcov merge build/coverage-api --html build/coverage-api/coverage-html && rm build/coverage-api/*.cov" + ], "test:cli": "bin/test/run-cli-tests.sh", - "test:cli:ci": "GENERATE_COVERAGE=yes composer test:cli && vendor/bin/phpcov merge build/coverage-cli --php build/coverage-cli.cov && rm build/coverage-cli/*.cov", - "test:cli:pretty": "GENERATE_COVERAGE=yes composer test:cli && vendor/bin/phpcov merge build/coverage-cli --html build/coverage-cli/coverage-html && rm build/coverage-cli/*.cov", + "test:cli:ci": [ + "@putenv GENERATE_COVERAGE=yes", + "@test:cli", + "vendor/bin/phpcov merge build/coverage-cli --php build/coverage-cli.cov && rm build/coverage-cli/*.cov" + ], + "test:cli:pretty": [ + "@putenv GENERATE_COVERAGE=yes", + "@test:cli", + "phpcov merge build/coverage-cli --html build/coverage-cli/coverage-html && rm build/coverage-cli/*.cov" + ], "swagger:validate": "php-openapi validate docs/swagger/swagger.json", "swagger:inline": "php-openapi inline docs/swagger/swagger.json docs/swagger/swagger-inlined.json", "clean:dev": "rm -f data/database.sqlite && rm -f config/params/generated_config.php" From 2a734b5d89d7c817880282592f38cde676ccdcb6 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Thu, 24 Oct 2024 14:20:45 +0200 Subject: [PATCH 21/25] Ensure proper env vars are promoted for dev and test envs --- bin/test/run-api-tests.sh | 4 ---- bin/test/run-cli-tests.sh | 5 ----- config/container.php | 7 +++++-- config/test/shlink-test.env | 5 ----- config/test/shlink_test_env.php | 15 +++++++++++++++ 5 files changed, 20 insertions(+), 16 deletions(-) delete mode 100644 config/test/shlink-test.env create mode 100644 config/test/shlink_test_env.php diff --git a/bin/test/run-api-tests.sh b/bin/test/run-api-tests.sh index 4c519a9b..ffc152b7 100755 --- a/bin/test/run-api-tests.sh +++ b/bin/test/run-api-tests.sh @@ -17,12 +17,8 @@ touch $OUTPUT_LOGS # Try to stop server just in case it hanged in last execution [ "$TEST_RUNTIME" = 'rr' ] && bin/rr stop -f -w . -# Resolve .env file absolute path, as it fails to load with relative paths -TESTS_DOTENV="${PWD}/config/test/shlink-test.env" - echo 'Starting server...' [ "$TEST_RUNTIME" = 'rr' ] && bin/rr serve -p -w . -c=config/roadrunner/.rr.test.yml \ - --dotenv "$TESTS_DOTENV" \ -o=logs.output="${PWD}/${OUTPUT_LOGS}" \ -o=logs.channels.http.output="${PWD}/${OUTPUT_LOGS}" \ -o=logs.channels.server.output="${PWD}/${OUTPUT_LOGS}" & diff --git a/bin/test/run-cli-tests.sh b/bin/test/run-cli-tests.sh index 96ea6cfa..60b97fcc 100755 --- a/bin/test/run-cli-tests.sh +++ b/bin/test/run-cli-tests.sh @@ -7,11 +7,6 @@ export GENERATE_COVERAGE="${GENERATE_COVERAGE:-"no"}" [ "$GENERATE_COVERAGE" != 'no' ] && export XDEBUG_MODE=coverage -# Load and export test env vars -set -a -. ./config/test/shlink-test.env -set +a - vendor/bin/phpunit --order-by=random --testdox --testdox-summary -c phpunit-cli.xml $* TESTS_EXIT_CODE=$? diff --git a/config/container.php b/config/container.php index e207fe16..6ac07896 100644 --- a/config/container.php +++ b/config/container.php @@ -15,8 +15,11 @@ chdir(dirname(__DIR__)); require 'vendor/autoload.php'; -// Promote env vars from installer or dev config -loadEnvVarsFromConfig('config/params/*.php', enumValues(EnvVars::class)); +// Promote env vars from installer, dev config or test config +loadEnvVarsFromConfig( + EnvVars::isTestEnv() ? 'config/test/shlink_test_env.php' : 'config/params/*.php', + enumValues(EnvVars::class), +); // This is one of the first files loaded. Configure the timezone and memory limit here ini_set('memory_limit', EnvVars::MEMORY_LIMIT->loadFromEnv()); diff --git a/config/test/shlink-test.env b/config/test/shlink-test.env deleted file mode 100644 index 48af9b6e..00000000 --- a/config/test/shlink-test.env +++ /dev/null @@ -1,5 +0,0 @@ -APP_ENV=test - -# URL shortener -DEFAULT_DOMAIN=s.test -IS_HTTPS_ENABLED=false diff --git a/config/test/shlink_test_env.php b/config/test/shlink_test_env.php new file mode 100644 index 00000000..f32d06db --- /dev/null +++ b/config/test/shlink_test_env.php @@ -0,0 +1,15 @@ +value => 'test', + + // URL shortener + EnvVars::DEFAULT_DOMAIN->value => 's.test', + EnvVars::IS_HTTPS_ENABLED->value => false, + +]; From d7423585ffa0c0c99cecfba2d38d922204dbe3c9 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 26 Oct 2024 10:25:11 +0200 Subject: [PATCH 22/25] Build docker image in CI using reusable workflow --- .github/workflows/ci-docker-image-build.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci-docker-image-build.yml b/.github/workflows/ci-docker-image-build.yml index ab9681c5..b2fe8b54 100644 --- a/.github/workflows/ci-docker-image-build.yml +++ b/.github/workflows/ci-docker-image-build.yml @@ -1,4 +1,4 @@ -name: Build docker image +name: Test docker image build on: pull_request: @@ -7,8 +7,4 @@ on: jobs: build-docker-image: - runs-on: ubuntu-24.04 - steps: - - name: Checkout code - uses: actions/checkout@v4 - - run: docker build -t shlink-docker-image:temp . + uses: shlinkio/github-actions/.github/workflows/docker-image-build-ci.yml@main From 0c3c7ff3b29cc64711871c51563916f47c52ee6e Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sun, 27 Oct 2024 08:23:38 +0100 Subject: [PATCH 23/25] Add v4.2.4 to changelog --- CHANGELOG.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eec09c68..82e3d4e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,13 +4,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com), and this project adheres to [Semantic Versioning](https://semver.org). -## [Unreleased] +## [4.2.4] - 2024-10-27 ### Added * *Nothing* ### Changed -* [#2232](https://github.com/shlinkio/shlink/issues/2232) Run RoadRunner in docker with `exec` to ensure signals are properly handled. * [#2231](https://github.com/shlinkio/shlink/issues/2231) Update to `endroid/qr-code` 6.0. +* [#2221](https://github.com/shlinkio/shlink/issues/2221) Switch to env vars to handle dev/local options. ### Deprecated * *Nothing* @@ -19,7 +19,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com), and this * *Nothing* ### Fixed -* *Nothing* +* [#2232](https://github.com/shlinkio/shlink/issues/2232) Run RoadRunner in docker with `exec` to ensure signals are properly handled. ## [4.2.3] - 2024-10-17 From 99639b9844fdb262c91f1a4de74e5c6c086a1c39 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sun, 27 Oct 2024 08:36:57 +0100 Subject: [PATCH 24/25] Depend on actual versions for shlink packages --- .gitattributes | 1 - .gitignore | 1 - CONTRIBUTING.md | 6 ------ composer.json | 4 ++-- docker-compose.override.yml.dist | 30 ------------------------------ docker-compose.yml | 5 +++++ 6 files changed, 7 insertions(+), 40 deletions(-) delete mode 100644 docker-compose.override.yml.dist diff --git a/.gitattributes b/.gitattributes index 4d66fe58..a34ddbf9 100644 --- a/.gitattributes +++ b/.gitattributes @@ -13,7 +13,6 @@ .travis.yml export-ignore build.sh export-ignore CHANGELOG.md export-ignore -docker-compose.override.yml.dist export-ignore docker-compose.yml export-ignore indocker export-ignore phpcs.xml export-ignore diff --git a/.gitignore b/.gitignore index 04c8ed56..9353d40c 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,5 @@ data/GeoLite2-City.* data/infra/matomo docs/swagger-ui* docs/mercure.html -docker-compose.override.yml .phpunit.result.cache docs/swagger/swagger-inlined.json diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e44c5fee..daf34a63 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -24,12 +24,6 @@ Then you will have to follow these steps: The `shlink_dev_env.php` file is gitignored, so you can customize it as you want. For example, by adding your own GeoLite license key. -* Do the same with the `docker-compose.override.yml.dist`: - - ``` - cp docker-compose.override.yml.dist docker-compose.override.yml - ``` - * Start-up the project by running `docker compose up`. The first time this command is run, it will create several containers that are used during development, so it may take some time. diff --git a/composer.json b/composer.json index e1e072a2..49872457 100644 --- a/composer.json +++ b/composer.json @@ -44,12 +44,12 @@ "pagerfanta/core": "^3.8", "ramsey/uuid": "^4.7", "shlinkio/doctrine-specification": "^2.1.1", - "shlinkio/shlink-common": "dev-main#16d19b7 as 6.4", + "shlinkio/shlink-common": "^6.4", "shlinkio/shlink-config": "^3.3", "shlinkio/shlink-event-dispatcher": "^4.1", "shlinkio/shlink-importer": "^5.3.2", "shlinkio/shlink-installer": "^9.2", - "shlinkio/shlink-ip-geolocation": "dev-main#6f35bd4 as 4.1", + "shlinkio/shlink-ip-geolocation": "^4.1", "shlinkio/shlink-json": "^1.1", "spiral/roadrunner": "^2024.1", "spiral/roadrunner-cli": "^2.6", diff --git a/docker-compose.override.yml.dist b/docker-compose.override.yml.dist deleted file mode 100644 index 85e58554..00000000 --- a/docker-compose.override.yml.dist +++ /dev/null @@ -1,30 +0,0 @@ -services: - shlink_php: - user: 1000:1000 - volumes: - - /etc/passwd:/etc/passwd:ro - - /etc/group:/etc/group:ro - - shlink_roadrunner: - user: 1000:1000 - volumes: - - /etc/passwd:/etc/passwd:ro - - /etc/group:/etc/group:ro - - shlink_db_mysql: - user: 1000:1000 - volumes: - - /etc/passwd:/etc/passwd:ro - - /etc/group:/etc/group:ro - - shlink_db_postgres: - user: 1000:1000 - volumes: - - /etc/passwd:/etc/passwd:ro - - /etc/group:/etc/group:ro - - shlink_db_maria: - user: 1000:1000 - volumes: - - /etc/passwd:/etc/passwd:ro - - /etc/group:/etc/group:ro diff --git a/docker-compose.yml b/docker-compose.yml index 050e6803..2b8d1b56 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,6 +13,7 @@ services: shlink_php: container_name: shlink_php + user: 1000:1000 build: context: . dockerfile: ./data/infra/php.Dockerfile @@ -40,6 +41,7 @@ services: shlink_roadrunner: container_name: shlink_roadrunner + user: 1000:1000 build: context: . dockerfile: ./data/infra/roadrunner.Dockerfile @@ -66,6 +68,7 @@ services: shlink_db_mysql: container_name: shlink_db_mysql + user: 1000:1000 image: mysql:8.0 ports: - "3307:3306" @@ -78,6 +81,7 @@ services: shlink_db_postgres: container_name: shlink_db_postgres + user: 1000:1000 image: postgres:16.3-alpine ports: - "5434:5432" @@ -91,6 +95,7 @@ services: shlink_db_maria: container_name: shlink_db_maria + user: 1000:1000 image: mariadb:10.7 ports: - "3308:3306" From 0d627ce808954e99d94072f622e268a922186790 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sun, 27 Oct 2024 08:44:30 +0100 Subject: [PATCH 25/25] Set user to 0 in database containers when running in CI --- docker-compose.ci.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docker-compose.ci.yml b/docker-compose.ci.yml index 6bc053c3..85a4d6dd 100644 --- a/docker-compose.ci.yml +++ b/docker-compose.ci.yml @@ -1,12 +1,15 @@ services: shlink_db_mysql: + user: '0' environment: MYSQL_DATABASE: shlink_test shlink_db_postgres: + user: '0' environment: POSTGRES_DB: shlink_test shlink_db_maria: + user: '0' environment: MYSQL_DATABASE: shlink_test