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 7575178bc..4f821b7d2 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 33768eda5..006aea91d 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 e2cce68f2..e1acd118d 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 7f8cc1641..77cbaa43c 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 f800cf097..37ec30dfe 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 a7f9b895b..04c8ed564 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 b96cf6719..ffc152b7e 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 8c85e1eba..013f03ee4 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 20732e3fe..4a7904bf1 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 46ad43bbe..ba8b1620a 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 006aea91d..2cb660d2b 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 4f821b7d2..eec09c684 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 013f03ee4..6f8feba49 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 53fb12514..4da8bb329 100644 --- a/module/Core/src/Action/QrCodeAction.php +++ b/module/Core/src/Action/QrCodeAction.php @@ -5,6 +5,7 @@ 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 @@ public function process(Request $request, RequestHandlerInterface $handler): Res } $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 04c8ed564..e539b6097 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 2d203ea1d..000000000 --- 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 abe5dd877..000000000 --- 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 d7369ea73..000000000 --- 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 2a9404071..000000000 --- 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 c50e1f8e7..83abed1a0 100644 --- a/config/autoload/mercure.global.php +++ b/config/autoload/mercure.global.php @@ -8,34 +8,31 @@ 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 13a74022b..000000000 --- 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 23caadf2c..000000000 --- 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 86e2dd6a8..c95323777 100644 --- a/config/autoload/rabbit.global.php +++ b/config/autoload/rabbit.global.php @@ -6,6 +6,7 @@ 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 37faf181c..000000000 --- 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 dbf026db8..000000000 --- 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 7fd57112a..000000000 --- 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 35235d72b..000000000 --- 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 39bd71694..dc67b587f 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 713a209e4..000000000 --- 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 715d28224..000000000 --- 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 41f54cba3..718ba4c13 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 0ee1a3319..fd26a5bc3 100644 --- a/module/CLI/config/dependencies.config.php +++ b/module/CLI/config/dependencies.config.php @@ -7,10 +7,10 @@ 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 0273da71f..fdff2ddc8 100644 --- a/module/CLI/src/Command/ShortUrl/CreateShortUrlCommand.php +++ b/module/CLI/src/Command/ShortUrl/CreateShortUrlCommand.php @@ -6,8 +6,8 @@ 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 ab716f7ea..89cf42718 100644 --- a/module/CLI/src/Factory/ApplicationFactory.php +++ b/module/CLI/src/Factory/ApplicationFactory.php @@ -5,7 +5,7 @@ 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 ff42c930c..85ae1d3a5 100644 --- a/module/CLI/src/GeoLite/GeolocationDbUpdater.php +++ b/module/CLI/src/GeoLite/GeolocationDbUpdater.php @@ -9,7 +9,7 @@ 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 10c2da7c0..2d3bf91ea 100644 --- a/module/CLI/src/Input/ShortUrlDataInput.php +++ b/module/CLI/src/Input/ShortUrlDataInput.php @@ -4,7 +4,7 @@ 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 0bc77acab..32240fc51 100644 --- a/module/CLI/test/Command/Domain/DomainRedirectsCommandTest.php +++ b/module/CLI/test/Command/Domain/DomainRedirectsCommandTest.php @@ -10,10 +10,10 @@ 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 cfa09e183..2621f9401 100644 --- a/module/CLI/test/Command/Domain/ListDomainsCommandTest.php +++ b/module/CLI/test/Command/Domain/ListDomainsCommandTest.php @@ -11,10 +11,10 @@ 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 33031c6bf..86bd87fb6 100644 --- a/module/CLI/test/Command/ShortUrl/CreateShortUrlCommandTest.php +++ b/module/CLI/test/Command/ShortUrl/CreateShortUrlCommandTest.php @@ -12,8 +12,8 @@ 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 83b0fc66a..88b1280b7 100644 --- a/module/CLI/test/Factory/ApplicationFactoryTest.php +++ b/module/CLI/test/Factory/ApplicationFactoryTest.php @@ -8,7 +8,7 @@ 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 cbb4af5c0..3b0f452e1 100644 --- a/module/CLI/test/GeoLite/GeolocationDbUpdaterTest.php +++ b/module/CLI/test/GeoLite/GeolocationDbUpdaterTest.php @@ -14,7 +14,7 @@ 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 8f8b609ba..f50469c0e 100644 --- a/module/Core/config/dependencies.config.php +++ b/module/Core/config/dependencies.config.php @@ -9,7 +9,7 @@ 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 @@ 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 @@ 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 @@ 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 @@ ], 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 @@ 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 @@ 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 @@ 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 f401f255a..2491d6064 100644 --- a/module/Core/config/event_dispatcher.config.php +++ b/module/Core/config/event_dispatcher.config.php @@ -129,14 +129,14 @@ 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 @@ ], 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 2a3907cc6..46d90056e 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\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 4da8bb329..607fab50b 100644 --- a/module/Core/src/Action/QrCodeAction.php +++ b/module/Core/src/Action/QrCodeAction.php @@ -13,8 +13,8 @@ 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 29c2c8d28..21b7d21e5 100644 --- a/module/Core/src/Action/RobotsAction.php +++ b/module/Core/src/Action/RobotsAction.php @@ -9,8 +9,8 @@ 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 ec545352b..42f07642b 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 000000000..97916d36a --- /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 fe99ac7e5..e6ef6a241 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 da130d17b..4d85e6ccd 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_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 @@ public function __construct( 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 000000000..4bc86e34a --- /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 dd9f0a6d6..75faf0977 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 @@ public function __construct( ? $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 000000000..6a8c364ca --- /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 000000000..eddfba345 --- /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 000000000..b815ac0ff --- /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 4138a72e6..4e7360d5a 100644 --- a/module/Core/src/ErrorHandler/NotFoundRedirectHandler.php +++ b/module/Core/src/ErrorHandler/NotFoundRedirectHandler.php @@ -9,15 +9,15 @@ 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 ad4c80700..c348cbbd8 100644 --- a/module/Core/src/EventDispatcher/Helper/EnabledListenerChecker.php +++ b/module/Core/src/EventDispatcher/Helper/EnabledListenerChecker.php @@ -5,9 +5,9 @@ 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 daa7cafb3..b8c18fb63 100644 --- a/module/Core/src/EventDispatcher/RabbitMq/NotifyNewShortUrlToRabbitMq.php +++ b/module/Core/src/EventDispatcher/RabbitMq/NotifyNewShortUrlToRabbitMq.php @@ -7,10 +7,10 @@ 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 ddc4221c7..bb55f1696 100644 --- a/module/Core/src/EventDispatcher/RabbitMq/NotifyVisitToRabbitMq.php +++ b/module/Core/src/EventDispatcher/RabbitMq/NotifyVisitToRabbitMq.php @@ -7,10 +7,10 @@ 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 235993211..af0ace92d 100644 --- a/module/Core/src/Matomo/MatomoOptions.php +++ b/module/Core/src/Matomo/MatomoOptions.php @@ -4,17 +4,31 @@ 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 a645181ba..000000000 --- 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 d42723748..000000000 --- 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 03091d75a..000000000 --- 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 b65381aab..aeb08c47d 100644 --- a/module/Core/src/ShortUrl/DeleteShortUrlService.php +++ b/module/Core/src/ShortUrl/DeleteShortUrlService.php @@ -5,8 +5,8 @@ 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 b428019e1..7f863f6cc 100644 --- a/module/Core/src/ShortUrl/Helper/ShortCodeUniquenessHelper.php +++ b/module/Core/src/ShortUrl/Helper/ShortCodeUniquenessHelper.php @@ -5,7 +5,7 @@ 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 4d801c6fa..375d88370 100644 --- a/module/Core/src/ShortUrl/Helper/ShortUrlRedirectionBuilder.php +++ b/module/Core/src/ShortUrl/Helper/ShortUrlRedirectionBuilder.php @@ -8,7 +8,7 @@ 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 3f9b62256..0950e042f 100644 --- a/module/Core/src/ShortUrl/Helper/ShortUrlTitleResolutionHelper.php +++ b/module/Core/src/ShortUrl/Helper/ShortUrlTitleResolutionHelper.php @@ -8,7 +8,7 @@ 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 7780f91be..b164ffd68 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\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 d3823b27a..0b70c3ae0 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\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 f53360754..c9c85e1b9 100644 --- a/module/Core/src/ShortUrl/Model/ShortUrlCreation.php +++ b/module/Core/src/ShortUrl/Model/ShortUrlCreation.php @@ -5,8 +5,8 @@ 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 2512fc44a..ba4e39599 100644 --- a/module/Core/src/ShortUrl/Model/Validation/CustomSlugFilter.php +++ b/module/Core/src/ShortUrl/Model/Validation/CustomSlugFilter.php @@ -5,7 +5,7 @@ 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 24afc72e4..2bc417b42 100644 --- a/module/Core/src/ShortUrl/Model/Validation/CustomSlugValidator.php +++ b/module/Core/src/ShortUrl/Model/Validation/CustomSlugValidator.php @@ -5,7 +5,7 @@ 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 f4a35969d..3cd7744aa 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\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 3aa6c887e..0b79f97d4 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\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 f84414547..b65392565 100644 --- a/module/Core/src/ShortUrl/ShortUrlListService.php +++ b/module/Core/src/ShortUrl/ShortUrlListService.php @@ -5,7 +5,7 @@ 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 42d274c00..14727ff56 100644 --- a/module/Core/src/ShortUrl/ShortUrlResolver.php +++ b/module/Core/src/ShortUrl/ShortUrlResolver.php @@ -5,8 +5,8 @@ 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 01e581a7a..edb04b8e2 100644 --- a/module/Core/src/Util/RedirectResponseHelper.php +++ b/module/Core/src/Util/RedirectResponseHelper.php @@ -6,7 +6,7 @@ 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 9cf524bcd..ca5d79b2e 100644 --- a/module/Core/src/Visit/Model/Visitor.php +++ b/module/Core/src/Visit/Model/Visitor.php @@ -5,7 +5,7 @@ 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 824e7c244..986e221c3 100644 --- a/module/Core/src/Visit/RequestTracker.php +++ b/module/Core/src/Visit/RequestTracker.php @@ -7,9 +7,9 @@ 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 f2e26493d..85085220e 100644 --- a/module/Core/src/Visit/VisitsTracker.php +++ b/module/Core/src/Visit/VisitsTracker.php @@ -6,8 +6,8 @@ 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 08564bf9c..9afae7932 100644 --- a/module/Core/test/Action/QrCodeActionTest.php +++ b/module/Core/test/Action/QrCodeActionTest.php @@ -16,8 +16,8 @@ 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 1d83fb8cc..38d80de31 100644 --- a/module/Core/test/Action/RobotsActionTest.php +++ b/module/Core/test/Action/RobotsActionTest.php @@ -10,8 +10,8 @@ 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 5ef5db2b4..75df09482 100644 --- a/module/Core/test/Config/NotFoundRedirectResolverTest.php +++ b/module/Core/test/Config/NotFoundRedirectResolverTest.php @@ -18,8 +18,8 @@ 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 53642f3a2..25d9952f2 100644 --- a/module/Core/test/ErrorHandler/NotFoundRedirectHandlerTest.php +++ b/module/Core/test/ErrorHandler/NotFoundRedirectHandlerTest.php @@ -16,11 +16,11 @@ 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 cebde4374..180ab48d9 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\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\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 fc44fd874..b0c5a0e0e 100644 --- a/module/Core/test/EventDispatcher/RabbitMq/NotifyNewShortUrlToRabbitMqTest.php +++ b/module/Core/test/EventDispatcher/RabbitMq/NotifyNewShortUrlToRabbitMqTest.php @@ -15,11 +15,11 @@ 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 2251d3011..ac744824a 100644 --- a/module/Core/test/EventDispatcher/RabbitMq/NotifyVisitToRabbitMqTest.php +++ b/module/Core/test/EventDispatcher/RabbitMq/NotifyVisitToRabbitMqTest.php @@ -16,10 +16,10 @@ 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 4788818e8..faddafeb0 100644 --- a/module/Core/test/ShortUrl/DeleteShortUrlServiceTest.php +++ b/module/Core/test/ShortUrl/DeleteShortUrlServiceTest.php @@ -9,8 +9,8 @@ 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 be5b41018..b37202545 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\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 efbbde21f..de1402f64 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\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 afe83c8d8..d1283a78b 100644 --- a/module/Core/test/ShortUrl/Helper/ShortUrlRedirectionBuilderTest.php +++ b/module/Core/test/ShortUrl/Helper/ShortUrlRedirectionBuilderTest.php @@ -12,7 +12,7 @@ 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 4e3056b3f..b5b8e00c2 100644 --- a/module/Core/test/ShortUrl/Helper/ShortUrlTitleResolutionHelperTest.php +++ b/module/Core/test/ShortUrl/Helper/ShortUrlTitleResolutionHelperTest.php @@ -16,7 +16,7 @@ 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 3965fe189..6815acb65 100644 --- a/module/Core/test/ShortUrl/Middleware/ExtraPathRedirectMiddlewareTest.php +++ b/module/Core/test/ShortUrl/Middleware/ExtraPathRedirectMiddlewareTest.php @@ -16,9 +16,9 @@ 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 b05ab7d9e..c37c81a5f 100644 --- a/module/Core/test/ShortUrl/Middleware/TrimTrailingSlashMiddlewareTest.php +++ b/module/Core/test/ShortUrl/Middleware/TrimTrailingSlashMiddlewareTest.php @@ -13,7 +13,7 @@ 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 b84b5b272..e963923b8 100644 --- a/module/Core/test/ShortUrl/Model/ShortUrlCreationTest.php +++ b/module/Core/test/ShortUrl/Model/ShortUrlCreationTest.php @@ -8,8 +8,8 @@ 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 290fe63d1..86f695c7d 100644 --- a/module/Core/test/ShortUrl/Model/Validation/CustomSlugValidatorTest.php +++ b/module/Core/test/ShortUrl/Model/Validation/CustomSlugValidatorTest.php @@ -7,7 +7,7 @@ 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 43860909a..d62237342 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\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 a469ed608..d86637614 100644 --- a/module/Core/test/ShortUrl/ShortUrlListServiceTest.php +++ b/module/Core/test/ShortUrl/ShortUrlListServiceTest.php @@ -8,7 +8,7 @@ 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 1b3aa564a..e8443a13e 100644 --- a/module/Core/test/ShortUrl/ShortUrlResolverTest.php +++ b/module/Core/test/ShortUrl/ShortUrlResolverTest.php @@ -12,8 +12,8 @@ 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 63e55e2e2..89d3fa5ae 100644 --- a/module/Core/test/Util/RedirectResponseHelperTest.php +++ b/module/Core/test/Util/RedirectResponseHelperTest.php @@ -8,7 +8,7 @@ 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 74065d7dc..04e57179a 100644 --- a/module/Core/test/Visit/Model/VisitorTest.php +++ b/module/Core/test/Visit/Model/VisitorTest.php @@ -7,7 +7,7 @@ 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 c8746f91c..ae0a74c4e 100644 --- a/module/Core/test/Visit/RequestTrackerTest.php +++ b/module/Core/test/Visit/RequestTrackerTest.php @@ -13,8 +13,8 @@ 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 d6cbf4bff..414f5254a 100644 --- a/module/Core/test/Visit/VisitsTrackerTest.php +++ b/module/Core/test/Visit/VisitsTrackerTest.php @@ -10,8 +10,8 @@ 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 d334d5b04..3d7a66a48 100644 --- a/module/Rest/config/dependencies.config.php +++ b/module/Rest/config/dependencies.config.php @@ -10,8 +10,8 @@ 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 @@ 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 @@ 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 e50ada168..d156a7202 100644 --- a/module/Rest/src/Action/Domain/ListDomainsAction.php +++ b/module/Rest/src/Action/Domain/ListDomainsAction.php @@ -8,8 +8,8 @@ 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 809bf4d18..ce7313309 100644 --- a/module/Rest/src/Action/HealthAction.php +++ b/module/Rest/src/Action/HealthAction.php @@ -8,7 +8,7 @@ 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 65f47d95d..75d5480b5 100644 --- a/module/Rest/src/Action/ShortUrl/AbstractCreateShortUrlAction.php +++ b/module/Rest/src/Action/ShortUrl/AbstractCreateShortUrlAction.php @@ -7,8 +7,8 @@ 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 41906539e..4864048cf 100644 --- a/module/Rest/test/Action/Domain/ListDomainsActionTest.php +++ b/module/Rest/test/Action/Domain/ListDomainsActionTest.php @@ -10,10 +10,10 @@ 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 89b93b804..292c17484 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\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 590756461..aae90f49a 100644 --- a/module/Rest/test/Action/HealthActionTest.php +++ b/module/Rest/test/Action/HealthActionTest.php @@ -14,7 +14,7 @@ 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 7361df5c2..06cd5554f 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\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 69914f0e7..d28c3b492 100644 --- a/module/Rest/test/Action/ShortUrl/SingleStepCreateShortUrlActionTest.php +++ b/module/Rest/test/Action/ShortUrl/SingleStepCreateShortUrlActionTest.php @@ -8,7 +8,7 @@ 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 000000000..02c2aa925 --- /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 e539b6097..96ac67191 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 718ba4c13..b9fbd210d 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 02c2aa925..5ab21ea16 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 f7c6ce073..da3d17784 100644 --- a/config/autoload/routes.config.php +++ b/config/autoload/routes.config.php @@ -19,8 +19,6 @@ 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 98b07ea24..000000000 --- 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 fd26a5bc3..2f098998c 100644 --- a/module/CLI/config/dependencies.config.php +++ b/module/CLI/config/dependencies.config.php @@ -88,7 +88,7 @@ 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 76787451a..bfcf11a3d 100644 --- a/module/CLI/src/ApiKey/RoleResolver.php +++ b/module/CLI/src/ApiKey/RoleResolver.php @@ -5,6 +5,7 @@ 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 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 @@ public function determineRoles(InputInterface $input): iterable 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 cbd4f0fa1..d1f696f56 100644 --- a/module/CLI/test/ApiKey/RoleResolverTest.php +++ b/module/CLI/test/ApiKey/RoleResolverTest.php @@ -10,6 +10,7 @@ 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 6016da1c1..176800abe 100644 --- a/module/CLI/test/Command/ShortUrl/ListShortUrlsCommandTest.php +++ b/module/CLI/test/Command/ShortUrl/ListShortUrlsCommandTest.php @@ -37,7 +37,7 @@ protected function setUp(): void { $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 f50469c0e..420d8a395 100644 --- a/module/Core/config/dependencies.config.php +++ b/module/Core/config/dependencies.config.php @@ -167,7 +167,7 @@ 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 @@ 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 585f78b6b..c4078890b 100644 --- a/module/Core/src/Config/PostProcessor/MultiSegmentSlugProcessor.php +++ b/module/Core/src/Config/PostProcessor/MultiSegmentSlugProcessor.php @@ -4,6 +4,8 @@ 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 93adbf5f1..382ca08ea 100644 --- a/module/Core/src/Domain/DomainService.php +++ b/module/Core/src/Domain/DomainService.php @@ -7,6 +7,7 @@ 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 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 @@ public function listDomains(?ApiKey $apiKey = null): array } return [ - DomainItem::forDefaultDomain($this->defaultDomain, $default ?? new EmptyNotFoundRedirectConfig()), + DomainItem::forDefaultDomain( + $this->urlShortenerOptions->defaultDomain(), + $default ?? new EmptyNotFoundRedirectConfig(), + ), ...$mappedDomains, ]; } @@ -52,7 +56,7 @@ private function defaultDomainAndRest(?ApiKey $apiKey): array $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 886a4d25d..540b444c0 100644 --- a/module/Core/src/ShortUrl/Helper/ShortUrlStringifier.php +++ b/module/Core/src/ShortUrl/Helper/ShortUrlStringifier.php @@ -5,22 +5,23 @@ 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 @@ public function stringify(ShortUrl $shortUrl): string 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 9afae7932..d040b7d7c 100644 --- a/module/Core/test/Action/QrCodeActionTest.php +++ b/module/Core/test/Action/QrCodeActionTest.php @@ -17,6 +17,7 @@ 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 @@ public function action(?QrCodeOptions $options = null): QrCodeAction { 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 c67597ec8..82a256547 100644 --- a/module/Core/test/Domain/DomainServiceTest.php +++ b/module/Core/test/Domain/DomainServiceTest.php @@ -11,6 +11,7 @@ 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 bd3c82c83..1ea76bf67 100644 --- a/module/Core/test/EventDispatcher/PublishingUpdatesGeneratorTest.php +++ b/module/Core/test/EventDispatcher/PublishingUpdatesGeneratorTest.php @@ -31,7 +31,7 @@ protected function setUp(): void 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 816c8eeaf..1794973b0 100644 --- a/module/Core/test/Matomo/MatomoVisitSenderTest.php +++ b/module/Core/test/Matomo/MatomoVisitSenderTest.php @@ -11,6 +11,7 @@ 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 @@ protected function setUp(): void $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 e74d61829..423795318 100644 --- a/module/Core/test/ShortUrl/Helper/ShortUrlStringifierTest.php +++ b/module/Core/test/ShortUrl/Helper/ShortUrlStringifierTest.php @@ -7,6 +7,7 @@ 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 349392dbd..9ff014752 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 3d7a66a48..b69cf36d6 100644 --- a/module/Rest/config/dependencies.config.php +++ b/module/Rest/config/dependencies.config.php @@ -86,7 +86,7 @@ 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 @@ ], 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 91f071b5f..31b8add4e 100644 --- a/module/Rest/src/Action/Visit/DomainVisitsAction.php +++ b/module/Rest/src/Action/Visit/DomainVisitsAction.php @@ -8,6 +8,7 @@ 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 @@ public function handle(Request $request): Response 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 4b7779c21..0d7d947e4 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\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 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface /** @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 73a6ac696..f50341d2e 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\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 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface 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 dd7a0e14f..669037d1b 100644 --- a/module/Rest/test/Action/ShortUrl/EditShortUrlActionTest.php +++ b/module/Rest/test/Action/ShortUrl/EditShortUrlActionTest.php @@ -25,7 +25,7 @@ protected function setUp(): void { $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 d372d6929..071fb8441 100644 --- a/module/Rest/test/Action/ShortUrl/ListShortUrlsActionTest.php +++ b/module/Rest/test/Action/ShortUrl/ListShortUrlsActionTest.php @@ -13,6 +13,7 @@ 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 @@ protected function setUp(): void $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 0576fd525..13b9e35d2 100644 --- a/module/Rest/test/Action/ShortUrl/ResolveShortUrlActionTest.php +++ b/module/Rest/test/Action/ShortUrl/ResolveShortUrlActionTest.php @@ -25,7 +25,7 @@ protected function setUp(): void { $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 a6d64bd2b..3fb4c498d 100644 --- a/module/Rest/test/Action/Visit/DomainVisitsActionTest.php +++ b/module/Rest/test/Action/Visit/DomainVisitsActionTest.php @@ -11,6 +11,7 @@ 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 e6b0c6ec2..db579451c 100644 --- a/module/Rest/test/Middleware/ShortUrl/DefaultShortCodesLengthMiddlewareTest.php +++ b/module/Rest/test/Middleware/ShortUrl/DefaultShortCodesLengthMiddlewareTest.php @@ -13,6 +13,7 @@ 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 e9fceea50..530e28ca0 100644 --- a/module/Rest/test/Middleware/ShortUrl/DropDefaultDomainFromRequestMiddlewareTest.php +++ b/module/Rest/test/Middleware/ShortUrl/DropDefaultDomainFromRequestMiddlewareTest.php @@ -13,6 +13,7 @@ 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 | 38 ++++++++++--------- 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/data/infra/roadrunner.Dockerfile b/data/infra/roadrunner.Dockerfile index 2cb660d2b..8c412350f 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 b9fbd210d..2052b1cfd 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 d811a2e24..487228ec2 100644 --- a/module/Core/test/Config/PostProcessor/MultiSegmentSlugProcessorTest.php +++ b/module/Core/test/Config/PostProcessor/MultiSegmentSlugProcessorTest.php @@ -7,8 +7,11 @@ 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 @@ protected function setUp(): void $this->processor = new MultiSegmentSlugProcessor(); } + protected function tearDown(): void + { + putenv(EnvVars::MULTI_SEGMENT_SLUGS_ENABLED->value); + } + #[Test, DataProvider('provideConfigs')] - public function parsesRoutesAsExpected(array $config, array $expectedRoutes): void + public function parsesRoutesAsExpected(bool $multiSegmentEnabled, array $routes, array $expectedRoutes): void { - self::assertEquals($expectedRoutes, ($this->processor)($config)['routes'] ?? []); + 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 e3aff686c..db2d21f01 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 ffc152b7e..4c519a9b5 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 000000000..d0eac32a0 --- /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 aad5e9d0a..bd6771e69 100644 --- a/config/test/test_config.global.php +++ b/config/test/test_config.global.php @@ -93,13 +93,6 @@ 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 @@ ], ], - // 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 8c412350f..28d2d3438 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 2052b1cfd..05132dfbd 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 bfcf11a3d..ece56c774 100644 --- a/module/CLI/src/ApiKey/RoleResolver.php +++ b/module/CLI/src/ApiKey/RoleResolver.php @@ -40,7 +40,7 @@ public function determineRoles(InputInterface $input): iterable 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 d1f696f56..184780d7d 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 86bd87fb6..19c574819 100644 --- a/module/CLI/test/Command/ShortUrl/CreateShortUrlCommandTest.php +++ b/module/CLI/test/Command/ShortUrl/CreateShortUrlCommandTest.php @@ -37,10 +37,7 @@ protected function setUp(): void $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 b815ac0ff..98b450b4d 100644 --- a/module/Core/src/Config/Options/UrlShortenerOptions.php +++ b/module/Core/src/Config/Options/UrlShortenerOptions.php @@ -15,10 +15,11 @@ 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 @@ public static function fromEnv(): self $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 @@ public function isLooseMode(): bool { 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 382ca08ea..e514af55a 100644 --- a/module/Core/src/Domain/DomainService.php +++ b/module/Core/src/Domain/DomainService.php @@ -37,7 +37,7 @@ public function listDomains(?ApiKey $apiKey = null): array return [ DomainItem::forDefaultDomain( - $this->urlShortenerOptions->defaultDomain(), + $this->urlShortenerOptions->defaultDomain, $default ?? new EmptyNotFoundRedirectConfig(), ), ...$mappedDomains, @@ -56,7 +56,7 @@ private function defaultDomainAndRest(?ApiKey $apiKey): array $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 540b444c0..6659bc0c0 100644 --- a/module/Core/src/ShortUrl/Helper/ShortUrlStringifier.php +++ b/module/Core/src/ShortUrl/Helper/ShortUrlStringifier.php @@ -20,8 +20,7 @@ public function __construct( 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 @@ public function stringify(ShortUrl $shortUrl): string 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 0b79f97d4..94fb314a3 100644 --- a/module/Core/src/ShortUrl/Resolver/PersistenceShortUrlRelationResolver.php +++ b/module/Core/src/ShortUrl/Resolver/PersistenceShortUrlRelationResolver.php @@ -40,7 +40,7 @@ public function __construct( 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 b65392565..853a40b93 100644 --- a/module/Core/src/ShortUrl/ShortUrlListService.php +++ b/module/Core/src/ShortUrl/ShortUrlListService.php @@ -24,7 +24,7 @@ public function __construct( */ 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 d040b7d7c..5e499403c 100644 --- a/module/Core/test/Action/QrCodeActionTest.php +++ b/module/Core/test/Action/QrCodeActionTest.php @@ -17,7 +17,6 @@ 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 82a256547..7e2fea189 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 1794973b0..6a4659f19 100644 --- a/module/Core/test/Matomo/MatomoVisitSenderTest.php +++ b/module/Core/test/Matomo/MatomoVisitSenderTest.php @@ -37,7 +37,7 @@ protected function setUp(): void $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 423795318..d28fdf0e6 100644 --- a/module/Core/test/ShortUrl/Helper/ShortUrlStringifierTest.php +++ b/module/Core/test/ShortUrl/Helper/ShortUrlStringifierTest.php @@ -14,14 +14,18 @@ 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 @@ public static function provideConfigAndShortUrls(): iterable ]), ); - 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 d62237342..722ac347e 100644 --- a/module/Core/test/ShortUrl/Resolver/PersistenceShortUrlRelationResolverTest.php +++ b/module/Core/test/ShortUrl/Resolver/PersistenceShortUrlRelationResolverTest.php @@ -29,9 +29,7 @@ protected function setUp(): void $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 31b8add4e..4d5342028 100644 --- a/module/Rest/src/Action/Visit/DomainVisitsAction.php +++ b/module/Rest/src/Action/Visit/DomainVisitsAction.php @@ -38,7 +38,7 @@ public function handle(Request $request): Response 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 f50341d2e..acf33603c 100644 --- a/module/Rest/src/Middleware/ShortUrl/DropDefaultDomainFromRequestMiddleware.php +++ b/module/Rest/src/Middleware/ShortUrl/DropDefaultDomainFromRequestMiddleware.php @@ -28,7 +28,7 @@ public function process(ServerRequestInterface $request, RequestHandlerInterface 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 071fb8441..a3beba1b0 100644 --- a/module/Rest/test/Action/ShortUrl/ListShortUrlsActionTest.php +++ b/module/Rest/test/Action/ShortUrl/ListShortUrlsActionTest.php @@ -31,10 +31,7 @@ protected function setUp(): void $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 3fb4c498d..d60dae2eb 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 530e28ca0..b82354982 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 5ab21ea16..1bd586369 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 000000000..c97f87052 --- /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 6f8feba49..2fe653e0a 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 d0eac32a0..a7126e10f 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 4251b3e41..3a1d5bcc1 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 1a83fda62..000000000 --- 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 0b7ec9376..9590e5446 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 14633a61f..000000000 --- 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 c7db57f19..0ae55ce3a 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 f29c74b05..000000000 --- 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 469171cae..0a99d3235 100644 --- a/config/autoload/dependencies.global.php +++ b/config/autoload/dependencies.global.php @@ -11,6 +11,7 @@ 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 @@ '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 d9569029c..000000000 --- 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 c7d7d7576..56ba42bba 100644 --- a/config/autoload/logger.global.php +++ b/config/autoload/logger.global.php @@ -14,23 +14,33 @@ 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 fe2c8c54b..000000000 --- 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 db389f3a2..0464ca834 100644 --- a/config/autoload/router.global.php +++ b/config/autoload/router.global.php @@ -13,7 +13,7 @@ '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 dc67b587f..000000000 --- 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 40f88ea3e..92c69ba03 100644 --- a/config/config.php +++ b/config/config.php @@ -8,10 +8,7 @@ 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 @@ 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 2d5d6a067..000000000 --- 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 2fe5dfd9e..6cdf62974 100644 --- a/module/Core/src/Config/EnvVars.php +++ b/module/Core/src/Config/EnvVars.php @@ -27,6 +27,7 @@ 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 @@ private function loadFromFileEnv(): string|int|bool|null 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 @@ public function existsInEnv(): bool { 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 1bd586369..ee6173f23 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 3a1d5bcc1..e6e94734e 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 7b77295fc..6786c492e 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 9590e5446..000000000 --- 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 420d8a395..552d5e2a3 100644 --- a/module/Core/config/dependencies.config.php +++ b/module/Core/config/dependencies.config.php @@ -8,7 +8,6 @@ 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 @@ 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 42f07642b..71e3f507c 100644 --- a/module/Core/src/Config/Options/AppOptions.php +++ b/module/Core/src/Config/Options/AppOptions.php @@ -4,12 +4,20 @@ 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 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 2fe653e0a..4c66c67f1 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 db2d21f01..893aa6593 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 96ac67191..04c8ed564 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 4c66c67f1..50633ca1e 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 3a1e33553..e207fe16c 100644 --- a/config/container.php +++ b/config/container.php @@ -15,8 +15,8 @@ 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 d6b7ef32c..36565258b 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 000000000..2a3905dc1 --- /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 28d2d3438..0bf251f66 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 05132dfbd..e5a34e80f 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 ee6173f23..000000000 --- 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 e5a34e80f..050e68039 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 4ee94c70e..e44c5feea 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 000000000..004c2fce5 --- /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 bafb80b52..fa0e72154 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 c97f87052..96ea6cfaa 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 50633ca1e..3e2a089d8 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 a7126e10f..48af9b6e0 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 893aa6593..dff3cea10 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 3e2a089d8..e1e072a2b 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 4c519a9b5..ffc152b7e 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 96ea6cfaa..60b97fccd 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 e207fe16c..6ac078961 100644 --- a/config/container.php +++ b/config/container.php @@ -15,8 +15,11 @@ 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 48af9b6e0..000000000 --- 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 000000000..f32d06dba --- /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 ab9681c50..b2fe8b54e 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 eec09c684..82e3d4e06 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 4d66fe589..a34ddbf9b 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 04c8ed564..9353d40c9 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 e44c5feea..daf34a63a 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 e1e072a2b..498724572 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 85e58554a..000000000 --- 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 050e68039..2b8d1b562 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 6bc053c33..85a4d6dd2 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