diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index b2bf280..8933119 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -29,25 +29,6 @@ jobs: run: | task --yes api:test - coding-standards-yaml: - name: Coding standards YAML - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - # https://taskfile.dev/installation/#github-actions - - uses: arduino/setup-task@v2 - - - run: | - docker network create frontend - - - name: Check YAML coding standards - run: | - task --yes coding-standards:yaml:check - - - name: Check that nothing has changed - run: git diff --exit-code - code-analysis-phpstan: runs-on: ubuntu-latest name: PHPStan static analysis diff --git a/.github/workflows/yaml.yaml b/.github/workflows/yaml.yaml new file mode 100644 index 0000000..89a2ad3 --- /dev/null +++ b/.github/workflows/yaml.yaml @@ -0,0 +1,33 @@ +# Do not edit this file! Make a pull request on changing +# github/workflows/symfony/yaml.yaml in +# https://github.com/itk-dev/devops_itkdev-docker if need be. + +### ### Symfony YAML +### +### Validates Symfony YAML files. +### +### #### Assumptions +### +### 1. A docker compose service named `prettier` for running +### [Prettier](https://prettier.io/) exists. + +name: YAML + +on: + pull_request: + push: + branches: + - main + - develop + +jobs: + yaml-lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - run: | + docker network create frontend + + - run: | + docker compose run --rm prettier '.github/workflows/*.{yml,yaml}' 'config/**/*.{yml,yaml}' --check diff --git a/CHANGELOG.md b/CHANGELOG.md index 34ac90b..ee9f165 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ See [keep a changelog] for information about writing changes to this log. ## [Unreleased] +- [PR-21](https://github.com/itk-dev/event-database-api/pull/21) + Linted YAML - [PR-18](https://github.com/itk-dev/event-database-api/pull/18) - Updated docker compose setup - Added simple API tests and resolved some deprecations diff --git a/Taskfile.yml b/Taskfile.yml index 9ea978e..9646ba6 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -10,7 +10,7 @@ vars: DOCKER_COMPOSE: '{{.TASK_DOCKER_COMPOSE | default "docker compose"}}' # We're not yet ready to normalize config files … - YAML_FILES_GLOB: "Taskfile.yml .github/workflows/**.{yml,yaml} phpstan.dist.neon" + YAML_FILES_GLOB: "'.github/workflows/*.{yml,yaml}' 'config/**/*.{yml,yaml}' Taskfile.yml phpstan.dist.neon" tasks: site:update: diff --git a/config/packages/api_platform.yaml b/config/packages/api_platform.yaml index 44441f1..29f82bf 100644 --- a/config/packages/api_platform.yaml +++ b/config/packages/api_platform.yaml @@ -1,58 +1,58 @@ api_platform: - title: '%env(OPENAPI_TITLE)%' - description: '%env(OPENAPI_DESCRIPTION)%' - version: 2.0.0 - show_webby: false - - openapi: - contact: - name: '%env(OPENAPI_CONTACT_NAME)%' - url: '%env(OPENAPI_CONTACT_URL)%' - email: '%env(OPENAPI_CONTACT_EMAIL)%' - termsOfService: '%env(OPENAPI_TERMS_OF_SERVICE)%' - - formats: - jsonld: ['application/ld+json'] - - docs_formats: - jsonld: ['application/ld+json'] - jsonopenapi: ['application/vnd.openapi+json'] - html: ['text/html'] - - mapping: - paths: - - '%kernel.project_dir%/src/Api/Dto' - - doctrine: - enabled: false - - swagger: - api_keys: - ApiKeyAuth: - name: 'X-Api-Key' - type: header - - elasticsearch: - hosts: ['%env(INDEX_URL)%'] - - defaults: - stateless: true - pagination_items_per_page: '%env(int:PAGINATION_ITEMS_PER_PAGE)%' - pagination_maximum_items_per_page: '%env(int:PAGINATION_MAXIMUM_ITEMS_PER_PAGE)%' - cache_headers: - max_age: 3600 - shared_max_age: 3600 - vary: ['Content-Type', 'Authorization', 'Origin'] - extra_properties: - # Note: The api-platform/core 3.4 recipe sets `standard_put` eventhough - # https://api-platform.com/docs/core/upgrade-guide/#api-platform-34 - # (probably) tells us to remove it - # standard_put: true - # https://api-platform.com/docs/core/upgrade-guide/#api-platform-34 - rfc_7807_compliant_errors: true - # Note: The api-platform/core 3.4 recipe wants `use_symfony_listeners` to be true - use_symfony_listeners: false - - # https://api-platform.com/docs/core/upgrade-guide/#upgrade-guide - serializer: - hydra_prefix: true + title: "%env(OPENAPI_TITLE)%" + description: "%env(OPENAPI_DESCRIPTION)%" + version: 2.0.0 + show_webby: false + + openapi: + contact: + name: "%env(OPENAPI_CONTACT_NAME)%" + url: "%env(OPENAPI_CONTACT_URL)%" + email: "%env(OPENAPI_CONTACT_EMAIL)%" + termsOfService: "%env(OPENAPI_TERMS_OF_SERVICE)%" + + formats: + jsonld: ["application/ld+json"] + + docs_formats: + jsonld: ["application/ld+json"] + jsonopenapi: ["application/vnd.openapi+json"] + html: ["text/html"] + + mapping: + paths: + - "%kernel.project_dir%/src/Api/Dto" + + doctrine: + enabled: false + + swagger: + api_keys: + ApiKeyAuth: + name: "X-Api-Key" + type: header + + elasticsearch: + hosts: ["%env(INDEX_URL)%"] + + defaults: + stateless: true + pagination_items_per_page: "%env(int:PAGINATION_ITEMS_PER_PAGE)%" + pagination_maximum_items_per_page: "%env(int:PAGINATION_MAXIMUM_ITEMS_PER_PAGE)%" + cache_headers: + max_age: 3600 + shared_max_age: 3600 + vary: ["Content-Type", "Authorization", "Origin"] + extra_properties: + # Note: The api-platform/core 3.4 recipe sets `standard_put` eventhough + # https://api-platform.com/docs/core/upgrade-guide/#api-platform-34 + # (probably) tells us to remove it + # standard_put: true + # https://api-platform.com/docs/core/upgrade-guide/#api-platform-34 + rfc_7807_compliant_errors: true + # Note: The api-platform/core 3.4 recipe wants `use_symfony_listeners` to be true + use_symfony_listeners: false + + # https://api-platform.com/docs/core/upgrade-guide/#upgrade-guide + serializer: + hydra_prefix: true diff --git a/config/packages/cache.yaml b/config/packages/cache.yaml index 6899b72..72cca01 100644 --- a/config/packages/cache.yaml +++ b/config/packages/cache.yaml @@ -1,19 +1,19 @@ framework: - cache: - # Unique name of your app: used to compute stable namespaces for cache keys. - #prefix_seed: your_vendor_name/app_name + cache: + # Unique name of your app: used to compute stable namespaces for cache keys. + #prefix_seed: your_vendor_name/app_name - # The "app" cache stores to the filesystem by default. - # The data in this cache should persist between deploys. - # Other options include: + # The "app" cache stores to the filesystem by default. + # The data in this cache should persist between deploys. + # Other options include: - # Redis - #app: cache.adapter.redis - #default_redis_provider: redis://localhost + # Redis + #app: cache.adapter.redis + #default_redis_provider: redis://localhost - # APCu (not recommended with heavy random-write workloads as memory fragmentation can cause perf issues) - #app: cache.adapter.apcu + # APCu (not recommended with heavy random-write workloads as memory fragmentation can cause perf issues) + #app: cache.adapter.apcu - # Namespaced pools use the above "app" backend by default - #pools: - #my.dedicated.cache: null + # Namespaced pools use the above "app" backend by default + #pools: + #my.dedicated.cache: null diff --git a/config/packages/framework.yaml b/config/packages/framework.yaml index 7d97622..3db7510 100644 --- a/config/packages/framework.yaml +++ b/config/packages/framework.yaml @@ -1,27 +1,27 @@ # see https://symfony.com/doc/current/reference/configuration/framework.html framework: - secret: '%env(APP_SECRET)%' - annotations: false - http_method_override: false - handle_all_throwables: true + secret: "%env(APP_SECRET)%" + annotations: false + http_method_override: false + handle_all_throwables: true - # Enables session support. Note that the session will ONLY be started if you read or write from it. - # Remove or comment this section to explicitly disable session support. - session: - handler_id: null - cookie_secure: auto - cookie_samesite: lax + # Enables session support. Note that the session will ONLY be started if you read or write from it. + # Remove or comment this section to explicitly disable session support. + session: + handler_id: null + cookie_secure: auto + cookie_samesite: lax - #esi: true - #fragments: true - php_errors: - log: true + #esi: true + #fragments: true + php_errors: + log: true - assets: - base_path: '%env(APP_PATH_PREFIX)%' + assets: + base_path: "%env(APP_PATH_PREFIX)%" when@test: - framework: - test: true - session: - storage_factory_id: session.storage.factory.mock_file + framework: + test: true + session: + storage_factory_id: session.storage.factory.mock_file diff --git a/config/packages/http_discovery.yaml b/config/packages/http_discovery.yaml index 2a789e7..3e7ef4b 100644 --- a/config/packages/http_discovery.yaml +++ b/config/packages/http_discovery.yaml @@ -1,10 +1,10 @@ services: - Psr\Http\Message\RequestFactoryInterface: '@http_discovery.psr17_factory' - Psr\Http\Message\ResponseFactoryInterface: '@http_discovery.psr17_factory' - Psr\Http\Message\ServerRequestFactoryInterface: '@http_discovery.psr17_factory' - Psr\Http\Message\StreamFactoryInterface: '@http_discovery.psr17_factory' - Psr\Http\Message\UploadedFileFactoryInterface: '@http_discovery.psr17_factory' - Psr\Http\Message\UriFactoryInterface: '@http_discovery.psr17_factory' + Psr\Http\Message\RequestFactoryInterface: "@http_discovery.psr17_factory" + Psr\Http\Message\ResponseFactoryInterface: "@http_discovery.psr17_factory" + Psr\Http\Message\ServerRequestFactoryInterface: "@http_discovery.psr17_factory" + Psr\Http\Message\StreamFactoryInterface: "@http_discovery.psr17_factory" + Psr\Http\Message\UploadedFileFactoryInterface: "@http_discovery.psr17_factory" + Psr\Http\Message\UriFactoryInterface: "@http_discovery.psr17_factory" - http_discovery.psr17_factory: - class: Http\Discovery\Psr17Factory + http_discovery.psr17_factory: + class: Http\Discovery\Psr17Factory diff --git a/config/packages/nelmio_cors.yaml b/config/packages/nelmio_cors.yaml index 8977033..12577e6 100644 --- a/config/packages/nelmio_cors.yaml +++ b/config/packages/nelmio_cors.yaml @@ -1,10 +1,10 @@ nelmio_cors: - defaults: - origin_regex: true - allow_origin: ['%env(CORS_ALLOW_ORIGIN)%'] - allow_methods: ['GET', 'OPTIONS'] - allow_headers: ['Content-Type', 'X-Api-Key'] - expose_headers: ['Link'] - max_age: 3600 - paths: - '^/': null + defaults: + origin_regex: true + allow_origin: ["%env(CORS_ALLOW_ORIGIN)%"] + allow_methods: ["GET", "OPTIONS"] + allow_headers: ["Content-Type", "X-Api-Key"] + expose_headers: ["Link"] + max_age: 3600 + paths: + "^/": null diff --git a/config/packages/routing.yaml b/config/packages/routing.yaml index 4b766ce..c0f1474 100644 --- a/config/packages/routing.yaml +++ b/config/packages/routing.yaml @@ -1,12 +1,12 @@ framework: - router: - utf8: true + router: + utf8: true - # Configure how to generate URLs in non-HTTP contexts, such as CLI commands. - # See https://symfony.com/doc/current/routing.html#generating-urls-in-commands - #default_uri: http://localhost + # Configure how to generate URLs in non-HTTP contexts, such as CLI commands. + # See https://symfony.com/doc/current/routing.html#generating-urls-in-commands + #default_uri: http://localhost when@prod: - framework: - router: - strict_requirements: null + framework: + router: + strict_requirements: null diff --git a/config/packages/security.yaml b/config/packages/security.yaml index 0d1e5f5..ee040de 100644 --- a/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -1,44 +1,44 @@ security: - # https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords - password_hashers: - Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto' - # https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider - providers: - users_in_memory: { memory: null } - api_user_provider: - id: App\Security\ApiUserProvider - firewalls: - dev: - pattern: ^/(_(profiler|wdt)|css|images|js)/ - security: false - main: - stateless: true - custom_authenticators: - - App\Security\ApiKeyAuthenticator - provider: api_user_provider + # https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords + password_hashers: + Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: "auto" + # https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider + providers: + users_in_memory: { memory: null } + api_user_provider: + id: App\Security\ApiUserProvider + firewalls: + dev: + pattern: ^/(_(profiler|wdt)|css|images|js)/ + security: false + main: + stateless: true + custom_authenticators: + - App\Security\ApiKeyAuthenticator + provider: api_user_provider - # activate different ways to authenticate - # https://symfony.com/doc/current/security.html#the-firewall + # activate different ways to authenticate + # https://symfony.com/doc/current/security.html#the-firewall - # https://symfony.com/doc/current/security/impersonating_user.html - # switch_user: true + # https://symfony.com/doc/current/security/impersonating_user.html + # switch_user: true - # Easy way to control access for large sections of your site - # Note: Only the *first* access control that matches will be used - access_control: - - { path: ^/api/v2/docs, roles: PUBLIC_ACCESS } - - { path: ^/api, roles: IS_AUTHENTICATED_FULLY } - # - { path: ^/profile, roles: ROLE_USER } + # Easy way to control access for large sections of your site + # Note: Only the *first* access control that matches will be used + access_control: + - { path: ^/api/v2/docs, roles: PUBLIC_ACCESS } + - { path: ^/api, roles: IS_AUTHENTICATED_FULLY } + # - { path: ^/profile, roles: ROLE_USER } when@test: - security: - password_hashers: - # By default, password hashers are resource intensive and take time. This is - # important to generate secure password hashes. In tests however, secure hashes - # are not important, waste resources and increase test times. The following - # reduces the work factor to the lowest possible values. - Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: - algorithm: auto - cost: 4 # Lowest possible value for bcrypt - time_cost: 3 # Lowest possible value for argon - memory_cost: 10 # Lowest possible value for argon + security: + password_hashers: + # By default, password hashers are resource intensive and take time. This is + # important to generate secure password hashes. In tests however, secure hashes + # are not important, waste resources and increase test times. The following + # reduces the work factor to the lowest possible values. + Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: + algorithm: auto + cost: 4 # Lowest possible value for bcrypt + time_cost: 3 # Lowest possible value for argon + memory_cost: 10 # Lowest possible value for argon diff --git a/config/packages/twig.yaml b/config/packages/twig.yaml index b46e8b3..43c7276 100644 --- a/config/packages/twig.yaml +++ b/config/packages/twig.yaml @@ -1,9 +1,9 @@ twig: - file_name_pattern: '*.twig' + file_name_pattern: "*.twig" - globals: - app_project_uri: '%env(APP_PROJECT_URI)%' + globals: + app_project_uri: "%env(APP_PROJECT_URI)%" when@test: - twig: - strict_variables: true + twig: + strict_variables: true diff --git a/config/packages/validator.yaml b/config/packages/validator.yaml index 0201281..876481d 100644 --- a/config/packages/validator.yaml +++ b/config/packages/validator.yaml @@ -1,13 +1,13 @@ framework: - validation: - email_validation_mode: html5 + validation: + email_validation_mode: html5 - # Enables validator auto-mapping support. - # For instance, basic validation constraints will be inferred from Doctrine's metadata. - #auto_mapping: - # App\Entity\: [] + # Enables validator auto-mapping support. + # For instance, basic validation constraints will be inferred from Doctrine's metadata. + #auto_mapping: + # App\Entity\: [] when@test: - framework: - validation: - not_compromised_password: false + framework: + validation: + not_compromised_password: false diff --git a/config/packages/web_profiler.yaml b/config/packages/web_profiler.yaml index 1e039b7..1e77fbc 100644 --- a/config/packages/web_profiler.yaml +++ b/config/packages/web_profiler.yaml @@ -1,11 +1,11 @@ when@dev: - web_profiler: - toolbar: true + web_profiler: + toolbar: true - framework: - profiler: - collect_serializer_data: true + framework: + profiler: + collect_serializer_data: true when@test: - framework: - profiler: { collect: false } + framework: + profiler: { collect: false } diff --git a/config/routes.yaml b/config/routes.yaml index 41ef814..2d0ef99 100644 --- a/config/routes.yaml +++ b/config/routes.yaml @@ -1,5 +1,5 @@ controllers: - resource: - path: ../src/Controller/ - namespace: App\Controller - type: attribute + resource: + path: ../src/Controller/ + namespace: App\Controller + type: attribute diff --git a/config/routes/api_platform.yaml b/config/routes/api_platform.yaml index 6dbbb2d..251216e 100644 --- a/config/routes/api_platform.yaml +++ b/config/routes/api_platform.yaml @@ -1,4 +1,4 @@ api_platform: - resource: . - type: api_platform - prefix: '%api.path_prefix%' + resource: . + type: api_platform + prefix: "%api.path_prefix%" diff --git a/config/routes/framework.yaml b/config/routes/framework.yaml index 0fc74bb..cce01c1 100644 --- a/config/routes/framework.yaml +++ b/config/routes/framework.yaml @@ -1,4 +1,4 @@ when@dev: - _errors: - resource: '@FrameworkBundle/Resources/config/routing/errors.xml' - prefix: /_error + _errors: + resource: "@FrameworkBundle/Resources/config/routing/errors.xml" + prefix: /_error diff --git a/config/routes/security.yaml b/config/routes/security.yaml index f853be1..3cb5ef0 100644 --- a/config/routes/security.yaml +++ b/config/routes/security.yaml @@ -1,3 +1,3 @@ _security_logout: - resource: security.route_loader.logout - type: service + resource: security.route_loader.logout + type: service diff --git a/config/routes/web_profiler.yaml b/config/routes/web_profiler.yaml index 8d85319..e004603 100644 --- a/config/routes/web_profiler.yaml +++ b/config/routes/web_profiler.yaml @@ -1,8 +1,8 @@ when@dev: - web_profiler_wdt: - resource: '@WebProfilerBundle/Resources/config/routing/wdt.xml' - prefix: /_wdt + web_profiler_wdt: + resource: "@WebProfilerBundle/Resources/config/routing/wdt.xml" + prefix: /_wdt - web_profiler_profiler: - resource: '@WebProfilerBundle/Resources/config/routing/profiler.xml' - prefix: /_profiler + web_profiler_profiler: + resource: "@WebProfilerBundle/Resources/config/routing/profiler.xml" + prefix: /_profiler diff --git a/config/services.yaml b/config/services.yaml index 662a287..42e9aae 100644 --- a/config/services.yaml +++ b/config/services.yaml @@ -4,71 +4,70 @@ # Put parameters here that don't need to change on each machine where the app is deployed # https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration parameters: - api.path_prefix: '%env(string:APP_PATH_PREFIX)%' + api.path_prefix: "%env(string:APP_PATH_PREFIX)%" services: - # default configuration for services in *this* file - _defaults: - autowire: true # Automatically injects dependencies in your services. - autoconfigure: true # Automatically registers your services as commands, event subscribers, etc. - - # makes classes in src/ available to be used as services - # this creates a service per class whose id is the fully-qualified class name - App\: - resource: '../src/' - exclude: - - '../src/DependencyInjection/' - - '../src/Entity/' - - '../src/Kernel.php' - - # add more service definitions when explicit configuration is needed - # please note that last definitions always *replace* previous ones - - - App\Api\State\AbstractProvider: - arguments: - $filterLocator: '@api_platform.filter_locator' - - App\Api\State\DailyOccurrenceRepresentationProvider: - arguments: - $filterLocator: '@api_platform.filter_locator' - - App\Api\State\EventRepresentationProvider: - arguments: - $filterLocator: '@api_platform.filter_locator' - - App\Api\State\LocationRepresentationProvider: - arguments: - $filterLocator: '@api_platform.filter_locator' - - App\Api\State\OccurrenceRepresentationProvider: - arguments: - $filterLocator: '@api_platform.filter_locator' - - App\Api\State\OrganizationRepresentationProvider: - arguments: - $filterLocator: '@api_platform.filter_locator' - - App\Api\State\TagRepresentationProvider: - arguments: - $filterLocator: '@api_platform.filter_locator' - - App\Api\State\VocabularyRepresentationProvider: - arguments: - $filterLocator: '@api_platform.filter_locator' - - App\Command\FixturesLoadCommand: - arguments: - $appEnv: '%env(string:APP_ENV)%' - - App\Security\ApiUserProvider: - arguments: - $apikeys: '%env(json:APP_API_KEYS)%' - - Elastic\Elasticsearch\Client: - factory: [ '@Elastic\Elasticsearch\ClientBuilder', fromConfig ] - arguments: - $config: - hosts: [ '%env(INDEX_URL)%' ] - - Elastic\Elasticsearch\ClientBuilder: ~ + # default configuration for services in *this* file + _defaults: + autowire: true # Automatically injects dependencies in your services. + autoconfigure: true # Automatically registers your services as commands, event subscribers, etc. + + # makes classes in src/ available to be used as services + # this creates a service per class whose id is the fully-qualified class name + App\: + resource: "../src/" + exclude: + - "../src/DependencyInjection/" + - "../src/Entity/" + - "../src/Kernel.php" + + # add more service definitions when explicit configuration is needed + # please note that last definitions always *replace* previous ones + + App\Api\State\AbstractProvider: + arguments: + $filterLocator: "@api_platform.filter_locator" + + App\Api\State\DailyOccurrenceRepresentationProvider: + arguments: + $filterLocator: "@api_platform.filter_locator" + + App\Api\State\EventRepresentationProvider: + arguments: + $filterLocator: "@api_platform.filter_locator" + + App\Api\State\LocationRepresentationProvider: + arguments: + $filterLocator: "@api_platform.filter_locator" + + App\Api\State\OccurrenceRepresentationProvider: + arguments: + $filterLocator: "@api_platform.filter_locator" + + App\Api\State\OrganizationRepresentationProvider: + arguments: + $filterLocator: "@api_platform.filter_locator" + + App\Api\State\TagRepresentationProvider: + arguments: + $filterLocator: "@api_platform.filter_locator" + + App\Api\State\VocabularyRepresentationProvider: + arguments: + $filterLocator: "@api_platform.filter_locator" + + App\Command\FixturesLoadCommand: + arguments: + $appEnv: "%env(string:APP_ENV)%" + + App\Security\ApiUserProvider: + arguments: + $apikeys: "%env(json:APP_API_KEYS)%" + + Elastic\Elasticsearch\Client: + factory: ['@Elastic\Elasticsearch\ClientBuilder', fromConfig] + arguments: + $config: + hosts: ["%env(INDEX_URL)%"] + + Elastic\Elasticsearch\ClientBuilder: ~