From 1c54d3f0b3bf417fb388fcf10e4a3bdb01251b9f Mon Sep 17 00:00:00 2001 From: turegjorup Date: Sun, 8 Nov 2026 21:10:13 +0100 Subject: [PATCH 1/5] chore: update docker compose template to itk-version 3.2.4 Restore the APP_PATH_PREFIX rewrite in nginx and the php-cs-fixer exclusions. Remove the JavaScript and styles workflows, because the project has no assets. Give the cron-metrics router the lowest priority on the server, because the importer uses the same domain. --- .docker/data/.gitignore | 4 +-- .docker/nginx.conf | 4 +-- .docker/templates/default.conf.template | 17 ++++++++--- .github/workflows/api-spec.yml | 4 +-- .github/workflows/changelog.yaml | 6 ++-- .github/workflows/composer.yaml | 13 ++++---- .github/workflows/markdown.yaml | 5 ++-- .github/workflows/php.yaml | 8 ++++- .github/workflows/pr.yaml | 12 ++++---- .github/workflows/twig.yaml | 10 +++++-- .github/workflows/yaml.yaml | 6 +++- .markdownlintignore | 2 ++ .php-cs-fixer.dist.php | 5 ++-- .prettierrc.yaml | 7 +++++ Taskfile.yml | 40 ++++++++++++------------- docker-compose.dev.yml | 16 +++++----- docker-compose.override.yml | 1 - docker-compose.redirect.yml | 14 ++++----- docker-compose.server.override.yml | 3 +- docker-compose.server.yml | 18 ++++++----- docker-compose.yml | 20 ++++++++----- 21 files changed, 127 insertions(+), 88 deletions(-) diff --git a/.docker/data/.gitignore b/.docker/data/.gitignore index 80a9dc9..4ce1020 100644 --- a/.docker/data/.gitignore +++ b/.docker/data/.gitignore @@ -1,5 +1,5 @@ # Ignore everything in this directory * -# Except this file +# Except !.gitignore -!Readme.md +!README.md diff --git a/.docker/nginx.conf b/.docker/nginx.conf index 8fe03db..ec278a5 100644 --- a/.docker/nginx.conf +++ b/.docker/nginx.conf @@ -17,9 +17,7 @@ http { include /etc/nginx/mime.types; default_type application/octet-stream; - set_real_ip_from 172.16.0.0/16; - real_ip_recursive on; - real_ip_header X-Forwarded-For; + # Note: set_real_ip_from is set in the server block log_format main '$http_x_real_ip - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' diff --git a/.docker/templates/default.conf.template b/.docker/templates/default.conf.template index 36f49cb..9efa01c 100644 --- a/.docker/templates/default.conf.template +++ b/.docker/templates/default.conf.template @@ -6,15 +6,24 @@ server { client_max_body_size ${NGINX_MAX_BODY_SIZE}; - # This also needs to be set in the single server tag and not only in http. set_real_ip_from 172.16.0.0/16; + set_real_ip_from 192.168.39.0/24; real_ip_recursive on; real_ip_header X-Forwarded-For; + location = /cron-metrics { + # Proxy to supercronic metrics + proxy_pass http://${NGINX_CRON_METRICS}/metrics; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + location / { - # We are co-hosting with the legacy EventDB, so this app is hosted under '/api/v2/' (.env::APP_PATH_PREFIX) - # This is configured for Symfony in both 'api_platform.prefix' and 'framework.assets.base_path'. However - # Symfony still installs assets in the root path so we need to rewrite the uri to remove '/api/v2/'. + # This app shares the domain with event-database-imports (admin). This app is under '/api/v2/' (.env::APP_PATH_PREFIX). + # Symfony uses the prefix in 'api_platform.prefix' and 'framework.assets.base_path'. But Symfony installs + # the assets in the root path. Thus, remove '/api/v2/' from the URI. rewrite ^${APP_PATH_PREFIX}/(.*) /$1 break; # try to serve file directly, fallback to index.php diff --git a/.github/workflows/api-spec.yml b/.github/workflows/api-spec.yml index 7dd51eb..b53a1d8 100644 --- a/.github/workflows/api-spec.yml +++ b/.github/workflows/api-spec.yml @@ -31,8 +31,8 @@ jobs: uses: actions/cache@v5 with: path: vendor - key: vendor-php8.4-${{ hashFiles('composer.lock') }} - restore-keys: vendor-php8.4- + key: vendor-php8.5-${{ hashFiles('composer.lock') }} + restore-keys: vendor-php8.5- - name: Install dependencies run: | diff --git a/.github/workflows/changelog.yaml b/.github/workflows/changelog.yaml index 71da908..54b9acb 100644 --- a/.github/workflows/changelog.yaml +++ b/.github/workflows/changelog.yaml @@ -14,8 +14,6 @@ on: jobs: changelog: runs-on: ubuntu-latest - strategy: - fail-fast: false steps: - name: Checkout uses: actions/checkout@v7 @@ -26,4 +24,6 @@ jobs: run: git fetch - name: Check that changelog has been updated. - run: git diff --exit-code origin/${{ github.base_ref }} -- CHANGELOG.md && exit 1 || exit 0 + env: + BASE_REF: ${{ github.base_ref }} + run: git diff --exit-code "origin/$BASE_REF" -- CHANGELOG.md && exit 1 || exit 0 diff --git a/.github/workflows/composer.yaml b/.github/workflows/composer.yaml index def97d2..b50dadb 100644 --- a/.github/workflows/composer.yaml +++ b/.github/workflows/composer.yaml @@ -26,20 +26,23 @@ name: Composer env: - COMPOSE_USER: root + COMPOSE_USER: runner on: pull_request: + paths: &paths + - "composer.json" + - "composer.lock" + - "docker-compose.yml" push: branches: - main - develop + paths: *paths jobs: composer-validate: runs-on: ubuntu-latest - strategy: - fail-fast: false steps: - uses: actions/checkout@v7 @@ -52,8 +55,6 @@ jobs: composer-normalized: runs-on: ubuntu-latest - strategy: - fail-fast: false steps: - uses: actions/checkout@v7 @@ -67,8 +68,6 @@ jobs: composer-audit: runs-on: ubuntu-latest - strategy: - fail-fast: false steps: - uses: actions/checkout@v7 diff --git a/.github/workflows/markdown.yaml b/.github/workflows/markdown.yaml index b0f74c5..ac0fc21 100644 --- a/.github/workflows/markdown.yaml +++ b/.github/workflows/markdown.yaml @@ -22,16 +22,17 @@ name: Markdown on: pull_request: + paths: &paths + - "**/*.md" push: branches: - main - develop + paths: *paths jobs: markdown-lint: runs-on: ubuntu-latest - strategy: - fail-fast: false steps: - name: Checkout uses: actions/checkout@v7 diff --git a/.github/workflows/php.yaml b/.github/workflows/php.yaml index 5188018..6326d8e 100644 --- a/.github/workflows/php.yaml +++ b/.github/workflows/php.yaml @@ -34,14 +34,20 @@ name: Symfony PHP env: - COMPOSE_USER: root + COMPOSE_USER: runner on: pull_request: + paths: &paths + - "**/*.php" + - "composer.json" + - "composer.lock" + - "docker-compose.yml" push: branches: - main - develop + paths: *paths jobs: coding-standards: diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index b83d147..b43bd59 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -17,8 +17,8 @@ jobs: uses: actions/cache@v5 with: path: vendor - key: vendor-php8.4-${{ hashFiles('composer.lock') }} - restore-keys: vendor-php8.4- + key: vendor-php8.5-${{ hashFiles('composer.lock') }} + restore-keys: vendor-php8.5- - name: Start docker compose setup and install site run: | @@ -57,8 +57,8 @@ jobs: uses: actions/cache@v5 with: path: vendor - key: vendor-php8.4-${{ hashFiles('composer.lock') }} - restore-keys: vendor-php8.4- + key: vendor-php8.5-${{ hashFiles('composer.lock') }} + restore-keys: vendor-php8.5- - name: Install dependencies run: | @@ -81,8 +81,8 @@ jobs: uses: actions/cache@v5 with: path: vendor - key: vendor-php8.4-${{ hashFiles('composer.lock') }} - restore-keys: vendor-php8.4- + key: vendor-php8.5-${{ hashFiles('composer.lock') }} + restore-keys: vendor-php8.5- - name: Install dependencies run: | diff --git a/.github/workflows/twig.yaml b/.github/workflows/twig.yaml index 047d881..ffb7055 100644 --- a/.github/workflows/twig.yaml +++ b/.github/workflows/twig.yaml @@ -24,20 +24,24 @@ name: Twig env: - COMPOSE_USER: root + COMPOSE_USER: runner on: pull_request: + paths: &paths + - "**/*.twig" + - "composer.json" + - "composer.lock" + - "docker-compose.yml" push: branches: - main - develop + paths: *paths jobs: twig-lint: runs-on: ubuntu-latest - strategy: - fail-fast: false steps: - name: Checkout uses: actions/checkout@v7 diff --git a/.github/workflows/yaml.yaml b/.github/workflows/yaml.yaml index 80d0a23..c54cf8e 100644 --- a/.github/workflows/yaml.yaml +++ b/.github/workflows/yaml.yaml @@ -22,10 +22,14 @@ name: YAML on: pull_request: + paths: &paths + - "**/*.yml" + - "**/*.yaml" push: branches: - main - develop + paths: *paths jobs: yaml-lint: @@ -38,4 +42,4 @@ jobs: docker network create frontend - run: | - docker compose run --rm prettier '**/*.{yml,yaml}' --check + docker compose run --rm prettier --no-editorconfig '**/*.{yml,yaml}' --check diff --git a/.markdownlintignore b/.markdownlintignore index d143ace..14323f9 100644 --- a/.markdownlintignore +++ b/.markdownlintignore @@ -5,6 +5,8 @@ vendor/ node_modules/ LICENSE.md +# Python +.venv/ # Drupal web/*.md web/core/ diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index e9d23a8..18f6586 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -1,4 +1,5 @@ setFinder($finder); $config->setRules([ - '@Symfony' => true, - 'phpdoc_align' => false, + '@Symfony' => true, + 'phpdoc_align' => false, ]); return $config; diff --git a/.prettierrc.yaml b/.prettierrc.yaml index 12e0898..b1d7be1 100644 --- a/.prettierrc.yaml +++ b/.prettierrc.yaml @@ -3,6 +3,13 @@ # https://prettier.io/docs/configuration overrides: + # https://taskfile.dev/docs/styleguide + - files: + - "Taskfile.{yml,yaml}" + options: + tabWidth: 2 + singleQuote: true + # Symfony config - files: - "config/**/*.{yml,yaml}" diff --git a/Taskfile.yml b/Taskfile.yml index b63cd08..b437714 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -1,9 +1,9 @@ # https://taskfile.dev -version: "3" +version: '3' # https://taskfile.dev/usage/#env-files -dotenv: [".env.local", ".env"] +dotenv: ['.env.local', '.env'] vars: # https://taskfile.dev/reference/templating/ @@ -57,20 +57,20 @@ tasks: COMPOSE_ARGS: exec phpfpm bin/console app:fixtures:load {{.ITEM}} --url=file:///app/tests/resources/{{.ITEM}}.json composer: - desc: "Run `composer` command. Example: task composer -- normalize" + desc: 'Run `composer` command. Example: task composer -- normalize' cmds: - task: compose vars: COMPOSE_ARGS: exec phpfpm composer {{.COMPOSER_ARGS}} compose: - desc: "Run `docker compose` command. Example: task compose -- ps" + desc: 'Run `docker compose` command. Example: task compose -- ps' cmds: # Run docker compose with both arguments passed via var plus any cli args. - - "{{.DOCKER_COMPOSE}} {{.COMPOSE_ARGS}} {{.CLI_ARGS}}" + - '{{.DOCKER_COMPOSE}} {{.COMPOSE_ARGS}} {{.CLI_ARGS}}' console: - desc: "Run Symfony console command. Example: task console -- cache:clear" + desc: 'Run Symfony console command. Example: task console -- cache:clear' cmds: # Run docker compose with both arguments passed via var plus any cli args. - task: compose @@ -78,7 +78,7 @@ tasks: COMPOSE_ARGS: exec phpfpm bin/console {{.CONSOLE_ARGS}} coding-standards:apply: - desc: "Apply coding standards" + desc: 'Apply coding standards' cmds: - task: coding-standards:markdown:apply - task: coding-standards:php:apply @@ -87,7 +87,7 @@ tasks: silent: true coding-standards:check: - desc: "Apply coding standards" + desc: 'Apply coding standards' cmds: - task: coding-standards:markdown:check - task: coding-standards:php:check @@ -96,14 +96,14 @@ tasks: silent: true coding-standards:markdown:apply: - desc: "Apply coding standards for Markdown" + desc: 'Apply coding standards for Markdown' cmds: - task: compose vars: COMPOSE_ARGS: run --rm markdownlint markdownlint '**/*.md' --fix coding-standards:markdown:check: - desc: "Apply and check coding standards for Markdown" + desc: 'Apply and check coding standards for Markdown' cmds: - task: coding-standards:markdown:apply - task: compose @@ -111,7 +111,7 @@ tasks: COMPOSE_ARGS: run --rm markdownlint markdownlint '**/*.md' coding-standards:php:apply: - desc: "Apply coding standards for PHP" + desc: 'Apply coding standards for PHP' cmds: - task: compose vars: @@ -119,7 +119,7 @@ tasks: silent: true coding-standards:php:check: - desc: "Apply and check coding standards for PHP" + desc: 'Apply and check coding standards for PHP' cmds: - task: coding-standards:php:apply - task: compose @@ -128,7 +128,7 @@ tasks: silent: true coding-standards:twig:apply: - desc: "Apply coding standards for Twig" + desc: 'Apply coding standards for Twig' cmds: - task: compose vars: @@ -136,7 +136,7 @@ tasks: silent: true coding-standards:twig:check: - desc: "Apply and check coding standards for Twig" + desc: 'Apply and check coding standards for Twig' cmds: - task: coding-standards:twig:apply - task: compose @@ -145,7 +145,7 @@ tasks: silent: true coding-standards:yaml:apply: - desc: "Apply coding standards for YAML" + desc: 'Apply coding standards for YAML' cmds: - task: compose vars: @@ -153,7 +153,7 @@ tasks: silent: true coding-standards:yaml:check: - desc: "Apply and check coding standards for YAML" + desc: 'Apply and check coding standards for YAML' cmds: - task: coding-standards:yaml:apply - task: compose @@ -162,14 +162,14 @@ tasks: silent: true code-analysis: - desc: "Run code analysis" + desc: 'Run code analysis' cmds: - task: code-analysis:phpstan - task: code-analysis:rector silent: true code-analysis:phpstan: - desc: "Run PHPStan" + desc: 'Run PHPStan' cmds: - task: compose vars: @@ -177,7 +177,7 @@ tasks: silent: true code-analysis:rector: - desc: "Check for Rector suggestions (dry-run; no changes applied)" + desc: 'Check for Rector suggestions (dry-run; no changes applied)' cmds: - task: compose vars: @@ -185,7 +185,7 @@ tasks: silent: true code-analysis:rector:apply: - desc: "Apply Rector changes" + desc: 'Apply Rector changes' cmds: - task: compose vars: diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index ca2f4d1..71a88b4 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -6,7 +6,7 @@ services: nginx: labels: - - "traefik.http.routers.${COMPOSE_PROJECT_NAME}.middlewares=ITKBasicAuth@file" + - "traefik.http.routers.${COMPOSE_PROJECT_NAME:?}.middlewares=ITKBasicAuth@file" mail: image: axllent/mailpit @@ -17,11 +17,11 @@ services: labels: - "traefik.enable=true" - "traefik.docker.network=frontend" - - "traefik.http.routers.mail_${COMPOSE_PROJECT_NAME}-http.rule=Host(`mail.${COMPOSE_SERVER_DOMAIN}`)" - - "traefik.http.routers.mail_${COMPOSE_PROJECT_NAME}-http.entrypoints=web" - - "traefik.http.routers.mail_${COMPOSE_PROJECT_NAME}-http.middlewares=redirect-to-https" + - "traefik.http.routers.mail_${COMPOSE_PROJECT_NAME:?}-http.rule=Host(`mail.${COMPOSE_SERVER_DOMAIN:?}`)" + - "traefik.http.routers.mail_${COMPOSE_PROJECT_NAME:?}-http.entrypoints=web" + - "traefik.http.routers.mail_${COMPOSE_PROJECT_NAME:?}-http.middlewares=redirect-to-https" - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https" - - "traefik.http.routers.mail_${COMPOSE_PROJECT_NAME}.rule=Host(`mail.${COMPOSE_SERVER_DOMAIN}`)" - - "traefik.http.routers.mail_${COMPOSE_PROJECT_NAME}.entrypoints=websecure" - - "traefik.http.services.mail_${COMPOSE_PROJECT_NAME}.loadbalancer.server.port=8025" - - "traefik.http.routers.mail_${COMPOSE_PROJECT_NAME}.middlewares=ITKMailhogAuth@file" + - "traefik.http.routers.mail_${COMPOSE_PROJECT_NAME:?}.rule=Host(`mail.${COMPOSE_SERVER_DOMAIN:?}`)" + - "traefik.http.routers.mail_${COMPOSE_PROJECT_NAME:?}.entrypoints=websecure" + - "traefik.http.services.mail_${COMPOSE_PROJECT_NAME:?}.loadbalancer.server.port=8025" + - "traefik.http.routers.mail_${COMPOSE_PROJECT_NAME:?}.middlewares=ITKMailhogAuth@file" diff --git a/docker-compose.override.yml b/docker-compose.override.yml index 898fd23..9928b01 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -1,6 +1,5 @@ services: phpfpm: - image: itkdev/php8.4-fpm:latest networks: - frontend depends_on: diff --git a/docker-compose.redirect.yml b/docker-compose.redirect.yml index e9ba157..2e7ac33 100644 --- a/docker-compose.redirect.yml +++ b/docker-compose.redirect.yml @@ -3,13 +3,13 @@ services: nginx: labels: # Add www before domain and set redirect to non-www - - "traefik.http.routers.www_${COMPOSE_PROJECT_NAME}-http.rule=Host(`www.${COMPOSE_SERVER_DOMAIN}`)" - - "traefik.http.routers.www_${COMPOSE_PROJECT_NAME}-http.entrypoints=web" - - "traefik.http.routers.www_${COMPOSE_PROJECT_NAME}-http.middlewares=redirect-to-https,non_www" - - "traefik.http.routers.www_${COMPOSE_PROJECT_NAME}.rule=Host(`www.${COMPOSE_SERVER_DOMAIN}`)" - - "traefik.http.routers.www_${COMPOSE_PROJECT_NAME}.entrypoints=websecure" - - "traefik.http.routers.www_${COMPOSE_PROJECT_NAME}.middlewares=non_www" + - "traefik.http.routers.www_${COMPOSE_PROJECT_NAME:?}-http.rule=Host(`www.${COMPOSE_SERVER_DOMAIN:?}`)" + - "traefik.http.routers.www_${COMPOSE_PROJECT_NAME:?}-http.entrypoints=web" + - "traefik.http.routers.www_${COMPOSE_PROJECT_NAME:?}-http.middlewares=redirect-to-https,non_www" + - "traefik.http.routers.www_${COMPOSE_PROJECT_NAME:?}.rule=Host(`www.${COMPOSE_SERVER_DOMAIN:?}`)" + - "traefik.http.routers.www_${COMPOSE_PROJECT_NAME:?}.entrypoints=websecure" + - "traefik.http.routers.www_${COMPOSE_PROJECT_NAME:?}.middlewares=non_www" - traefik.http.middlewares.non_www.redirectregex.regex=^(http|https)?://(?:www\.)?(.+) - - traefik.http.middlewares.non_www.redirectregex.replacement=https://$${2} + - traefik.http.middlewares.non_www.redirectregex.replacement=https://$${2:?} - traefik.http.middlewares.non_www.redirectregex.permanent=true diff --git a/docker-compose.server.override.yml b/docker-compose.server.override.yml index e796748..3211589 100644 --- a/docker-compose.server.override.yml +++ b/docker-compose.server.override.yml @@ -1,6 +1,5 @@ services: phpfpm: - image: itkdev/php8.4-fpm:alpine environment: - PHP_MEMORY_LIMIT=256M @@ -15,3 +14,5 @@ services: - "traefik.http.routers.${COMPOSE_PROJECT_NAME}-http.priority=200" - "traefik.http.routers.${COMPOSE_PROJECT_NAME}.rule=Host(`${COMPOSE_SERVER_DOMAIN}`) && PathPrefix(`${APP_PATH_PREFIX}`)" - "traefik.http.routers.${COMPOSE_PROJECT_NAME}.priority=200" + # This app has no cron jobs. Let the importer on the same domain serve '/cron-metrics'. + - "traefik.http.routers.${COMPOSE_PROJECT_NAME}-metrics.priority=1" diff --git a/docker-compose.server.yml b/docker-compose.server.yml index dd16da4..19a4e89 100644 --- a/docker-compose.server.yml +++ b/docker-compose.server.yml @@ -8,7 +8,7 @@ networks: services: phpfpm: - image: itkdev/php8.4-fpm:alpine + image: itkdev/php8.5-fpm:alpine restart: unless-stopped networks: - app @@ -33,16 +33,20 @@ services: - ./.docker/nginx.conf:/etc/nginx/nginx.conf:ro - .:/app environment: - NGINX_FPM_SERVICE: ${COMPOSE_PROJECT_NAME}-phpfpm-1:9000 + NGINX_FPM_SERVICE: ${COMPOSE_PROJECT_NAME:?}-phpfpm-1:9000 + NGINX_CRON_METRICS: ${COMPOSE_PROJECT_NAME:?}-phpfpm-1:9746 NGINX_WEB_ROOT: /app/public NGINX_PORT: 8080 NGINX_MAX_BODY_SIZE: 5M labels: - "traefik.enable=true" - "traefik.docker.network=frontend" - - "traefik.http.routers.${COMPOSE_PROJECT_NAME}-http.rule=Host(`${COMPOSE_SERVER_DOMAIN}`)" - - "traefik.http.routers.${COMPOSE_PROJECT_NAME}-http.entrypoints=web" - - "traefik.http.routers.${COMPOSE_PROJECT_NAME}-http.middlewares=redirect-to-https" + - "traefik.http.routers.${COMPOSE_PROJECT_NAME:?}-http.rule=Host(`${COMPOSE_SERVER_DOMAIN:?}`)" + - "traefik.http.routers.${COMPOSE_PROJECT_NAME:?}-http.entrypoints=web" + - "traefik.http.routers.${COMPOSE_PROJECT_NAME:?}-http.middlewares=redirect-to-https" - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https" - - "traefik.http.routers.${COMPOSE_PROJECT_NAME}.rule=Host(`${COMPOSE_SERVER_DOMAIN}`)" - - "traefik.http.routers.${COMPOSE_PROJECT_NAME}.entrypoints=websecure" + - "traefik.http.routers.${COMPOSE_PROJECT_NAME:?}.rule=Host(`${COMPOSE_SERVER_DOMAIN:?}`)" + - "traefik.http.routers.${COMPOSE_PROJECT_NAME:?}.entrypoints=websecure" + # Cron-metrics protection. + - "traefik.http.routers.${COMPOSE_PROJECT_NAME:?}-metrics.rule=Host(`${COMPOSE_SERVER_DOMAIN:?}`) && PathPrefix(`/cron-metrics`) " + - "traefik.http.routers.${COMPOSE_PROJECT_NAME:?}-metrics.middlewares=ITKMetricsAuth@file" diff --git a/docker-compose.yml b/docker-compose.yml index cc88b52..4455116 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -27,7 +27,7 @@ services: #- ENCRYPT=1 # Uncomment to enable database encryption. phpfpm: - image: itkdev/php8.4-fpm:latest + image: itkdev/php8.5-fpm:latest user: ${COMPOSE_USER:-deploy} networks: - app @@ -39,7 +39,7 @@ services: - PHP_MEMORY_LIMIT=256M # Depending on the setup, you may have to remove --read-envelope-from from msmtp (cf. https://marlam.de/msmtp/msmtp.html) or use SMTP to send mail - PHP_SENDMAIL_PATH=/usr/bin/msmtp --host=mail --port=1025 --read-recipients --read-envelope-from - - DOCKER_HOST_DOMAIN=${COMPOSE_DOMAIN} + - DOCKER_HOST_DOMAIN=${COMPOSE_DOMAIN:?} - PHP_IDE_CONFIG=serverName=localhost depends_on: mariadb: @@ -60,17 +60,21 @@ services: - ./.docker/templates:/etc/nginx/templates:ro - .:/app environment: - NGINX_FPM_SERVICE: ${COMPOSE_PROJECT_NAME}-phpfpm-1:9000 + NGINX_FPM_SERVICE: ${COMPOSE_PROJECT_NAME:?}-phpfpm-1:9000 + NGINX_CRON_METRICS: ${COMPOSE_PROJECT_NAME:?}-phpfpm-1:9746 NGINX_WEB_ROOT: /app/public NGINX_PORT: 8080 NGINX_MAX_BODY_SIZE: 5M labels: - "traefik.enable=true" - "traefik.docker.network=frontend" - - "traefik.http.routers.${COMPOSE_PROJECT_NAME}.rule=Host(`${COMPOSE_DOMAIN}`)" + - "traefik.http.routers.${COMPOSE_PROJECT_NAME:?}.rule=Host(`${COMPOSE_DOMAIN:?}`)" # HTTPS config - uncomment to enable redirect from :80 to :443 - # - "traefik.http.routers.${COMPOSE_PROJECT_NAME}.middlewares=redirect-to-https" + # - "traefik.http.routers.${COMPOSE_PROJECT_NAME:?}.middlewares=redirect-to-https" # - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https" + # Cron-metrics protection. + - "traefik.http.routers.${COMPOSE_PROJECT_NAME:?}-metrics.rule=Host(`${COMPOSE_DOMAIN:?}`) && PathPrefix(`/cron-metrics`) " + - "traefik.http.routers.${COMPOSE_PROJECT_NAME:?}-metrics.middlewares=ITKMetricsAuth@file" mail: image: axllent/mailpit @@ -83,8 +87,8 @@ services: labels: - "traefik.enable=true" - "traefik.docker.network=frontend" - - "traefik.http.routers.${COMPOSE_PROJECT_NAME}mail.rule=Host(`mail-${COMPOSE_DOMAIN}`)" - - "traefik.http.services.${COMPOSE_PROJECT_NAME}mail.loadbalancer.server.port=8025" + - "traefik.http.routers.${COMPOSE_PROJECT_NAME:?}mail.rule=Host(`mail-${COMPOSE_DOMAIN:?}`)" + - "traefik.http.services.${COMPOSE_PROJECT_NAME:?}mail.loadbalancer.server.port=8025" # Code checks tools markdownlint: @@ -95,7 +99,7 @@ services: - ./:/md prettier: - # Prettier does not (yet, fcf. + # Prettier does not (yet, cf. # https://github.com/prettier/prettier/issues/15206) have an official # docker image. # https://hub.docker.com/r/jauderho/prettier is good candidate (cf. https://hub.docker.com/search?q=prettier&sort=updated_at&order=desc) From 51c68bb67da53eabc6dd91931964b25cb20950ad Mon Sep 17 00:00:00 2001 From: turegjorup Date: Sun, 8 Nov 2026 21:10:16 +0100 Subject: [PATCH 2/5] chore: require PHP 8.5 and apply Rector PHP 8.5 rules Rector changes are syntax only: new without parentheses and #[\Override] on overridden properties. --- CHANGELOG.md | 3 +++ CLAUDE.md | 4 ++-- composer.json | 2 +- composer.lock | 9 +++++---- rector.php | 2 +- src/Command/ApiKeysAddCommand.php | 2 +- src/Service/ElasticSearch/ElasticIndexException.php | 2 +- tests/ApiPlatform/AbstractApiTestCase.php | 3 ++- tests/ApiPlatform/AuthenticationTest.php | 1 + .../ApiPlatform/Consumer/AarhusguidenContractTest.php | 1 + tests/ApiPlatform/Consumer/Os2displayContractTest.php | 1 + tests/ApiPlatform/Contract/CollectionContractTest.php | 1 + .../Contract/ContentNegotiationContractTest.php | 1 + tests/ApiPlatform/Contract/DateFormatContractTest.php | 1 + tests/ApiPlatform/Contract/ErrorContractTest.php | 1 + tests/ApiPlatform/Contract/ItemContractTest.php | 1 + tests/ApiPlatform/DailyOccurrencesFilterTest.php | 1 + tests/ApiPlatform/DailyOccurrencesTest.php | 4 ++++ tests/ApiPlatform/EventsFilterTest.php | 1 + tests/ApiPlatform/EventsTest.php | 4 ++++ tests/ApiPlatform/FilterErrorTest.php | 1 + tests/ApiPlatform/LocationsFilterTest.php | 1 + tests/ApiPlatform/LocationsTest.php | 4 ++++ tests/ApiPlatform/OccurrencesFilterTest.php | 1 + tests/ApiPlatform/OccurrencesTest.php | 4 ++++ tests/ApiPlatform/OrganizationsFilterTest.php | 1 + tests/ApiPlatform/OrganizationsTest.php | 4 ++++ tests/ApiPlatform/PaginationTest.php | 1 + tests/ApiPlatform/SortTest.php | 1 + tests/ApiPlatform/TagsFilterTest.php | 1 + tests/ApiPlatform/TagsTest.php | 4 ++++ tests/ApiPlatform/VocabulariesFilterTest.php | 1 + tests/ApiPlatform/VocabulariesTest.php | 4 ++++ tests/Command/ApiKeysAddCommandTest.php | 4 ++-- .../Service/ElasticSearch/SearchParamsBuilderTest.php | 10 +++++----- tests/bootstrap.php | 2 +- 36 files changed, 70 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a5462b0..4c5f5e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,9 @@ See [keep a changelog] for information about writing changes to this log. ## [Unreleased] +- [PR-XX](https://github.com/itk-dev/event-database-api/pull/XX) + Update the docker compose template (itk-version 3.2.4) and PHP 8.5 + ## [1.3.0] - 2026-11-04 - [PR-62](https://github.com/itk-dev/event-database-api/pull/62) diff --git a/CLAUDE.md b/CLAUDE.md index d94f409..7cc162e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -10,8 +10,8 @@ API platform front-end for the Danish event database used by the municipality of `docker-compose.yml` is part of the standard ITK Dev Symfony image but is **not used for domain data** (the `migrations/` and `src/Entity/` directories are empty). -Stack: PHP 8.3+, Symfony 7.4, API Platform 4.3, Elasticsearch 8.x. Runs entirely in Docker via -`itkdev/php8.4-fpm` + nginx. +Stack: PHP 8.5+, Symfony 7.4, API Platform 4.3, Elasticsearch 8.x. Runs entirely in Docker via +`itkdev/php8.5-fpm` + nginx. ## Common commands diff --git a/composer.json b/composer.json index ffeefda..26f7f92 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,7 @@ "license": "proprietary", "type": "project", "require": { - "php": ">=8.3", + "php": ">=8.5", "ext-ctype": "*", "ext-iconv": "*", "api-platform/core": "~4.3.0", diff --git a/composer.lock b/composer.lock index 3c3f893..382719a 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "747cb423309e517227c3648b1003fc5a", + "content-hash": "1c8b255e46f2edded27db7812fe8114e", "packages": [ { "name": "api-platform/core", @@ -8030,7 +8030,8 @@ "type": "github" } ], - "time": "2026-07-05T06:31:06+00:00" + "time": "2026-07-05T06:31:06+00:00", + "published-time": "2026-07-05T06:31:19+00:00" }, { "name": "phpstan/phpstan-strict-rules", @@ -11116,10 +11117,10 @@ "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": ">=8.3", + "php": ">=8.5", "ext-ctype": "*", "ext-iconv": "*" }, "platform-dev": {}, - "plugin-api-version": "2.9.0" + "plugin-api-version": "2.11.0" } diff --git a/rector.php b/rector.php index 37ea484..9a2a485 100644 --- a/rector.php +++ b/rector.php @@ -13,7 +13,7 @@ // Coding style and import (`use`) management are owned by php-cs-fixer // (PostToolUse hook / coding standards), so Rector does not touch them. // - // Modernize to the language level declared in composer.json (PHP >= 8.3). + // Modernize to the language level declared in composer.json (PHP >= 8.5). ->withPhpSets() // Version-based upgrade + quality rules resolved from the installed package // versions (Symfony 7.4, PHPUnit 13). No domain Doctrine here — the entities diff --git a/src/Command/ApiKeysAddCommand.php b/src/Command/ApiKeysAddCommand.php index a343f19..4104de0 100644 --- a/src/Command/ApiKeysAddCommand.php +++ b/src/Command/ApiKeysAddCommand.php @@ -187,7 +187,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int */ private function parseApiKeys(string $contents): array { - $value = (new Dotenv())->parse($contents)[self::ENV_VAR] ?? '[]'; + $value = new Dotenv()->parse($contents)[self::ENV_VAR] ?? '[]'; return json_decode($value, true, 512, JSON_THROW_ON_ERROR); } diff --git a/src/Service/ElasticSearch/ElasticIndexException.php b/src/Service/ElasticSearch/ElasticIndexException.php index 7b0074d..3c1aa3c 100644 --- a/src/Service/ElasticSearch/ElasticIndexException.php +++ b/src/Service/ElasticSearch/ElasticIndexException.php @@ -35,7 +35,7 @@ private function parse400(string $message): string $message = json_decode($message, false, 512, JSON_THROW_ON_ERROR); // Convert snake_case type to readable format (e.g., "parse_exception" -> "Parse exception") - $type = (new UnicodeString($message->error->root_cause[0]->type)) + $type = new UnicodeString($message->error->root_cause[0]->type) ->replace('_', ' ') ->title() ->toString(); diff --git a/tests/ApiPlatform/AbstractApiTestCase.php b/tests/ApiPlatform/AbstractApiTestCase.php index 36075f9..efd7fd7 100644 --- a/tests/ApiPlatform/AbstractApiTestCase.php +++ b/tests/ApiPlatform/AbstractApiTestCase.php @@ -10,6 +10,7 @@ abstract class AbstractApiTestCase extends ApiTestCase { + #[\Override] protected static ?bool $alwaysBootKernel = false; protected static string $requestPath; @@ -52,7 +53,7 @@ protected function get(array $query, ?string $path = null, bool $authenticated = protected static function formatDateTime(string $datetime): string { - return (new \DateTimeImmutable($datetime))->format(\DateTimeImmutable::ATOM); + return new \DateTimeImmutable($datetime)->format(\DateTimeImmutable::ATOM); } /** diff --git a/tests/ApiPlatform/AuthenticationTest.php b/tests/ApiPlatform/AuthenticationTest.php index 17c391e..d209526 100644 --- a/tests/ApiPlatform/AuthenticationTest.php +++ b/tests/ApiPlatform/AuthenticationTest.php @@ -12,6 +12,7 @@ */ final class AuthenticationTest extends AbstractApiTestCase { + #[\Override] protected static string $requestPath = '/api/v2/events'; #[DataProvider('protectedEndpointProvider')] diff --git a/tests/ApiPlatform/Consumer/AarhusguidenContractTest.php b/tests/ApiPlatform/Consumer/AarhusguidenContractTest.php index 99b7146..27120c8 100644 --- a/tests/ApiPlatform/Consumer/AarhusguidenContractTest.php +++ b/tests/ApiPlatform/Consumer/AarhusguidenContractTest.php @@ -23,6 +23,7 @@ */ final class AarhusguidenContractTest extends AbstractApiTestCase { + #[\Override] protected static string $requestPath = '/api/v2/daily_occurrences'; // Calendar / tag view: public events carrying a tag, within a date window, diff --git a/tests/ApiPlatform/Consumer/Os2displayContractTest.php b/tests/ApiPlatform/Consumer/Os2displayContractTest.php index 5811f6b..a6a9db8 100644 --- a/tests/ApiPlatform/Consumer/Os2displayContractTest.php +++ b/tests/ApiPlatform/Consumer/Os2displayContractTest.php @@ -30,6 +30,7 @@ */ final class Os2displayContractTest extends AbstractApiTestCase { + #[\Override] protected static string $requestPath = '/api/v2/occurrences'; // Subscription poster: upcoming occurrences (end[gt]) for a tag/location. diff --git a/tests/ApiPlatform/Contract/CollectionContractTest.php b/tests/ApiPlatform/Contract/CollectionContractTest.php index 7c65a30..95b5fd6 100644 --- a/tests/ApiPlatform/Contract/CollectionContractTest.php +++ b/tests/ApiPlatform/Contract/CollectionContractTest.php @@ -19,6 +19,7 @@ */ final class CollectionContractTest extends AbstractApiTestCase { + #[\Override] protected static string $requestPath = '/api/v2/events'; #[DataProvider('collectionProvider')] diff --git a/tests/ApiPlatform/Contract/ContentNegotiationContractTest.php b/tests/ApiPlatform/Contract/ContentNegotiationContractTest.php index a075060..c9cb196 100644 --- a/tests/ApiPlatform/Contract/ContentNegotiationContractTest.php +++ b/tests/ApiPlatform/Contract/ContentNegotiationContractTest.php @@ -16,6 +16,7 @@ */ final class ContentNegotiationContractTest extends AbstractApiTestCase { + #[\Override] protected static string $requestPath = '/api/v2/events'; public function testLdJsonIsSupported(): void diff --git a/tests/ApiPlatform/Contract/DateFormatContractTest.php b/tests/ApiPlatform/Contract/DateFormatContractTest.php index eb49f13..c0e9e72 100644 --- a/tests/ApiPlatform/Contract/DateFormatContractTest.php +++ b/tests/ApiPlatform/Contract/DateFormatContractTest.php @@ -13,6 +13,7 @@ */ final class DateFormatContractTest extends AbstractApiTestCase { + #[\Override] protected static string $requestPath = '/api/v2/events'; private const string ISO_8601_OFFSET = '/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+-]\d{2}:\d{2}$/'; diff --git a/tests/ApiPlatform/Contract/ErrorContractTest.php b/tests/ApiPlatform/Contract/ErrorContractTest.php index d5656a6..56a6111 100644 --- a/tests/ApiPlatform/Contract/ErrorContractTest.php +++ b/tests/ApiPlatform/Contract/ErrorContractTest.php @@ -22,6 +22,7 @@ */ final class ErrorContractTest extends AbstractApiTestCase { + #[\Override] protected static string $requestPath = '/api/v2/events'; public function testUnauthenticatedErrorShape(): void diff --git a/tests/ApiPlatform/Contract/ItemContractTest.php b/tests/ApiPlatform/Contract/ItemContractTest.php index 3c6ec51..045cb80 100644 --- a/tests/ApiPlatform/Contract/ItemContractTest.php +++ b/tests/ApiPlatform/Contract/ItemContractTest.php @@ -21,6 +21,7 @@ */ final class ItemContractTest extends AbstractApiTestCase { + #[\Override] protected static string $requestPath = '/api/v2/events'; #[DataProvider('collectionWrappedItemProvider')] diff --git a/tests/ApiPlatform/DailyOccurrencesFilterTest.php b/tests/ApiPlatform/DailyOccurrencesFilterTest.php index 1b9efb6..db92321 100644 --- a/tests/ApiPlatform/DailyOccurrencesFilterTest.php +++ b/tests/ApiPlatform/DailyOccurrencesFilterTest.php @@ -14,6 +14,7 @@ */ final class DailyOccurrencesFilterTest extends AbstractApiTestCase { + #[\Override] protected static string $requestPath = '/api/v2/daily_occurrences'; #[DataProvider('getDailyOccurrencesProvider')] diff --git a/tests/ApiPlatform/DailyOccurrencesTest.php b/tests/ApiPlatform/DailyOccurrencesTest.php index c7a7883..826d711 100644 --- a/tests/ApiPlatform/DailyOccurrencesTest.php +++ b/tests/ApiPlatform/DailyOccurrencesTest.php @@ -13,13 +13,17 @@ final class DailyOccurrencesTest extends AbstractApiTestCase use GetEntitiesTestTrait; use GetItemTestTrait; + #[\Override] protected static string $requestPath = '/api/v2/daily_occurrences'; + #[\Override] protected static string $resourceClass = DailyOccurrence::class; // Members are the raw Elasticsearch _source with no @id/@type, so API // Platform's self-generated collection schema does not apply (see D6). + #[\Override] protected static bool $assertsGeneratedCollectionSchema = false; + #[\Override] protected static int|string $itemId = 10; } diff --git a/tests/ApiPlatform/EventsFilterTest.php b/tests/ApiPlatform/EventsFilterTest.php index 32b5e0d..d906dab 100644 --- a/tests/ApiPlatform/EventsFilterTest.php +++ b/tests/ApiPlatform/EventsFilterTest.php @@ -15,6 +15,7 @@ */ final class EventsFilterTest extends AbstractApiTestCase { + #[\Override] protected static string $requestPath = '/api/v2/events'; #[DataProvider('getEventsProvider')] diff --git a/tests/ApiPlatform/EventsTest.php b/tests/ApiPlatform/EventsTest.php index 6b0f0d4..0c1c154 100644 --- a/tests/ApiPlatform/EventsTest.php +++ b/tests/ApiPlatform/EventsTest.php @@ -16,13 +16,17 @@ final class EventsTest extends AbstractApiTestCase use GetEntitiesTestTrait; use GetItemTestTrait; + #[\Override] protected static string $requestPath = '/api/v2/events'; + #[\Override] protected static string $resourceClass = Event::class; // Members are the raw Elasticsearch _source with no @id/@type, so API // Platform's self-generated collection schema does not apply (see D6). + #[\Override] protected static bool $assertsGeneratedCollectionSchema = false; + #[\Override] protected static int|string $itemId = 7; } diff --git a/tests/ApiPlatform/FilterErrorTest.php b/tests/ApiPlatform/FilterErrorTest.php index 8982021..daf88ec 100644 --- a/tests/ApiPlatform/FilterErrorTest.php +++ b/tests/ApiPlatform/FilterErrorTest.php @@ -19,6 +19,7 @@ */ final class FilterErrorTest extends AbstractApiTestCase { + #[\Override] protected static string $requestPath = '/api/v2/events'; #[DataProvider('invalidDateRangeProvider')] diff --git a/tests/ApiPlatform/LocationsFilterTest.php b/tests/ApiPlatform/LocationsFilterTest.php index 6866c51..a3c44b6 100644 --- a/tests/ApiPlatform/LocationsFilterTest.php +++ b/tests/ApiPlatform/LocationsFilterTest.php @@ -14,6 +14,7 @@ */ final class LocationsFilterTest extends AbstractApiTestCase { + #[\Override] protected static string $requestPath = '/api/v2/locations'; #[DataProvider('getLocationsProvider')] diff --git a/tests/ApiPlatform/LocationsTest.php b/tests/ApiPlatform/LocationsTest.php index 4db86b7..bf43a18 100644 --- a/tests/ApiPlatform/LocationsTest.php +++ b/tests/ApiPlatform/LocationsTest.php @@ -13,13 +13,17 @@ final class LocationsTest extends AbstractApiTestCase use GetEntitiesTestTrait; use GetItemTestTrait; + #[\Override] protected static string $requestPath = '/api/v2/locations'; + #[\Override] protected static string $resourceClass = Location::class; // Members are the raw Elasticsearch _source with no @id/@type, so API // Platform's self-generated collection schema does not apply (see D6). + #[\Override] protected static bool $assertsGeneratedCollectionSchema = false; + #[\Override] protected static int|string $itemId = 4; } diff --git a/tests/ApiPlatform/OccurrencesFilterTest.php b/tests/ApiPlatform/OccurrencesFilterTest.php index 6018da4..70993ef 100644 --- a/tests/ApiPlatform/OccurrencesFilterTest.php +++ b/tests/ApiPlatform/OccurrencesFilterTest.php @@ -15,6 +15,7 @@ */ final class OccurrencesFilterTest extends AbstractApiTestCase { + #[\Override] protected static string $requestPath = '/api/v2/occurrences'; #[DataProvider('getOccurrencesProvider')] diff --git a/tests/ApiPlatform/OccurrencesTest.php b/tests/ApiPlatform/OccurrencesTest.php index b516b84..a021486 100644 --- a/tests/ApiPlatform/OccurrencesTest.php +++ b/tests/ApiPlatform/OccurrencesTest.php @@ -13,13 +13,17 @@ final class OccurrencesTest extends AbstractApiTestCase use GetEntitiesTestTrait; use GetItemTestTrait; + #[\Override] protected static string $requestPath = '/api/v2/occurrences'; + #[\Override] protected static string $resourceClass = Occurrence::class; // Members are the raw Elasticsearch _source with no @id/@type, so API // Platform's self-generated collection schema does not apply (see D6). + #[\Override] protected static bool $assertsGeneratedCollectionSchema = false; + #[\Override] protected static int|string $itemId = 10; } diff --git a/tests/ApiPlatform/OrganizationsFilterTest.php b/tests/ApiPlatform/OrganizationsFilterTest.php index 73c5251..b84dcfc 100644 --- a/tests/ApiPlatform/OrganizationsFilterTest.php +++ b/tests/ApiPlatform/OrganizationsFilterTest.php @@ -14,6 +14,7 @@ */ final class OrganizationsFilterTest extends AbstractApiTestCase { + #[\Override] protected static string $requestPath = '/api/v2/organizations'; #[DataProvider('getOrganizationsProvider')] diff --git a/tests/ApiPlatform/OrganizationsTest.php b/tests/ApiPlatform/OrganizationsTest.php index 92e7eb6..bef3062 100644 --- a/tests/ApiPlatform/OrganizationsTest.php +++ b/tests/ApiPlatform/OrganizationsTest.php @@ -13,13 +13,17 @@ final class OrganizationsTest extends AbstractApiTestCase use GetEntitiesTestTrait; use GetItemTestTrait; + #[\Override] protected static string $requestPath = '/api/v2/organizations'; + #[\Override] protected static string $resourceClass = Organization::class; // Members are the raw Elasticsearch _source with no @id/@type, so API // Platform's self-generated collection schema does not apply (see D6). + #[\Override] protected static bool $assertsGeneratedCollectionSchema = false; + #[\Override] protected static int|string $itemId = 9; } diff --git a/tests/ApiPlatform/PaginationTest.php b/tests/ApiPlatform/PaginationTest.php index 46d4adb..5e23950 100644 --- a/tests/ApiPlatform/PaginationTest.php +++ b/tests/ApiPlatform/PaginationTest.php @@ -12,6 +12,7 @@ */ final class PaginationTest extends AbstractApiTestCase { + #[\Override] protected static string $requestPath = '/api/v2/events'; #[DataProvider('resourceProvider')] diff --git a/tests/ApiPlatform/SortTest.php b/tests/ApiPlatform/SortTest.php index 7e707ad..bac6a23 100644 --- a/tests/ApiPlatform/SortTest.php +++ b/tests/ApiPlatform/SortTest.php @@ -16,6 +16,7 @@ */ final class SortTest extends AbstractApiTestCase { + #[\Override] protected static string $requestPath = '/api/v2/events'; public function testEventsAreSortedByTitleAscending(): void diff --git a/tests/ApiPlatform/TagsFilterTest.php b/tests/ApiPlatform/TagsFilterTest.php index f1a6d74..dc82b6e 100644 --- a/tests/ApiPlatform/TagsFilterTest.php +++ b/tests/ApiPlatform/TagsFilterTest.php @@ -15,6 +15,7 @@ */ final class TagsFilterTest extends AbstractApiTestCase { + #[\Override] protected static string $requestPath = '/api/v2/tags'; #[DataProvider('getTagsProvider')] diff --git a/tests/ApiPlatform/TagsTest.php b/tests/ApiPlatform/TagsTest.php index c3b0610..b958e32 100644 --- a/tests/ApiPlatform/TagsTest.php +++ b/tests/ApiPlatform/TagsTest.php @@ -16,11 +16,15 @@ final class TagsTest extends AbstractApiTestCase use GetEntitiesTestTrait; use GetItemTestTrait; + #[\Override] protected static string $requestPath = '/api/v2/tags'; + #[\Override] protected static string $resourceClass = Tag::class; + #[\Override] protected static int|string $itemId = 'aros'; + #[\Override] protected static int|string $unknownItemId = 'no-such-tag-slug'; } diff --git a/tests/ApiPlatform/VocabulariesFilterTest.php b/tests/ApiPlatform/VocabulariesFilterTest.php index 51533e0..3870fff 100644 --- a/tests/ApiPlatform/VocabulariesFilterTest.php +++ b/tests/ApiPlatform/VocabulariesFilterTest.php @@ -15,6 +15,7 @@ */ final class VocabulariesFilterTest extends AbstractApiTestCase { + #[\Override] protected static string $requestPath = '/api/v2/vocabularies'; #[DataProvider('getVocabulariesProvider')] diff --git a/tests/ApiPlatform/VocabulariesTest.php b/tests/ApiPlatform/VocabulariesTest.php index 5f86b89..b569a95 100644 --- a/tests/ApiPlatform/VocabulariesTest.php +++ b/tests/ApiPlatform/VocabulariesTest.php @@ -16,11 +16,15 @@ final class VocabulariesTest extends AbstractApiTestCase use GetEntitiesTestTrait; use GetItemTestTrait; + #[\Override] protected static string $requestPath = '/api/v2/vocabularies'; + #[\Override] protected static string $resourceClass = Vocabulary::class; + #[\Override] protected static int|string $itemId = 'aarhusguiden'; + #[\Override] protected static int|string $unknownItemId = 'no-such-vocabulary-slug'; } diff --git a/tests/Command/ApiKeysAddCommandTest.php b/tests/Command/ApiKeysAddCommandTest.php index b3389ec..e5c1b79 100644 --- a/tests/Command/ApiKeysAddCommandTest.php +++ b/tests/Command/ApiKeysAddCommandTest.php @@ -60,7 +60,7 @@ public function testReplacesMultiLineValueAndKeepsOtherLines(): void $this->assertFileDoesNotExist($this->dir.'/.env.local.backup'); $this->assertFileDoesNotExist($this->dir.'/.env.local.temp'); - $env = (new Dotenv())->parse((string) file_get_contents($this->dir.'/.env.local')); + $env = new Dotenv()->parse((string) file_get_contents($this->dir.'/.env.local')); $this->assertSame('http://elasticsearch:9200', $env['INDEX_URL']); $this->assertSame('value', $env['OTHER']); $keys = $this->keys(); @@ -181,7 +181,7 @@ private function runCommand(string $username, array $answers = []): CommandTeste */ private function keys(): array { - $env = (new Dotenv())->parse((string) file_get_contents($this->dir.'/.env.local')); + $env = new Dotenv()->parse((string) file_get_contents($this->dir.'/.env.local')); return json_decode((string) $env['APP_API_KEYS'], true, 512, JSON_THROW_ON_ERROR); } diff --git a/tests/Unit/Service/ElasticSearch/SearchParamsBuilderTest.php b/tests/Unit/Service/ElasticSearch/SearchParamsBuilderTest.php index 7ba3c20..70ecf33 100644 --- a/tests/Unit/Service/ElasticSearch/SearchParamsBuilderTest.php +++ b/tests/Unit/Service/ElasticSearch/SearchParamsBuilderTest.php @@ -33,7 +33,7 @@ private function withFilters(array $clauses): array // Goal: with no filters the query defaults to match_all and pagination/sort are set. public function testDefaultsToMatchAllWithPagination(): void { - $params = (new SearchParamsBuilder())->buildParams(IndexName::Events->value, $this->noFilters(), 20, 5); + $params = new SearchParamsBuilder()->buildParams(IndexName::Events->value, $this->noFilters(), 20, 5); $this->assertSame(IndexName::Events->value, $params['index']); $this->assertEquals(['match_all' => (object) []], $params['body']['query']); @@ -45,7 +45,7 @@ public function testDefaultsToMatchAllWithPagination(): void // Goal: a single associative clause (e.g. a MatchFilter hit) is wrapped in bool/must. public function testSingleClauseWrappedInBoolMust(): void { - $params = (new SearchParamsBuilder())->buildParams( + $params = new SearchParamsBuilder()->buildParams( IndexName::Events->value, $this->withFilters([['match' => ['title' => 'x']]]), 0, @@ -59,7 +59,7 @@ public function testSingleClauseWrappedInBoolMust(): void // nested under numeric keys. public function testListClauseIsFlattenedIntoMust(): void { - $params = (new SearchParamsBuilder())->buildParams( + $params = new SearchParamsBuilder()->buildParams( IndexName::Events->value, $this->withFilters([[['terms' => ['organizer.entityId' => ['9'], 'boost' => 1.0]]]]), 0, @@ -72,7 +72,7 @@ public function testListClauseIsFlattenedIntoMust(): void // Goal: multiple clauses accumulate under a single bool/must. public function testMultipleClausesCombine(): void { - $params = (new SearchParamsBuilder())->buildParams( + $params = new SearchParamsBuilder()->buildParams( IndexName::Events->value, $this->withFilters([ ['match' => ['title' => 'x']], @@ -92,7 +92,7 @@ public function testMultipleClausesCombine(): void #[DataProvider('sortProvider')] public function testSortPerIndex(string $index, array $expectedSort): void { - $params = (new SearchParamsBuilder())->buildParams($index, $this->noFilters(), 0, 10); + $params = new SearchParamsBuilder()->buildParams($index, $this->noFilters(), 0, 10); $this->assertSame($expectedSort, $params['body']['sort']); } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 8e0530d..bc44ee1 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -7,7 +7,7 @@ require dirname(__DIR__).'/vendor/autoload.php'; if (method_exists(Dotenv::class, 'bootEnv')) { - (new Dotenv())->bootEnv(dirname(__DIR__).'/.env'); + new Dotenv()->bootEnv(dirname(__DIR__).'/.env'); } if ($_SERVER['APP_DEBUG']) { From 32e0138c709454269bc3ca077fd2c4484eed23b8 Mon Sep 17 00:00:00 2001 From: turegjorup Date: Sun, 8 Nov 2026 21:10:47 +0100 Subject: [PATCH 3/5] docs: set PR number in CHANGELOG entry --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c5f5e1..d4f66eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,7 +8,7 @@ See [keep a changelog] for information about writing changes to this log. ## [Unreleased] -- [PR-XX](https://github.com/itk-dev/event-database-api/pull/XX) +- [PR-70](https://github.com/itk-dev/event-database-api/pull/70) Update the docker compose template (itk-version 3.2.4) and PHP 8.5 ## [1.3.0] - 2026-11-04 From f8062a91060b636f4c32c48154a2ad124f7bfaa2 Mon Sep 17 00:00:00 2001 From: turegjorup Date: Sun, 8 Nov 2026 21:12:50 +0100 Subject: [PATCH 4/5] chore: move API path rewrite to nginx server.d override Keep the APP_PATH_PREFIX rewrite in .docker/templates/server.d/, the same as event-database-imports, so template updates do not remove it. The include line matches itk-dev/devops_itkdev-docker#163. --- .docker/templates/default.conf.template | 8 +++----- .docker/templates/server.d/app-path-prefix.conf.template | 4 ++++ 2 files changed, 7 insertions(+), 5 deletions(-) create mode 100644 .docker/templates/server.d/app-path-prefix.conf.template diff --git a/.docker/templates/default.conf.template b/.docker/templates/default.conf.template index 9efa01c..69b9694 100644 --- a/.docker/templates/default.conf.template +++ b/.docker/templates/default.conf.template @@ -20,12 +20,10 @@ server { proxy_set_header X-Forwarded-Proto $scheme; } - location / { - # This app shares the domain with event-database-imports (admin). This app is under '/api/v2/' (.env::APP_PATH_PREFIX). - # Symfony uses the prefix in 'api_platform.prefix' and 'framework.assets.base_path'. But Symfony installs - # the assets in the root path. Thus, remove '/api/v2/' from the URI. - rewrite ^${APP_PATH_PREFIX}/(.*) /$1 break; + # Project-specific server config (.docker/templates/server.d/*.conf.template). + include /etc/nginx/conf.d/server.d/*.conf; + location / { # try to serve file directly, fallback to index.php try_files $uri /index.php$is_args$args; } diff --git a/.docker/templates/server.d/app-path-prefix.conf.template b/.docker/templates/server.d/app-path-prefix.conf.template new file mode 100644 index 0000000..97f4641 --- /dev/null +++ b/.docker/templates/server.d/app-path-prefix.conf.template @@ -0,0 +1,4 @@ +# This app shares the domain with event-database-imports, so this app is served under '/api/v2/' (.env::APP_PATH_PREFIX). +# This is configured for Symfony in both 'api_platform.prefix' and 'framework.assets.base_path'. However +# Symfony still installs assets in the root path so we need to rewrite the uri to remove '/api/v2/'. +rewrite ^${APP_PATH_PREFIX}/(.*) /$1 break; From fd777d7a8cb2ee967b11edb62ab5d6bd7f6c8e6d Mon Sep 17 00:00:00 2001 From: turegjorup Date: Sun, 8 Nov 2026 21:15:42 +0100 Subject: [PATCH 5/5] chore: move PHP 8.5 requirement and Rector fixes to a separate PR Keep require.php at >=8.3, so that Rector does not apply the PHP 8.5 rules in this PR. --- CHANGELOG.md | 2 +- CLAUDE.md | 2 +- composer.json | 2 +- composer.lock | 9 ++++----- rector.php | 2 +- src/Command/ApiKeysAddCommand.php | 2 +- src/Service/ElasticSearch/ElasticIndexException.php | 2 +- tests/ApiPlatform/AbstractApiTestCase.php | 3 +-- tests/ApiPlatform/AuthenticationTest.php | 1 - .../ApiPlatform/Consumer/AarhusguidenContractTest.php | 1 - tests/ApiPlatform/Consumer/Os2displayContractTest.php | 1 - tests/ApiPlatform/Contract/CollectionContractTest.php | 1 - .../Contract/ContentNegotiationContractTest.php | 1 - tests/ApiPlatform/Contract/DateFormatContractTest.php | 1 - tests/ApiPlatform/Contract/ErrorContractTest.php | 1 - tests/ApiPlatform/Contract/ItemContractTest.php | 1 - tests/ApiPlatform/DailyOccurrencesFilterTest.php | 1 - tests/ApiPlatform/DailyOccurrencesTest.php | 4 ---- tests/ApiPlatform/EventsFilterTest.php | 1 - tests/ApiPlatform/EventsTest.php | 4 ---- tests/ApiPlatform/FilterErrorTest.php | 1 - tests/ApiPlatform/LocationsFilterTest.php | 1 - tests/ApiPlatform/LocationsTest.php | 4 ---- tests/ApiPlatform/OccurrencesFilterTest.php | 1 - tests/ApiPlatform/OccurrencesTest.php | 4 ---- tests/ApiPlatform/OrganizationsFilterTest.php | 1 - tests/ApiPlatform/OrganizationsTest.php | 4 ---- tests/ApiPlatform/PaginationTest.php | 1 - tests/ApiPlatform/SortTest.php | 1 - tests/ApiPlatform/TagsFilterTest.php | 1 - tests/ApiPlatform/TagsTest.php | 4 ---- tests/ApiPlatform/VocabulariesFilterTest.php | 1 - tests/ApiPlatform/VocabulariesTest.php | 4 ---- tests/Command/ApiKeysAddCommandTest.php | 4 ++-- .../Service/ElasticSearch/SearchParamsBuilderTest.php | 10 +++++----- tests/bootstrap.php | 2 +- 36 files changed, 19 insertions(+), 67 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d4f66eb..6e3212f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ See [keep a changelog] for information about writing changes to this log. ## [Unreleased] - [PR-70](https://github.com/itk-dev/event-database-api/pull/70) - Update the docker compose template (itk-version 3.2.4) and PHP 8.5 + Update the docker compose template (itk-version 3.2.4) and the PHP 8.5 image ## [1.3.0] - 2026-11-04 diff --git a/CLAUDE.md b/CLAUDE.md index 7cc162e..26679fd 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -10,7 +10,7 @@ API platform front-end for the Danish event database used by the municipality of `docker-compose.yml` is part of the standard ITK Dev Symfony image but is **not used for domain data** (the `migrations/` and `src/Entity/` directories are empty). -Stack: PHP 8.5+, Symfony 7.4, API Platform 4.3, Elasticsearch 8.x. Runs entirely in Docker via +Stack: PHP 8.3+, Symfony 7.4, API Platform 4.3, Elasticsearch 8.x. Runs entirely in Docker via `itkdev/php8.5-fpm` + nginx. ## Common commands diff --git a/composer.json b/composer.json index 26f7f92..ffeefda 100644 --- a/composer.json +++ b/composer.json @@ -4,7 +4,7 @@ "license": "proprietary", "type": "project", "require": { - "php": ">=8.5", + "php": ">=8.3", "ext-ctype": "*", "ext-iconv": "*", "api-platform/core": "~4.3.0", diff --git a/composer.lock b/composer.lock index 382719a..3c3f893 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "1c8b255e46f2edded27db7812fe8114e", + "content-hash": "747cb423309e517227c3648b1003fc5a", "packages": [ { "name": "api-platform/core", @@ -8030,8 +8030,7 @@ "type": "github" } ], - "time": "2026-07-05T06:31:06+00:00", - "published-time": "2026-07-05T06:31:19+00:00" + "time": "2026-07-05T06:31:06+00:00" }, { "name": "phpstan/phpstan-strict-rules", @@ -11117,10 +11116,10 @@ "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": ">=8.5", + "php": ">=8.3", "ext-ctype": "*", "ext-iconv": "*" }, "platform-dev": {}, - "plugin-api-version": "2.11.0" + "plugin-api-version": "2.9.0" } diff --git a/rector.php b/rector.php index 9a2a485..37ea484 100644 --- a/rector.php +++ b/rector.php @@ -13,7 +13,7 @@ // Coding style and import (`use`) management are owned by php-cs-fixer // (PostToolUse hook / coding standards), so Rector does not touch them. // - // Modernize to the language level declared in composer.json (PHP >= 8.5). + // Modernize to the language level declared in composer.json (PHP >= 8.3). ->withPhpSets() // Version-based upgrade + quality rules resolved from the installed package // versions (Symfony 7.4, PHPUnit 13). No domain Doctrine here — the entities diff --git a/src/Command/ApiKeysAddCommand.php b/src/Command/ApiKeysAddCommand.php index 4104de0..a343f19 100644 --- a/src/Command/ApiKeysAddCommand.php +++ b/src/Command/ApiKeysAddCommand.php @@ -187,7 +187,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int */ private function parseApiKeys(string $contents): array { - $value = new Dotenv()->parse($contents)[self::ENV_VAR] ?? '[]'; + $value = (new Dotenv())->parse($contents)[self::ENV_VAR] ?? '[]'; return json_decode($value, true, 512, JSON_THROW_ON_ERROR); } diff --git a/src/Service/ElasticSearch/ElasticIndexException.php b/src/Service/ElasticSearch/ElasticIndexException.php index 3c1aa3c..7b0074d 100644 --- a/src/Service/ElasticSearch/ElasticIndexException.php +++ b/src/Service/ElasticSearch/ElasticIndexException.php @@ -35,7 +35,7 @@ private function parse400(string $message): string $message = json_decode($message, false, 512, JSON_THROW_ON_ERROR); // Convert snake_case type to readable format (e.g., "parse_exception" -> "Parse exception") - $type = new UnicodeString($message->error->root_cause[0]->type) + $type = (new UnicodeString($message->error->root_cause[0]->type)) ->replace('_', ' ') ->title() ->toString(); diff --git a/tests/ApiPlatform/AbstractApiTestCase.php b/tests/ApiPlatform/AbstractApiTestCase.php index efd7fd7..36075f9 100644 --- a/tests/ApiPlatform/AbstractApiTestCase.php +++ b/tests/ApiPlatform/AbstractApiTestCase.php @@ -10,7 +10,6 @@ abstract class AbstractApiTestCase extends ApiTestCase { - #[\Override] protected static ?bool $alwaysBootKernel = false; protected static string $requestPath; @@ -53,7 +52,7 @@ protected function get(array $query, ?string $path = null, bool $authenticated = protected static function formatDateTime(string $datetime): string { - return new \DateTimeImmutable($datetime)->format(\DateTimeImmutable::ATOM); + return (new \DateTimeImmutable($datetime))->format(\DateTimeImmutable::ATOM); } /** diff --git a/tests/ApiPlatform/AuthenticationTest.php b/tests/ApiPlatform/AuthenticationTest.php index d209526..17c391e 100644 --- a/tests/ApiPlatform/AuthenticationTest.php +++ b/tests/ApiPlatform/AuthenticationTest.php @@ -12,7 +12,6 @@ */ final class AuthenticationTest extends AbstractApiTestCase { - #[\Override] protected static string $requestPath = '/api/v2/events'; #[DataProvider('protectedEndpointProvider')] diff --git a/tests/ApiPlatform/Consumer/AarhusguidenContractTest.php b/tests/ApiPlatform/Consumer/AarhusguidenContractTest.php index 27120c8..99b7146 100644 --- a/tests/ApiPlatform/Consumer/AarhusguidenContractTest.php +++ b/tests/ApiPlatform/Consumer/AarhusguidenContractTest.php @@ -23,7 +23,6 @@ */ final class AarhusguidenContractTest extends AbstractApiTestCase { - #[\Override] protected static string $requestPath = '/api/v2/daily_occurrences'; // Calendar / tag view: public events carrying a tag, within a date window, diff --git a/tests/ApiPlatform/Consumer/Os2displayContractTest.php b/tests/ApiPlatform/Consumer/Os2displayContractTest.php index a6a9db8..5811f6b 100644 --- a/tests/ApiPlatform/Consumer/Os2displayContractTest.php +++ b/tests/ApiPlatform/Consumer/Os2displayContractTest.php @@ -30,7 +30,6 @@ */ final class Os2displayContractTest extends AbstractApiTestCase { - #[\Override] protected static string $requestPath = '/api/v2/occurrences'; // Subscription poster: upcoming occurrences (end[gt]) for a tag/location. diff --git a/tests/ApiPlatform/Contract/CollectionContractTest.php b/tests/ApiPlatform/Contract/CollectionContractTest.php index 95b5fd6..7c65a30 100644 --- a/tests/ApiPlatform/Contract/CollectionContractTest.php +++ b/tests/ApiPlatform/Contract/CollectionContractTest.php @@ -19,7 +19,6 @@ */ final class CollectionContractTest extends AbstractApiTestCase { - #[\Override] protected static string $requestPath = '/api/v2/events'; #[DataProvider('collectionProvider')] diff --git a/tests/ApiPlatform/Contract/ContentNegotiationContractTest.php b/tests/ApiPlatform/Contract/ContentNegotiationContractTest.php index c9cb196..a075060 100644 --- a/tests/ApiPlatform/Contract/ContentNegotiationContractTest.php +++ b/tests/ApiPlatform/Contract/ContentNegotiationContractTest.php @@ -16,7 +16,6 @@ */ final class ContentNegotiationContractTest extends AbstractApiTestCase { - #[\Override] protected static string $requestPath = '/api/v2/events'; public function testLdJsonIsSupported(): void diff --git a/tests/ApiPlatform/Contract/DateFormatContractTest.php b/tests/ApiPlatform/Contract/DateFormatContractTest.php index c0e9e72..eb49f13 100644 --- a/tests/ApiPlatform/Contract/DateFormatContractTest.php +++ b/tests/ApiPlatform/Contract/DateFormatContractTest.php @@ -13,7 +13,6 @@ */ final class DateFormatContractTest extends AbstractApiTestCase { - #[\Override] protected static string $requestPath = '/api/v2/events'; private const string ISO_8601_OFFSET = '/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[+-]\d{2}:\d{2}$/'; diff --git a/tests/ApiPlatform/Contract/ErrorContractTest.php b/tests/ApiPlatform/Contract/ErrorContractTest.php index 56a6111..d5656a6 100644 --- a/tests/ApiPlatform/Contract/ErrorContractTest.php +++ b/tests/ApiPlatform/Contract/ErrorContractTest.php @@ -22,7 +22,6 @@ */ final class ErrorContractTest extends AbstractApiTestCase { - #[\Override] protected static string $requestPath = '/api/v2/events'; public function testUnauthenticatedErrorShape(): void diff --git a/tests/ApiPlatform/Contract/ItemContractTest.php b/tests/ApiPlatform/Contract/ItemContractTest.php index 045cb80..3c6ec51 100644 --- a/tests/ApiPlatform/Contract/ItemContractTest.php +++ b/tests/ApiPlatform/Contract/ItemContractTest.php @@ -21,7 +21,6 @@ */ final class ItemContractTest extends AbstractApiTestCase { - #[\Override] protected static string $requestPath = '/api/v2/events'; #[DataProvider('collectionWrappedItemProvider')] diff --git a/tests/ApiPlatform/DailyOccurrencesFilterTest.php b/tests/ApiPlatform/DailyOccurrencesFilterTest.php index db92321..1b9efb6 100644 --- a/tests/ApiPlatform/DailyOccurrencesFilterTest.php +++ b/tests/ApiPlatform/DailyOccurrencesFilterTest.php @@ -14,7 +14,6 @@ */ final class DailyOccurrencesFilterTest extends AbstractApiTestCase { - #[\Override] protected static string $requestPath = '/api/v2/daily_occurrences'; #[DataProvider('getDailyOccurrencesProvider')] diff --git a/tests/ApiPlatform/DailyOccurrencesTest.php b/tests/ApiPlatform/DailyOccurrencesTest.php index 826d711..c7a7883 100644 --- a/tests/ApiPlatform/DailyOccurrencesTest.php +++ b/tests/ApiPlatform/DailyOccurrencesTest.php @@ -13,17 +13,13 @@ final class DailyOccurrencesTest extends AbstractApiTestCase use GetEntitiesTestTrait; use GetItemTestTrait; - #[\Override] protected static string $requestPath = '/api/v2/daily_occurrences'; - #[\Override] protected static string $resourceClass = DailyOccurrence::class; // Members are the raw Elasticsearch _source with no @id/@type, so API // Platform's self-generated collection schema does not apply (see D6). - #[\Override] protected static bool $assertsGeneratedCollectionSchema = false; - #[\Override] protected static int|string $itemId = 10; } diff --git a/tests/ApiPlatform/EventsFilterTest.php b/tests/ApiPlatform/EventsFilterTest.php index d906dab..32b5e0d 100644 --- a/tests/ApiPlatform/EventsFilterTest.php +++ b/tests/ApiPlatform/EventsFilterTest.php @@ -15,7 +15,6 @@ */ final class EventsFilterTest extends AbstractApiTestCase { - #[\Override] protected static string $requestPath = '/api/v2/events'; #[DataProvider('getEventsProvider')] diff --git a/tests/ApiPlatform/EventsTest.php b/tests/ApiPlatform/EventsTest.php index 0c1c154..6b0f0d4 100644 --- a/tests/ApiPlatform/EventsTest.php +++ b/tests/ApiPlatform/EventsTest.php @@ -16,17 +16,13 @@ final class EventsTest extends AbstractApiTestCase use GetEntitiesTestTrait; use GetItemTestTrait; - #[\Override] protected static string $requestPath = '/api/v2/events'; - #[\Override] protected static string $resourceClass = Event::class; // Members are the raw Elasticsearch _source with no @id/@type, so API // Platform's self-generated collection schema does not apply (see D6). - #[\Override] protected static bool $assertsGeneratedCollectionSchema = false; - #[\Override] protected static int|string $itemId = 7; } diff --git a/tests/ApiPlatform/FilterErrorTest.php b/tests/ApiPlatform/FilterErrorTest.php index daf88ec..8982021 100644 --- a/tests/ApiPlatform/FilterErrorTest.php +++ b/tests/ApiPlatform/FilterErrorTest.php @@ -19,7 +19,6 @@ */ final class FilterErrorTest extends AbstractApiTestCase { - #[\Override] protected static string $requestPath = '/api/v2/events'; #[DataProvider('invalidDateRangeProvider')] diff --git a/tests/ApiPlatform/LocationsFilterTest.php b/tests/ApiPlatform/LocationsFilterTest.php index a3c44b6..6866c51 100644 --- a/tests/ApiPlatform/LocationsFilterTest.php +++ b/tests/ApiPlatform/LocationsFilterTest.php @@ -14,7 +14,6 @@ */ final class LocationsFilterTest extends AbstractApiTestCase { - #[\Override] protected static string $requestPath = '/api/v2/locations'; #[DataProvider('getLocationsProvider')] diff --git a/tests/ApiPlatform/LocationsTest.php b/tests/ApiPlatform/LocationsTest.php index bf43a18..4db86b7 100644 --- a/tests/ApiPlatform/LocationsTest.php +++ b/tests/ApiPlatform/LocationsTest.php @@ -13,17 +13,13 @@ final class LocationsTest extends AbstractApiTestCase use GetEntitiesTestTrait; use GetItemTestTrait; - #[\Override] protected static string $requestPath = '/api/v2/locations'; - #[\Override] protected static string $resourceClass = Location::class; // Members are the raw Elasticsearch _source with no @id/@type, so API // Platform's self-generated collection schema does not apply (see D6). - #[\Override] protected static bool $assertsGeneratedCollectionSchema = false; - #[\Override] protected static int|string $itemId = 4; } diff --git a/tests/ApiPlatform/OccurrencesFilterTest.php b/tests/ApiPlatform/OccurrencesFilterTest.php index 70993ef..6018da4 100644 --- a/tests/ApiPlatform/OccurrencesFilterTest.php +++ b/tests/ApiPlatform/OccurrencesFilterTest.php @@ -15,7 +15,6 @@ */ final class OccurrencesFilterTest extends AbstractApiTestCase { - #[\Override] protected static string $requestPath = '/api/v2/occurrences'; #[DataProvider('getOccurrencesProvider')] diff --git a/tests/ApiPlatform/OccurrencesTest.php b/tests/ApiPlatform/OccurrencesTest.php index a021486..b516b84 100644 --- a/tests/ApiPlatform/OccurrencesTest.php +++ b/tests/ApiPlatform/OccurrencesTest.php @@ -13,17 +13,13 @@ final class OccurrencesTest extends AbstractApiTestCase use GetEntitiesTestTrait; use GetItemTestTrait; - #[\Override] protected static string $requestPath = '/api/v2/occurrences'; - #[\Override] protected static string $resourceClass = Occurrence::class; // Members are the raw Elasticsearch _source with no @id/@type, so API // Platform's self-generated collection schema does not apply (see D6). - #[\Override] protected static bool $assertsGeneratedCollectionSchema = false; - #[\Override] protected static int|string $itemId = 10; } diff --git a/tests/ApiPlatform/OrganizationsFilterTest.php b/tests/ApiPlatform/OrganizationsFilterTest.php index b84dcfc..73c5251 100644 --- a/tests/ApiPlatform/OrganizationsFilterTest.php +++ b/tests/ApiPlatform/OrganizationsFilterTest.php @@ -14,7 +14,6 @@ */ final class OrganizationsFilterTest extends AbstractApiTestCase { - #[\Override] protected static string $requestPath = '/api/v2/organizations'; #[DataProvider('getOrganizationsProvider')] diff --git a/tests/ApiPlatform/OrganizationsTest.php b/tests/ApiPlatform/OrganizationsTest.php index bef3062..92e7eb6 100644 --- a/tests/ApiPlatform/OrganizationsTest.php +++ b/tests/ApiPlatform/OrganizationsTest.php @@ -13,17 +13,13 @@ final class OrganizationsTest extends AbstractApiTestCase use GetEntitiesTestTrait; use GetItemTestTrait; - #[\Override] protected static string $requestPath = '/api/v2/organizations'; - #[\Override] protected static string $resourceClass = Organization::class; // Members are the raw Elasticsearch _source with no @id/@type, so API // Platform's self-generated collection schema does not apply (see D6). - #[\Override] protected static bool $assertsGeneratedCollectionSchema = false; - #[\Override] protected static int|string $itemId = 9; } diff --git a/tests/ApiPlatform/PaginationTest.php b/tests/ApiPlatform/PaginationTest.php index 5e23950..46d4adb 100644 --- a/tests/ApiPlatform/PaginationTest.php +++ b/tests/ApiPlatform/PaginationTest.php @@ -12,7 +12,6 @@ */ final class PaginationTest extends AbstractApiTestCase { - #[\Override] protected static string $requestPath = '/api/v2/events'; #[DataProvider('resourceProvider')] diff --git a/tests/ApiPlatform/SortTest.php b/tests/ApiPlatform/SortTest.php index bac6a23..7e707ad 100644 --- a/tests/ApiPlatform/SortTest.php +++ b/tests/ApiPlatform/SortTest.php @@ -16,7 +16,6 @@ */ final class SortTest extends AbstractApiTestCase { - #[\Override] protected static string $requestPath = '/api/v2/events'; public function testEventsAreSortedByTitleAscending(): void diff --git a/tests/ApiPlatform/TagsFilterTest.php b/tests/ApiPlatform/TagsFilterTest.php index dc82b6e..f1a6d74 100644 --- a/tests/ApiPlatform/TagsFilterTest.php +++ b/tests/ApiPlatform/TagsFilterTest.php @@ -15,7 +15,6 @@ */ final class TagsFilterTest extends AbstractApiTestCase { - #[\Override] protected static string $requestPath = '/api/v2/tags'; #[DataProvider('getTagsProvider')] diff --git a/tests/ApiPlatform/TagsTest.php b/tests/ApiPlatform/TagsTest.php index b958e32..c3b0610 100644 --- a/tests/ApiPlatform/TagsTest.php +++ b/tests/ApiPlatform/TagsTest.php @@ -16,15 +16,11 @@ final class TagsTest extends AbstractApiTestCase use GetEntitiesTestTrait; use GetItemTestTrait; - #[\Override] protected static string $requestPath = '/api/v2/tags'; - #[\Override] protected static string $resourceClass = Tag::class; - #[\Override] protected static int|string $itemId = 'aros'; - #[\Override] protected static int|string $unknownItemId = 'no-such-tag-slug'; } diff --git a/tests/ApiPlatform/VocabulariesFilterTest.php b/tests/ApiPlatform/VocabulariesFilterTest.php index 3870fff..51533e0 100644 --- a/tests/ApiPlatform/VocabulariesFilterTest.php +++ b/tests/ApiPlatform/VocabulariesFilterTest.php @@ -15,7 +15,6 @@ */ final class VocabulariesFilterTest extends AbstractApiTestCase { - #[\Override] protected static string $requestPath = '/api/v2/vocabularies'; #[DataProvider('getVocabulariesProvider')] diff --git a/tests/ApiPlatform/VocabulariesTest.php b/tests/ApiPlatform/VocabulariesTest.php index b569a95..5f86b89 100644 --- a/tests/ApiPlatform/VocabulariesTest.php +++ b/tests/ApiPlatform/VocabulariesTest.php @@ -16,15 +16,11 @@ final class VocabulariesTest extends AbstractApiTestCase use GetEntitiesTestTrait; use GetItemTestTrait; - #[\Override] protected static string $requestPath = '/api/v2/vocabularies'; - #[\Override] protected static string $resourceClass = Vocabulary::class; - #[\Override] protected static int|string $itemId = 'aarhusguiden'; - #[\Override] protected static int|string $unknownItemId = 'no-such-vocabulary-slug'; } diff --git a/tests/Command/ApiKeysAddCommandTest.php b/tests/Command/ApiKeysAddCommandTest.php index e5c1b79..b3389ec 100644 --- a/tests/Command/ApiKeysAddCommandTest.php +++ b/tests/Command/ApiKeysAddCommandTest.php @@ -60,7 +60,7 @@ public function testReplacesMultiLineValueAndKeepsOtherLines(): void $this->assertFileDoesNotExist($this->dir.'/.env.local.backup'); $this->assertFileDoesNotExist($this->dir.'/.env.local.temp'); - $env = new Dotenv()->parse((string) file_get_contents($this->dir.'/.env.local')); + $env = (new Dotenv())->parse((string) file_get_contents($this->dir.'/.env.local')); $this->assertSame('http://elasticsearch:9200', $env['INDEX_URL']); $this->assertSame('value', $env['OTHER']); $keys = $this->keys(); @@ -181,7 +181,7 @@ private function runCommand(string $username, array $answers = []): CommandTeste */ private function keys(): array { - $env = new Dotenv()->parse((string) file_get_contents($this->dir.'/.env.local')); + $env = (new Dotenv())->parse((string) file_get_contents($this->dir.'/.env.local')); return json_decode((string) $env['APP_API_KEYS'], true, 512, JSON_THROW_ON_ERROR); } diff --git a/tests/Unit/Service/ElasticSearch/SearchParamsBuilderTest.php b/tests/Unit/Service/ElasticSearch/SearchParamsBuilderTest.php index 70ecf33..7ba3c20 100644 --- a/tests/Unit/Service/ElasticSearch/SearchParamsBuilderTest.php +++ b/tests/Unit/Service/ElasticSearch/SearchParamsBuilderTest.php @@ -33,7 +33,7 @@ private function withFilters(array $clauses): array // Goal: with no filters the query defaults to match_all and pagination/sort are set. public function testDefaultsToMatchAllWithPagination(): void { - $params = new SearchParamsBuilder()->buildParams(IndexName::Events->value, $this->noFilters(), 20, 5); + $params = (new SearchParamsBuilder())->buildParams(IndexName::Events->value, $this->noFilters(), 20, 5); $this->assertSame(IndexName::Events->value, $params['index']); $this->assertEquals(['match_all' => (object) []], $params['body']['query']); @@ -45,7 +45,7 @@ public function testDefaultsToMatchAllWithPagination(): void // Goal: a single associative clause (e.g. a MatchFilter hit) is wrapped in bool/must. public function testSingleClauseWrappedInBoolMust(): void { - $params = new SearchParamsBuilder()->buildParams( + $params = (new SearchParamsBuilder())->buildParams( IndexName::Events->value, $this->withFilters([['match' => ['title' => 'x']]]), 0, @@ -59,7 +59,7 @@ public function testSingleClauseWrappedInBoolMust(): void // nested under numeric keys. public function testListClauseIsFlattenedIntoMust(): void { - $params = new SearchParamsBuilder()->buildParams( + $params = (new SearchParamsBuilder())->buildParams( IndexName::Events->value, $this->withFilters([[['terms' => ['organizer.entityId' => ['9'], 'boost' => 1.0]]]]), 0, @@ -72,7 +72,7 @@ public function testListClauseIsFlattenedIntoMust(): void // Goal: multiple clauses accumulate under a single bool/must. public function testMultipleClausesCombine(): void { - $params = new SearchParamsBuilder()->buildParams( + $params = (new SearchParamsBuilder())->buildParams( IndexName::Events->value, $this->withFilters([ ['match' => ['title' => 'x']], @@ -92,7 +92,7 @@ public function testMultipleClausesCombine(): void #[DataProvider('sortProvider')] public function testSortPerIndex(string $index, array $expectedSort): void { - $params = new SearchParamsBuilder()->buildParams($index, $this->noFilters(), 0, 10); + $params = (new SearchParamsBuilder())->buildParams($index, $this->noFilters(), 0, 10); $this->assertSame($expectedSort, $params['body']['sort']); } diff --git a/tests/bootstrap.php b/tests/bootstrap.php index bc44ee1..8e0530d 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -7,7 +7,7 @@ require dirname(__DIR__).'/vendor/autoload.php'; if (method_exists(Dotenv::class, 'bootEnv')) { - new Dotenv()->bootEnv(dirname(__DIR__).'/.env'); + (new Dotenv())->bootEnv(dirname(__DIR__).'/.env'); } if ($_SERVER['APP_DEBUG']) {