diff --git a/docs/book/v7/core-features/error-reporting.md b/docs/book/v7/core-features/error-reporting.md index dd64eddf..1f3c2f3b 100644 --- a/docs/book/v7/core-features/error-reporting.md +++ b/docs/book/v7/core-features/error-reporting.md @@ -63,6 +63,10 @@ Below are the requirements for an application to be able to send error messages - `ErrorReportServiceInterface::class` . `tokens` must contain at least one token. - At least one of `ErrorReportServiceInterface::class` . `domain_whitelist`/`ip_whitelist` must have at least one value. +> When `enabled` is not `true`, Dotkernel API 7.2 answers `403 Forbidden` (`ForbiddenException`). +> Versions 7.0 and 7.1 answer `400 Bad Request` (`RuntimeException`). +> Update to the latest Dotkernel API 7.x release to get the current behavior, see [Upgrading from 7.1 to 7.2](../upgrading/UPGRADE-7.2.md). + > In `src/App/src/Service/ErrorReportService.php`, the method `checkRequest()` tries to validate the request by checking matches for `domain_whitelist` with `isMatchingDomain()` and for `ip_whitelist` with `isMatchingIpAddress()`. > If both return `false`, a `ForbiddenException` is thrown and the error message does not get stored. diff --git a/docs/book/v7/core-features/exceptions.md b/docs/book/v7/core-features/exceptions.md index 37b8f91a..2e3e619b 100644 --- a/docs/book/v7/core-features/exceptions.md +++ b/docs/book/v7/core-features/exceptions.md @@ -83,12 +83,27 @@ throw BadRequestException::create( | `ForbiddenException` | `403 Forbidden` | `ErrorReportPermissionMiddleware`, `ErrorReportService` (including when error reporting is disabled) | | `NotFoundException` | `404 Not Found` | handlers and services, for a missing resource | | `NotAcceptableException` | `406 Not Acceptable` | `ContentNegotiationMiddleware` | -| `ConflictException` | `409 Conflict` | `UserService`, `AdminService`, `DeprecationMiddleware`, `ResourceProviderMiddleware` | -| `ExpiredException` | `410 Gone` | the password reset handlers | +| `ConflictException` | `409 Conflict` | `UserService`, `AdminService`, `ResourceProviderMiddleware` | +| `ExpiredException` | `410 Gone` | the password reset handlers, `DeprecationMiddleware` once a `sunset` date is reached | | `UnsupportedMediaTypeException` | `415 Unsupported Media Type` | `ContentNegotiationMiddleware` | | `RuntimeException` | `500 Internal Server Error` | `Renderer`, `HandlerService`, `ErrorReportService`, `HandlerDelegatorFactory`, `ProblemDetailsDelegatorFactory` | | `SunsetException` | `500 Internal Server Error` | `BaseDeprecation`, on an invalid `sunset` date | +### Version differences + +The table above and the sections below describe Dotkernel API 7.2. +Earlier 7.x versions behave differently in these places: + +| Behavior | 7.0 | 7.1 | 7.2 | +| --- | --- | --- | --- | +| `RuntimeException` status | `400 Bad Request` | `400 Bad Request` | `500 Internal Server Error` | +| `SunsetException` status | `401 Unauthorized` | `401 Unauthorized` | `500 Internal Server Error` | +| Error reporting disabled | `RuntimeException`, `400 Bad Request` | `RuntimeException`, `400 Bad Request` | `ForbiddenException`, `403 Forbidden` | +| Malformed JSON request body | no dedicated handling: the problem details middleware answers with the exception's `400` only when exception details are exposed, otherwise with `500` | `MalformedRequestBodyMiddleware` throws `BadRequestException`, `400 Bad Request` | `MalformedRequestBodyMiddleware` throws `BadRequestException`, `400 Bad Request` | + +> Update to the latest Dotkernel API 7.x release to get the current behavior. +> See [Upgrading from 7.0 to 7.1](../upgrading/UPGRADE-7.1.md) and [Upgrading from 7.1 to 7.2](../upgrading/UPGRADE-7.2.md). + ### `BadRequestException` thrown when - The client tries to **create/update a resource**, but the **request data is invalid/incomplete** (example: client tries to create an account, but does not send the required `identity` field) @@ -139,6 +154,7 @@ Note that this is not what a failed RBAC check produces — see [How it works](# - The **resource cannot be accessed** - because it has **expired** (example: account activation link) - because it has been **consumed** (example: one-time password) + - because it has **reached its sunset date** (example: an endpoint marked with `ResourceDeprecation`), see [API Evolution pattern](../tutorials/api-evolution.md) ### `UnsupportedMediaTypeException` thrown when @@ -296,7 +312,7 @@ It returns `409 Conflict`. **Q: What does `ExpiredException` cover?** -A: Resources that can no longer be used because they expired, such as an activation link, or because they were already consumed, such as a one-time password. +A: Resources that can no longer be used because they expired, such as an activation link, because they were already consumed, such as a one-time password, or because they reached their sunset date, such as a deprecated endpoint. It returns `410 Gone`. **Q: Why can I not pass a message to the constructor?** diff --git a/docs/book/v7/core-features/rendering-and-sending-emails.md b/docs/book/v7/core-features/rendering-and-sending-emails.md index 1d518433..7e3af6cd 100644 --- a/docs/book/v7/core-features/rendering-and-sending-emails.md +++ b/docs/book/v7/core-features/rendering-and-sending-emails.md @@ -81,5 +81,5 @@ Within Dotkernel API the custom renderer is the default. **Q: Where is the mail transport configured?** -A: In `config/autoload/mail.local.php`, under the `mail` key. +A: In `config/autoload/mail.local.php`, under the `mail` key (in Dotkernel API 7.0 the file is `config/autoload/mail.global.php`). See [Configuration files](../installation/configuration-files.md). diff --git a/docs/book/v7/flow/middleware-flow.md b/docs/book/v7/flow/middleware-flow.md index c5b050b8..37908741 100644 --- a/docs/book/v7/flow/middleware-flow.md +++ b/docs/book/v7/flow/middleware-flow.md @@ -10,6 +10,10 @@ The graph below demonstrates a default flow between Dotkernel's middlewares. ![Dotkernel API Middleware Flow!](https://docs.dotkernel.org/img/api/v7/dotkernel-middleware-flow.png) +> Dotkernel API 7.1 and 7.2 pipe `MalformedRequestBodyMiddleware` right after `ProblemDetailsMiddleware`, so a malformed request body produces a `400 Bad Request` response. +> The diagram does not show it, and version 7.0 does not have it. +> Update to the latest Dotkernel API 7.x release to get it, see [Upgrading from 7.0 to 7.1](../upgrading/UPGRADE-7.1.md). + ## FAQ **Q: Why does middleware order matter?** diff --git a/docs/book/v7/installation/composer.md b/docs/book/v7/installation/composer.md index f1fe3494..93b8354f 100644 --- a/docs/book/v7/installation/composer.md +++ b/docs/book/v7/installation/composer.md @@ -34,13 +34,16 @@ The automatic setup script performs these tasks: - Creates the `composer.lock` file that locks all dependencies to exact versions (you can still run `composer update` to replace them with newer versions, if available and installable without conflicts). - Configures PHP CodeSniffer, a utility to detect code style errors in PHP code. - Generate and save the OAuth2 keys in the `data/oauth` folder. - - Performed by this script `./bin/generate-oauth2-keys.php`. + - Performed by this script `./bin/generate-oauth2-keys.php`, which keeps existing keys. + - In Dotkernel API 7.0 it is `./vendor/bin/generate-oauth2-keys`, which regenerates them; see [OAuth2 Security](../security/oauth2-security.md#autogeneration-of-cryptographic-keys). - Creates the initial `config/autoload` configuration files. - Performed by this script `./bin/composer-post-install-script.php`. - These files are created: - config/autoload/local.php - config/autoload/local.test.php - - config/autoload/mail.global.php + - config/autoload/mail.local.php (and `config/autoload/mail.local.php.dist`) + +> In Dotkernel API 7.0 the mail configuration file is `config/autoload/mail.global.php`; see [Upgrading from 7.0 to 7.1](../upgrading/UPGRADE-7.1.md). > The post install commands are run automatically on every `composer install` and `composer update`. > The scripts check if the files exist to prevent overwriting them. diff --git a/docs/book/v7/installation/configuration-files.md b/docs/book/v7/installation/configuration-files.md index 06e3987b..3e2a9c88 100644 --- a/docs/book/v7/installation/configuration-files.md +++ b/docs/book/v7/installation/configuration-files.md @@ -33,6 +33,10 @@ The installation script will duplicate the following files: > Most often, you will be using either `Sendmail` or `SMTP` to send emails. > If you opt for `SMTP`, ake sure to configure the SMTP connection parameters under the `smtp_options` key. +> Dotkernel API 7.0 creates this file as `config/autoload/mail.global.php` instead. +> From 7.1 it is `mail.local.php`, plus a `mail.local.php.dist` copy. +> Update to the latest Dotkernel API 7.x release and move your settings, see [Upgrading from 7.0 to 7.1](../upgrading/UPGRADE-7.1.md). + * `config/autoload/local.test.php.dist` as `config/autoload/local.test.php` to run and create tests. > This creates a new in-memory database that your tests will run on. @@ -62,6 +66,7 @@ See the [CORS tutorial](https://docs.dotkernel.org/api-documentation/v7/tutorial **Q: Where does `mail.local.php` come from?** A: It is copied from the `dot-mail` package in `vendor`. +In Dotkernel API 7.0 the copy is named `mail.global.php`. Configure the `mail` key, and the `smtp_options` sub-key if you send through SMTP. See [Rendering and sending emails](../core-features/rendering-and-sending-emails.md). diff --git a/docs/book/v7/installation/doctrine-orm.md b/docs/book/v7/installation/doctrine-orm.md index b45fc7d2..cb49bda5 100644 --- a/docs/book/v7/installation/doctrine-orm.md +++ b/docs/book/v7/installation/doctrine-orm.md @@ -37,7 +37,7 @@ $databases = [ 'table_prefix' => '', ], 'postgresql' => [ - 'host' => 'localhost', + 'host' => '127.0.0.1', 'dbname' => 'dotkernel', 'user' => '', 'password' => '', @@ -49,6 +49,9 @@ $databases = [ ]; ``` +> Dotkernel API 7.0 ships the PostgreSQL `host` as `localhost`; 7.1 and 7.2 ship `127.0.0.1`, because `localhost` only works for socket connections. +> See [Upgrading from 7.0 to 7.1](../upgrading/UPGRADE-7.1.md). + > The database `dotkernel` is provided as an example, but you can use any name you like. > Make sure to use the same database name when you create the database in the next step. diff --git a/docs/book/v7/installation/test-the-installation.md b/docs/book/v7/installation/test-the-installation.md index 99ff4c60..5bd99014 100644 --- a/docs/book/v7/installation/test-the-installation.md +++ b/docs/book/v7/installation/test-the-installation.md @@ -32,6 +32,10 @@ From this point on, you are ready to add custom code to your application. [Bruno](https://www.usebruno.com/) is a Git-native API client we recommend using for working with the API. The endpoint collection is found in `documentation/Dotkernel_API_Bruno.zip`. + +> The Bruno collection was added in Dotkernel API 7.1; version 7.0 ships only the Postman collection and environment files, see the alternative below. +> Update to the latest Dotkernel API 7.x release to get it, see [Upgrading from 7.0 to 7.1](../upgrading/UPGRADE-7.1.md). + Import it in Bruno by following these steps: - Open the `My Workspace` dropdown and select `Import workspace`. diff --git a/docs/book/v7/introduction/file-structure.md b/docs/book/v7/introduction/file-structure.md index 94e1c0b9..bc42d952 100644 --- a/docs/book/v7/introduction/file-structure.md +++ b/docs/book/v7/introduction/file-structure.md @@ -27,8 +27,8 @@ This folder contains: * `cli.php` - Used to build console applications based on [laminas-cli](https://github.com/laminas/laminas-cli) * `composer-post-install-script.php` - Runs after `composer install` and copies the shipped distributable config files into place; it asks nothing and needs no input * `doctrine` - Doctrine ORM console, used by the fixtures commands to populate the database tables -* `generate-oauth2-keys.php` - Generates the OAuth2 key pair and encryption key into `data/oauth` -* `generate-openapi.php` - Generates the OpenAPI document; run through `composer openapi` +* `generate-oauth2-keys.php` - Generates the OAuth2 key pair and encryption key into `data/oauth`; keeps existing keys (added in 7.1) +* `generate-openapi.php` - Generates the OpenAPI document; run through `composer openapi` (added in 7.2) ## `config` folder @@ -59,11 +59,11 @@ This folder contains all service-related local and global config files: * `error-handling.global.php` - Configures and activates error logs * `local.php.dist` - Local configuration file: database credentials, application name and URL, OAuth2 key paths * `local.test.php.dist` - Local configuration for functional tests -* `mail.local.php.dist` - Mail configuration; e.g. sendmail vs smtp, message configuration, mail logging. Not committed: the post-install script copies it out of `dotkernel/dot-mail` during installation +* `mail.local.php.dist` - Mail configuration; e.g. sendmail vs smtp, message configuration, mail logging. Not committed: the post-install script copies it out of `dotkernel/dot-mail` during installation (in 7.0 this file is `mail.global.php`) * `mezzio.global.php` - Mezzio core config file -* `openapi.global.php` - Configures the generated OpenAPI document: output file, version, info, servers, tags and security schemes +* `openapi.global.php` - Configures the generated OpenAPI document: output file, version, info, servers, tags and security schemes (added in 7.2) * `problem-details.global.php` - Maps HTTP status codes to the `type` URI used in problem details responses -* `response-header.global.php` - Defines headers per route +* `response-header.global.php` - Defines headers, globally and per route * `templates.global.php` - Configures `Api\App\Template\RendererInterface`, including the `phtml` template extension ### Note diff --git a/docs/book/v7/introduction/packages.md b/docs/book/v7/introduction/packages.md index b9a5d0b6..8de1e11c 100644 --- a/docs/book/v7/introduction/packages.md +++ b/docs/book/v7/introduction/packages.md @@ -45,7 +45,7 @@ Composer refuses to install the project unless all of these are satisfied: * `roave/psr-container-doctrine`:`^5.2.2 || ^6.0.0` - Doctrine Factories for PSR-11 Containers * `symfony/filesystem`:`^7.2.0` - Provides basic utilities for the filesystem * `symfony/var-exporter`:`^6.4 || ^7.4` - Exports serializable PHP data structures as plain PHP code; required to keep Doctrine LazyGhost proxy support -* `zircote/swagger-php`:`^6.0.0` - Generate interactive documentation for your RESTful API using PHP attributes (preferred) or PHPDoc annotations +* `zircote/swagger-php`:`^6.4.0` - Generate interactive documentation for your RESTful API using PHP attributes (preferred) or PHPDoc annotations ### Installed as dependencies @@ -62,6 +62,7 @@ To see which versions you actually have, run `composer show doctrine/orm` (or `c Declared under `require-dev`, installed unless you pass `--no-dev`: +* `boundwize/structarmed`:`^0.19.0` - Architecture and code quality checks, configured in `structarmed.php` * `dotkernel/dot-maker`:`^2.0.0` - Generates modules, handlers, services and input filters; see [Creating a book module using DotMaker](../tutorials/create-book-module-via-dot-maker.md) * `laminas/laminas-coding-standard`:`^3.0.1` - Coding standard ruleset used by `composer cs-check` and `composer cs-fix` * `laminas/laminas-development-mode`:`^3.13.0` - Toggles development mode; see [Composer](../installation/composer.md) @@ -72,6 +73,22 @@ Declared under `require-dev`, installed unless you pass `--no-dev`: * `roave/security-advisories`:`dev-latest` - Blocks installation of packages with known vulnerabilities * `symfony/var-dumper`:`^7.2.3` - Debug output helper +### Version differences + +The lists above show the constraints of Dotkernel API 7.2. +These constraints differ in earlier 7.x versions: + +| Package | 7.0 | 7.1 | 7.2 | +| --- | --- | --- | --- | +| `mezzio/mezzio-authentication-oauth2` | `^2.11.0` | `^3.0.1` | `^3.0.1` | +| `symfony/var-exporter` | not required | `^6.4 \|\| ^7.4` | `^6.4 \|\| ^7.4` | +| `zircote/swagger-php` | `^5.0.7` | `^6.0.0` | `^6.4.0` | +| `dotkernel/dot-maker` (dev) | `^1.0.2` | `^2.0.0` | `^2.0.0` | +| `phpunit/phpunit` (dev) | `^10.5.45` | `^12.5.23` | `^12.5.23` | +| `boundwize/structarmed` (dev) | not required | not required | `^0.19.0` | + +> Update to the latest Dotkernel API 7.x release to get these versions, see [Upgrading from 7.0 to 7.1](../upgrading/UPGRADE-7.1.md) and [Upgrading from 7.1 to 7.2](../upgrading/UPGRADE-7.2.md). + ## FAQ **Q: Is this list authoritative?** diff --git a/docs/book/v7/introduction/server-requirements.md b/docs/book/v7/introduction/server-requirements.md index 533b88cc..b3c71882 100644 --- a/docs/book/v7/introduction/server-requirements.md +++ b/docs/book/v7/introduction/server-requirements.md @@ -49,7 +49,14 @@ Dotkernel API v7 requires PHP 8.3, 8.4 or 8.5, as declared in `composer.json`: ``` Earlier PHP versions are not supported. -Support for PHP 8.2 was dropped in Dotkernel API 7.2.0. + +| Version | Supported PHP versions | +| --- | --- | +| 7.0 | 8.2, 8.3, 8.4 and 8.5 (`~8.2.0 \|\| ~8.3.0 \|\| ~8.4.0 \|\| ~8.5.0`) | +| 7.1 and 7.2 | 8.3, 8.4 and 8.5 | + +Support for PHP 8.2 was dropped in Dotkernel API 7.1. +Update to the latest Dotkernel API 7.x release and raise the `php` constraint in your `composer.json`, see [Upgrading from 7.0 to 7.1](../upgrading/UPGRADE-7.1.md). > The constraint uses `~` per minor version rather than `>=`, so each supported branch is listed explicitly and a newly released PHP version is not assumed to work until it has been tested. @@ -61,13 +68,14 @@ Support for PHP 8.2 was dropped in Dotkernel API 7.2.0. ### Why PHP 8.3+? -The floor is set by **typed class constants**, a PHP 8.3 feature used throughout the codebase — for example in `Api\App\Middleware\ContentNegotiationMiddleware`: +In 7.1 the floor came from the PHPUnit 12 upgrade, which requires PHP 8.3. +In 7.2 it is also enforced by **typed class constants**, a PHP 8.3 feature used throughout the codebase — for example in `Api\App\Middleware\ContentNegotiationMiddleware`: ```php public const string DEFAULT_HEADERS = 'default'; ``` -The project will not even parse on PHP 8.2. +The 7.2 code will not even parse on PHP 8.2. ## Required Settings and Modules & Extensions @@ -182,8 +190,8 @@ A: `utf8mb4_general_ci` or `utf8mb4_unicode_ci` on MariaDB, and `C.UTF-8` or `en A: PHP 8.3. `composer.json` requires `~8.3.0 || ~8.4.0 || ~8.5.0`, so 8.3, 8.4 and 8.5 are supported and earlier versions are not. -PHP 8.2 support was dropped in Dotkernel API 7.2.0. -The hard blocker is typed class constants, a PHP 8.3 feature used throughout the codebase, so the project will not parse on 8.2. +Dotkernel API 7.0 also supports PHP 8.2; that support was dropped in 7.1. +Since 7.2 typed class constants, a PHP 8.3 feature used throughout the codebase, are a hard blocker, so the project will not parse on 8.2. **Q: Do I need the CLI SAPI as well as FPM?** diff --git a/docs/book/v7/openapi/configuration.md b/docs/book/v7/openapi/configuration.md index 57b5b386..a3b25a38 100644 --- a/docs/book/v7/openapi/configuration.md +++ b/docs/book/v7/openapi/configuration.md @@ -7,6 +7,9 @@ They live in `config/autoload/openapi.global.php` and `application.url`, and `bi ## Details +> This configuration exists in Dotkernel API 7.2 only. +> In 7.0 and 7.1 the document root is declared in attributes in `src/App/src/OpenAPI.php`, see [Generate documentation](generate-documentation.md#version-differences) and [Upgrading from 7.1 to 7.2](../upgrading/UPGRADE-7.2.md). + PHP attribute arguments must be constant expressions, so an `OA\Server` attribute cannot read the base URL out of your local config. For that reason the document root is assembled by `bin/generate-openapi.php` from configuration after the scan of `src`, while paths and schemas stay in each module's `OpenAPI.php`. diff --git a/docs/book/v7/openapi/generate-documentation.md b/docs/book/v7/openapi/generate-documentation.md index aa2947ea..69e56618 100644 --- a/docs/book/v7/openapi/generate-documentation.md +++ b/docs/book/v7/openapi/generate-documentation.md @@ -53,6 +53,22 @@ This scans the attributes only. The generated file will not contain the `info`, `servers`, tags or security schemes that `composer openapi` adds from configuration, so use it only when you need the raw output. Its defaults differ: OpenAPI version `3.0.0` (override with `--version 3.1.0`) and the format taken from the output extension (force it with `--format json` or `--format yaml`). +## Version differences + +This page describes Dotkernel API 7.2. +Earlier 7.x versions generate the file differently: + +| Behavior | 7.0 | 7.1 | 7.2 | +| --- | --- | --- | --- | +| Command | `./vendor/bin/openapi ./src --output ` | `./vendor/bin/openapi ./src --output ` | `composer openapi` (`bin/generate-openapi.php`) | +| Document root (`info`, servers, security schemes, external docs) | attributes in `src/App/src/OpenAPI.php`, with the server `http://api.dotkernel.localhost` | same attributes | `config/autoload/openapi.global.php` and `application.url` | +| Output file, servers, tags, `exclude_tags` | not configurable | not configurable | `openapi.*` config keys | +| `operationId` | hash generated by `zircote/swagger-php` | hash generated by `zircote/swagger-php` | explicit, readable names, enforced by a test | +| `zircote/swagger-php` | `^5.0.7` | `^6.0.0` | `^6.4.0` | + +> Update to the latest Dotkernel API 7.x release to use `composer openapi`. +> See [Upgrading from 7.0 to 7.1](../upgrading/UPGRADE-7.1.md) and [Upgrading from 7.1 to 7.2](../upgrading/UPGRADE-7.2.md). + ## FAQ **Q: What must I configure before generating?** diff --git a/docs/book/v7/openapi/initialized-components.md b/docs/book/v7/openapi/initialized-components.md index 9fea4b31..132969d2 100644 --- a/docs/book/v7/openapi/initialized-components.md +++ b/docs/book/v7/openapi/initialized-components.md @@ -12,6 +12,7 @@ Below you will find details on some prepopulated OpenAPI components we added to > `OA\Info`, `OA\Server`, `OA\SecurityScheme` and `OA\ExternalDocumentation` are not declared as attributes. > `bin/generate-openapi.php` builds them from `config/autoload/openapi.global.php` and `application.url` when you run `composer openapi`, and adds them to the document after the scan of `src`. > See [OpenAPI configuration](configuration.md) for every key. +> That is how Dotkernel API 7.2 works; in 7.0 and 7.1 these components are attributes at the top of `src/App/src/OpenAPI.php`, see [Upgrading from 7.1 to 7.2](../upgrading/UPGRADE-7.2.md). ## OA\Info diff --git a/docs/book/v7/openapi/introduction.md b/docs/book/v7/openapi/introduction.md index cc2153b1..ab6d6dde 100644 --- a/docs/book/v7/openapi/introduction.md +++ b/docs/book/v7/openapi/introduction.md @@ -10,6 +10,10 @@ To provide an interactive documentation, Dotkernel API implemented [zircote/swag Developers can use this library to auto-generate documentation that outlines available endpoints, their request details, and their response formats. +> The pages of this section describe Dotkernel API 7.2, where `composer openapi` generates the file from configuration. +> Versions 7.0 and 7.1 have no `composer openapi` script or `openapi.global.php`; see the version differences in [Generate documentation](generate-documentation.md#version-differences). +> Update to the latest Dotkernel API 7.x release, see [Upgrading from 7.1 to 7.2](../upgrading/UPGRADE-7.2.md). + ## FAQ **Q: What is OpenAPI?** diff --git a/docs/book/v7/openapi/write-documentation.md b/docs/book/v7/openapi/write-documentation.md index 745768a8..13aba79a 100644 --- a/docs/book/v7/openapi/write-documentation.md +++ b/docs/book/v7/openapi/write-documentation.md @@ -33,6 +33,10 @@ There are a lot more, but these are the most often used ones. If you need help, take a look at the existing definitions found in Dotkernel API. +> An explicit `operationId` on every operation exists in Dotkernel API 7.2. +> In 7.0 and 7.1 the attributes have none, and `zircote/swagger-php` generates a hash for each operation. +> Update to the latest Dotkernel API 7.x release, see [Upgrading from 7.1 to 7.2](../upgrading/UPGRADE-7.2.md). + ### OA\Delete Defines a `DELETE` HTTP request. diff --git a/docs/book/v7/reference/production-readiness.md b/docs/book/v7/reference/production-readiness.md index 5f861d9f..f9391d2c 100644 --- a/docs/book/v7/reference/production-readiness.md +++ b/docs/book/v7/reference/production-readiness.md @@ -70,6 +70,7 @@ Two endpoints make this matter more than it would on a read-only API. - `POST /security/generate-token` is unauthenticated and verifies a password hash on every call, which makes it both a brute-force target and an expensive one. Nothing counts failed attempts: the `AdminLogin` entity models a login record, but no lockout or backoff is applied on repeated failures, and there is no equivalent record for regular users. + In 7.0 that record stores `deviceBrand`, `deviceModel`, `osPlatform`, `clientEngine` and `clientVersion`; from 7.1 it stores an `isCrawler` flag instead, which needs a database migration, see [Upgrading from 7.0 to 7.1](../upgrading/UPGRADE-7.1.md). - `POST /error-report` is guarded by a static token plus a domain or IP whitelist, and appends every accepted report to a single file. A leaked token with no rate limit in front of it is a way to fill a disk. @@ -99,6 +100,8 @@ Two things to get right when you do. CORS is answered inside the application by `mezzio/mezzio-cors` from the CORS local config, and the static security headers — `X-Content-Type-Options`, `Referrer-Policy` and `permissions-policy` — come from `dot-response-header` via `config/autoload/response-header.global.php`. If the gateway adds its own copies of these, clients receive duplicates. Decide which layer owns each header and disable it in the other. +`X-Content-Type-Options` and `Referrer-Policy` are only set from 7.1 onwards, and `Cache-Control: no-store` on the token routes also arrived in 7.1; Dotkernel API 7.0 sets `permissions-policy` alone. +See [Upgrading from 7.0 to 7.1](../upgrading/UPGRADE-7.1.md). **`X-Forwarded-For` is trusted without a proxy allowlist.** `Core\App\Service\IpService::getUserIp()` returns `HTTP_X_FORWARDED_FOR` whenever that value parses as a public IP address, then falls back to `HTTP_CLIENT_IP`, and only then to `REMOTE_ADDR`. @@ -222,7 +225,8 @@ Register a **new** service rather than redefining `dot-log.default_logger`. There is no health or readiness endpoint. `GET /` is handled by `Api\App\Handler\GetIndexResourceHandler`, which reads the application name and version out of configuration and returns them. It touches neither the database nor any downstream service, so a `200` from it means PHP is running and nothing more. -It also carries a `#[ResourceDeprecation]` attribute with a sunset date of 2038-01-01, present purely as an example of the deprecation feature, so every response to it is decorated with deprecation headers. +In 7.0 and 7.1 it also carries a `#[ResourceDeprecation]` attribute with a sunset date of 2038-01-01, present purely as an example of the deprecation feature, so every response to it is decorated with deprecation headers. +In 7.2 that attribute is removed and the response carries no deprecation headers; see [Upgrading from 7.1 to 7.2](../upgrading/UPGRADE-7.2.md). It is not a health endpoint and should not be used as one. A readiness check for this application needs to cover at least the database connection, whether migrations are at head, write access to `log/` and `data/cache`, read access to the three files in `data/oauth`, and the mail transport if you send mail during requests. @@ -282,6 +286,9 @@ Producing one is a step you add to your own build or deploy: composer openapi ``` +This script exists in Dotkernel API 7.2 only. +In 7.0 and 7.1, run `./vendor/bin/openapi ./src --output ` instead; see [Generate documentation](../openapi/generate-documentation.md#version-differences) and [Upgrading from 7.1 to 7.2](../upgrading/UPGRADE-7.2.md). + `zircote/swagger-php` is a `require` rather than a `require-dev` dependency, so the generator is present on a production install as well. The file is written to `openapi.output_file`, which defaults to `public/openapi.yaml`: point it outside `public/` if the specification must not be served. diff --git a/docs/book/v7/security/basic-security.md b/docs/book/v7/security/basic-security.md index 097b30c3..71cd1380 100644 --- a/docs/book/v7/security/basic-security.md +++ b/docs/book/v7/security/basic-security.md @@ -22,6 +22,17 @@ Whenever an endpoint needs custom `Accept` and/or `Content-Type`, make sure that > Read more about [content negotiation](https://www.dotkernel.com/dotkernel-api/content-negotiation-in-dotkernel-rest-api/) and the way it is implemented in [Dotkernel API](../core-features/content-validation.md). +## Response Headers + +Dotkernel API sets response headers through `dot-response-header`, configured in `config/autoload/response-header.global.php`. + +| Version | Headers on all responses | Headers on `security::generate-token` and `security::refresh-token` | +| --- | --- | --- | +| 7.0 | `Permissions-Policy: interest-cohort=()` | none | +| 7.1 and 7.2 | `Permissions-Policy: interest-cohort=()`, `X-Content-Type-Options: nosniff` and `Referrer-Policy: no-referrer` | `Cache-Control: no-store` and `Pragma: no-cache` | + +> Update to the latest Dotkernel API 7.x release to get the security headers, see [Upgrading from 7.0 to 7.1](../upgrading/UPGRADE-7.1.md). + ## Cross-Origin Resource Sharing Dotkernel API uses [mezzio/mezzio-cors](https://github.com/mezzio/mezzio-cors) to handle CORS details. diff --git a/docs/book/v7/security/oauth2-security.md b/docs/book/v7/security/oauth2-security.md index 69b54eeb..2252ecb3 100644 --- a/docs/book/v7/security/oauth2-security.md +++ b/docs/book/v7/security/oauth2-security.md @@ -47,7 +47,9 @@ Skipping...` and stops. Only when one is missing does it delegate to `vendor/mezzio/mezzio-authentication-oauth2/bin/generate-oauth2-keys` to generate the set. > This guard matters in production: regenerating the keys invalidates every access token already issued. -> Preserving them across updates was added in Dotkernel API 7.2.0 ([issue #503](https://github.com/dotkernel/api/issues/503)). +> Preserving them across updates was added in Dotkernel API 7.1 ([issue #503](https://github.com/dotkernel/api/issues/503)). +> In 7.0, `post-update-cmd` runs `php ./vendor/bin/generate-oauth2-keys` instead, which invalidated the issued tokens on every update. +> Update to the latest Dotkernel API 7.x release to get this behavior, see [Upgrading from 7.0 to 7.1](../upgrading/UPGRADE-7.1.md). > If you deliberately want to rotate the keys, delete the three files from `data/oauth` and run `composer update` — accepting that existing tokens stop working. While hidden to the VCS by default, keep in mind not to commit any local keys. @@ -83,7 +85,7 @@ Skipping...` and leaves them alone, so updating dependencies does not invalidate **Q: How do I stop the keys from being generated?** A: Remove `php ./bin/generate-oauth2-keys.php` from the `scripts.post-update-cmd` key in `composer.json`. -Since 7.2.0 this is rarely necessary — the script already preserves existing keys, which is what protects issued tokens on a server. +Since 7.1 this is rarely necessary — the script already preserves existing keys, which is what protects issued tokens on a server. **Q: How do I deliberately rotate the keys?** diff --git a/docs/book/v7/transition-from-api-tools/api-tools-vs-dotkernel-api.md b/docs/book/v7/transition-from-api-tools/api-tools-vs-dotkernel-api.md index 32c1abae..1baa87eb 100644 --- a/docs/book/v7/transition-from-api-tools/api-tools-vs-dotkernel-api.md +++ b/docs/book/v7/transition-from-api-tools/api-tools-vs-dotkernel-api.md @@ -27,6 +27,9 @@ API Tools is archived and MVC/event-driven; Dotkernel API is actively maintained | Code Generator | Yes | [dotkernel/dot-maker](https://www.dotkernel.com/headless-platform/dotmaker-generate-common-code-in-dotkernel/) | | PSR | PSR-7 | PSR-7, PSR-15 | +> The PHP versions listed are those of Dotkernel API 7.1 and 7.2; version 7.0 also supports PHP 8.2, see [Server requirements](../introduction/server-requirements.md#php-83-84-or-85). +> Update to the latest Dotkernel API 7.x release, see [Upgrading from 7.0 to 7.1](../upgrading/UPGRADE-7.1.md). + ## FAQ **Q: Is Dotkernel API a drop-in replacement for API Tools?** diff --git a/docs/book/v7/tutorials/api-evolution.md b/docs/book/v7/tutorials/api-evolution.md index 2bab0edf..36f1e408 100644 --- a/docs/book/v7/tutorials/api-evolution.md +++ b/docs/book/v7/tutorials/api-evolution.md @@ -3,7 +3,8 @@ ## Summary Dotkernel API lets you evolve endpoints without breaking existing consumers by marking a handler with the `ResourceDeprecation` attribute. -The `DeprecationMiddleware` then adds `Sunset` and `Link` response headers, telling consumers when the resource may stop responding and where the change is documented. +The `DeprecationMiddleware` then adds `Sunset` and `Link` response headers, telling consumers when the resource will stop responding and where the change is documented. +From the sunset date onwards the middleware no longer runs the handler and returns `410 Gone`. ## Details @@ -15,7 +16,7 @@ In Dotkernel API we can mark an endpoint as deprecated using attributes on handl We use response headers to inform the consumers about the future changes by using two new headers: - `Link` - it's a link to the official documentation pointing out the changes that will take place. -- `Sunset` - this header is a date, indicating when the deprecated resource will potentially become unresponsive. +- `Sunset` - this header is a date, indicating when the deprecated resource becomes unavailable. From that date the endpoint returns `410 Gone`. **The above headers are independent, so you can use them separately.** @@ -61,6 +62,13 @@ Link: https://docs.dotkernel.org/api-documentation/v7/tutorials/api-evolution/;r Vary: Origin ``` +## Version differences + +In Dotkernel API 7.0 and 7.1, the `GET /` handler (`GetIndexResourceHandler`) carries this attribute as a live example, so the `curl` call above shows the `Sunset` and `Link` headers. +In 7.2 the attribute is removed from that handler, so `GET /` returns no deprecation headers; the headers appear on the handlers you mark yourself. + +> Update to the latest Dotkernel API 7.x release to get the current behavior, see [Upgrading from 7.1 to 7.2](../upgrading/UPGRADE-7.2.md). + ## Notes > If `Link` or `Sunset` do not have a value they will not appear in the response headers. @@ -77,7 +85,7 @@ Vary: Origin **Q: What do the `Sunset` and `Link` headers mean?** -A: `Sunset` is the date on which the deprecated resource may stop responding; `Link` points to documentation describing the change. +A: `Sunset` is the date on which the deprecated resource stops responding and starts returning `410 Gone`; `Link` points to documentation describing the change. They are independent, so either can be used alone. **Q: What do I need in place before deprecations work?** @@ -92,13 +100,18 @@ Since each handler serves one method and route, deprecating the handler deprecat **Q: What happens if I leave `Sunset` or `Link` empty?** -A: The corresponding header is simply omitted from the response. +A: The corresponding header is simply omitted from the response. Without a `Sunset` date the endpoint never returns `410 Gone`. **Q: What if the `Sunset` date is invalid?** A: It throws an error. The value has to be a valid date. +**Q: What does the response look like once the sunset date has passed?** + +A: The handler is not run and the response is `410 Gone` with the detail `This resource is no longer available (sunset on ).` +The `type` field of the problem-details body holds the documentation URL: the attribute's `link`, or else the configured `documentation_url`. + **Q: Are `rel` and `type` required?** A: No. diff --git a/docs/book/v7/tutorials/create-book-module-via-dot-maker.md b/docs/book/v7/tutorials/create-book-module-via-dot-maker.md index 51351ab0..b477da52 100644 --- a/docs/book/v7/tutorials/create-book-module-via-dot-maker.md +++ b/docs/book/v7/tutorials/create-book-module-via-dot-maker.md @@ -85,7 +85,9 @@ Next you will be prompted to add the relevant components of a module, accepting * `Allow viewing Books?` (Y): will generate the single resource GET action handler - `GetBookResourceHandler.php`. * `Allow creating Books?` (Y): will generate the POST action handler for the `Book` entity - `PostBookResourceHandler.php`, as well as the input filter used for validating the data - `CreateBookInputFilter.php`. * `Allow deleting Books?`, `Allow editing Books?` and `Allow replacing Books?` (N): will generate handlers that reflect the DELETE, PATCH and PUT actions respectively, but are not necessary for this tutorial. -* Following this step, `dot-maker` will automatically generate the `ConfigProvider.php` classes for both the `Api` and `Core` namespaces, as well as the `OpenAPI.php` class which automatically documents the previously generated routes, each with its own `operationId`. +* Following this step, `dot-maker` will automatically generate the `ConfigProvider.php` classes for both the `Api` and `Core` namespaces, as well as the `OpenAPI.php` class which automatically documents the previously generated routes. + * `dot-maker` 2.0.0 does not add an `operationId` to those operations. + Dotkernel API 7.2 requires one on every operation and its `OperationIdTest` fails without it, so add each `operationId` yourself, see [Write documentation](../openapi/write-documentation.md). You will then be instructed to: diff --git a/docs/book/v7/tutorials/create-book-module.md b/docs/book/v7/tutorials/create-book-module.md index b1a59ea4..f28280de 100644 --- a/docs/book/v7/tutorials/create-book-module.md +++ b/docs/book/v7/tutorials/create-book-module.md @@ -183,6 +183,10 @@ class Book extends AbstractEntity ``` +> The `created` value is typed `DateTimeImmutable|null` because `TimestampsTrait` makes `created` nullable from Dotkernel API 7.1 (`getCreatedFormatted()` returns `?string`). +> In 7.0 `created` is a non-null `DateTimeImmutable` and `getCreatedFormatted()` returns `string`, so use `created: DateTimeImmutable` in the docblock. +> See [Upgrading from 7.0 to 7.1](../upgrading/UPGRADE-7.1.md). + * `src/Core/src/Book/src/Repository/BookRepository.php` ```php diff --git a/docs/book/v7/upgrading/UPGRADE-7.2.md b/docs/book/v7/upgrading/UPGRADE-7.2.md new file mode 100644 index 00000000..d45eefed --- /dev/null +++ b/docs/book/v7/upgrading/UPGRADE-7.2.md @@ -0,0 +1,79 @@ +# Upgrading from 7.1 to 7.2 + +## Summary + +The changes you need to port into your project when moving from Dotkernel API 7.1 to 7.2, each linked to the pull request that introduced it. +The headline items are the new `composer openapi` generator with its configuration file, the explicit `operationId` on every operation, and the changed exception status codes. + +## Details + +> You can find the release notes in [7.1.1](https://github.com/dotkernel/api/releases/tag/7.1.1), the [7.1.0...7.1.1 compare view](https://github.com/dotkernel/api/compare/7.1.0...7.1.1) and the commits on the [7.0 branch](https://github.com/dotkernel/api/commits/7.0). +> The project `CHANGELOG.md` does not list the 7.2 pull requests, and pull requests 565, 566 and 567 are not in any release notes, so they are listed here from their code changes. + +### Important updates + +These changes affect your Composer scripts and dependencies, configuration, the published OpenAPI document, response behavior or error behavior. + +* Add `bin/generate-openapi.php`, the `openapi` Composer script (`php bin/generate-openapi.php`) and `config/autoload/openapi.global.php`: the OpenAPI document root (`info`, `servers`, security schemes, tags, external docs) now comes from configuration instead of attributes, and the server URL is taken from `application.url`. Copy the config file, add the script to your `composer.json`, add the `openapi` key with `server_description` to `config/autoload/local.php`, and add `public/openapi.yaml` to `.gitignore`. The generated document also defines a shared `DateTimeObject` schema [https://github.com/dotkernel/api/pull/560](https://github.com/dotkernel/api/pull/560) +* Move the OpenAPI root attributes (`Info`, `Server`, security schemes, external docs) out of `src/App/src/OpenAPI.php`: pull request 551 wraps them in `OA\OpenApi`, and pull request 560 then removes them in favor of the configuration file. Apply both together and remove the attributes from your copy of the file [https://github.com/dotkernel/api/pull/551](https://github.com/dotkernel/api/pull/551) +* Add an explicit `operationId` to every operation in the `OpenAPI.php` files, add a test that fails on a missing or duplicate id (`test/Unit/App/OpenApi/OperationIdTest.php`) and require `zircote/swagger-php` `^6.4.0`: the ids in the generated document change from hashes to readable names, for example `getAdminCollection` and `postUserResource`. Clients generated from the document must be regenerated [https://github.com/dotkernel/api/pull/566](https://github.com/dotkernel/api/pull/566) +* Change exception status codes: `RuntimeException` and `SunsetException` return `500 Internal Server Error` instead of `400` and `401`, and `ErrorReportService` throws `ForbiddenException` (`403`) instead of `RuntimeException` when error reporting is not enabled. Update API clients that match on the old status codes [https://github.com/dotkernel/api/pull/567](https://github.com/dotkernel/api/pull/567) +* Remove the `ResourceDeprecation` attribute from `GetIndexResourceHandler`: `GET /` no longer returns the `Sunset` and `Link` headers [https://github.com/dotkernel/api/pull/565](https://github.com/dotkernel/api/pull/565) + +### Optional updates + +These changes cover documentation fixes, type-only cleanups, development tooling and CI. +Skipping them does not affect how the API runs. + +* Document the recover-account endpoint at its registered path in `src/User/src/OpenAPI.php`: `/user/account/recover` instead of `/account/recover-identity`. The route itself is unchanged [https://github.com/dotkernel/api/pull/550](https://github.com/dotkernel/api/pull/550) +* Add class constant type declarations and the `SlevomatCodingStandard.TypeHints.ClassConstantTypeHint` rule to `phpcs.xml`. It only matters if a subclass of the affected classes overrides one of those constants [https://github.com/dotkernel/api/pull/537](https://github.com/dotkernel/api/pull/537) +* Add `boundwize/structarmed` as a dev dependency with a `structarmed.php` architecture file, and make closures `static` [https://github.com/dotkernel/api/pull/553](https://github.com/dotkernel/api/pull/553) +* Update `boundwize/structarmed` to `^0.18.0` [https://github.com/dotkernel/api/pull/554](https://github.com/dotkernel/api/pull/554) +* Update `boundwize/structarmed` to `^0.19.0` [https://github.com/dotkernel/api/pull/562](https://github.com/dotkernel/api/pull/562) +* Clarify in `config/autoload/cors.local.php.dist` how to restrict the allowed origins [https://github.com/dotkernel/api/pull/536](https://github.com/dotkernel/api/pull/536) +* Update the README: password warning for the fixtures [https://github.com/dotkernel/api/pull/540](https://github.com/dotkernel/api/pull/540) +* Update the README to reflect the application purpose [https://github.com/dotkernel/api/pull/541](https://github.com/dotkernel/api/pull/541) +* Update `JetBrains/qodana-action` to v2026.2.0 [https://github.com/dotkernel/api/pull/532](https://github.com/dotkernel/api/pull/532) +* Update `JetBrains/qodana-action` to v2026.2.1 [https://github.com/dotkernel/api/pull/542](https://github.com/dotkernel/api/pull/542) +* Update `JetBrains/qodana-action` to v2026.2.2 [https://github.com/dotkernel/api/pull/557](https://github.com/dotkernel/api/pull/557) + +## FAQ + +**Q: Is there an automated upgrade from 7.1 to 7.2?** + +A: No. +You implement each listed change manually in your own project. +See [Upgrades](upgrading.md) for the recommended procedure. + +**Q: Which PHP versions does 7.2 support?** + +A: The same as 7.1: PHP 8.3, 8.4 and 8.5. +No pull request in this release changes the `php` constraint in `composer.json`. + +**Q: Do I need a database migration?** + +A: No. +None of the pull requests in this release changes an entity, an enum or a migration. + +**Q: Where did the OpenAPI root attributes go?** + +A: Pull request 551 wraps them in `OA\OpenApi`, and pull request 560 removes them from `src/App/src/OpenAPI.php`. +The document root is now assembled by `bin/generate-openapi.php` from `config/autoload/openapi.global.php` and the `application.url` key, so run `composer openapi` to generate the document. + +**Q: Will my API clients notice a change?** + +A: Yes, in four places. +`RuntimeException` and `SunsetException` now return `500` instead of `400` and `401`, and a disabled error-report endpoint now returns `403` (pull request 567). +`GET /` no longer returns `Sunset` and `Link` headers (pull request 565). +The `operationId` values in the generated OpenAPI document change (pull request 566). +No route or request format changes. + +**Q: Do I have to apply the optional updates?** + +A: No. +They only concern documentation, type declarations, development tooling and CI. + +**Q: Where do I find the complete list of changes?** + +A: In the [7.1.1 release notes](https://github.com/dotkernel/api/releases/tag/7.1.1) and the [commits on the 7.0 branch](https://github.com/dotkernel/api/commits/7.0). +The project `CHANGELOG.md` does not list these pull requests. diff --git a/mkdocs.yml b/mkdocs.yml index 64f8f3a4..f646a955 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -27,6 +27,7 @@ nav: - "FAQ": v7/installation/faq.md - Upgrading: - "Upgrade procedure": v7/upgrading/upgrading.md + - "Upgrading 7.1 to 7.2": v7/upgrading/UPGRADE-7.2.md - "Upgrading 7.0 to 7.1": v7/upgrading/UPGRADE-7.1.md - "Upgrading 6.x to 7.0": v7/upgrading/UPGRADE-7.0.md - "Upgrading 5.x to 6.0": v7/upgrading/UPGRADE-6.0.md