Skip to content

Commit 33aad87

Browse files
Merge remote-tracking branch 'upstream/2.6' into main
2 parents 477ce4f + 3ddeb9c commit 33aad87

File tree

9 files changed

+174
-42
lines changed

9 files changed

+174
-42
lines changed

core/jwt.md

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ security:
120120
# https://symfony.com/doc/current/security.html#c-hashing-passwords
121121
password_hashers:
122122
App\Entity\User: 'auto'
123-
123+
124124
# https://symfony.com/doc/current/security/authenticator_manager.html
125125
enable_authenticator_manager: true
126126
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
@@ -165,7 +165,7 @@ Want to test the routes of your JWT-authentication-protected API?
165165
api_platform:
166166
swagger:
167167
api_keys:
168-
apiKey:
168+
JWT:
169169
name: Authorization
170170
type: header
171171
```
@@ -236,6 +236,13 @@ final class JwtDecorator implements OpenApiFactoryInterface
236236
],
237237
]);
238238
239+
$schemas = $openApi->getComponents()->getSecuritySchemes() ?? [];
240+
$schemas['JWT'] = new ArrayObject([
241+
'type' => 'http',
242+
'scheme' => 'bearer',
243+
'bearerFormat' => 'JWT',
244+
]);
245+
239246
$pathItem = new Model\PathItem(
240247
ref: 'JWT Token',
241248
post: new Model\Operation(
@@ -278,11 +285,11 @@ And register this service in `config/services.yaml`:
278285
```yaml
279286
# api/config/services.yaml
280287
services:
281-
# ...
288+
# ...
282289
283290
App\OpenApi\JwtDecorator:
284291
decorates: 'api_platform.openapi.factory'
285-
arguments: ['@.inner']
292+
arguments: ['@.inner']
286293
```
287294

288295
## Testing
@@ -306,14 +313,15 @@ class AuthenticationTest extends ApiTestCase
306313
public function testLogin(): void
307314
{
308315
$client = self::createClient();
316+
$container = self::getContainer();
309317
310318
$user = new User();
311319
$user->setEmail('test@example.com');
312320
$user->setPassword(
313-
self::$container->get('security.user_password_hasher')->hashPassword($user, '$3CR3T')
321+
$container->get('security.user_password_hasher')->hashPassword($user, '$3CR3T')
314322
);
315323
316-
$manager = self::$container->get('doctrine')->getManager();
324+
$manager = $container->get('doctrine')->getManager();
317325
$manager->persist($user);
318326
$manager->flush();
319327
@@ -349,7 +357,7 @@ Since now we have a `JWT` authentication, functional tests require us to log in
349357

350358
Hashers are used for 2 reasons:
351359

352-
1. To generate a hash for a raw password (`self::$container->get('security.user_password_hasher')->hashPassword($user, '$3CR3T')`)
360+
1. To generate a hash for a raw password (`$container->get('security.user_password_hasher')->hashPassword($user, '$3CR3T')`)
353361
2. To verify a password during authentication
354362

355363
While hashing and verifying 1 password is quite a fast operation, doing it hundreds or even thousands of times in a tests suite becomes a bottleneck, because reliable hashing algorithms are slow by their nature.

core/openapi.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,9 @@ Change `/docs` to the URI you wish Swagger to be accessible on.
447447

448448
## Using a custom Asset Package in Swagger UI
449449

450-
Sometimes you may want to use a different [Asset Package](https://symfony.com/doc/current/reference/configuration/framework.html#packages) for the Swagger UI. In this way you'll have more fine-grained control over the asset url generations. This is useful i.e. if you want to use different base path, base url or asset versioning strategy.
450+
Sometimes you may want to use a different [Asset Package](https://symfony.com/doc/current/reference/configuration/framework.html#packages) for the Swagger UI.
451+
In this way you'll have more fine-grained control over the asset URL generations.
452+
This is useful i.e. if you want to use different base path, base URL or asset versioning strategy.
451453

452454
Specify a custom asset package name:
453455

@@ -485,15 +487,15 @@ As described [in the Symfony documentation](https://symfony.com/doc/current/temp
485487
</html>
486488
```
487489

488-
You may want to copy the [one shipped with API Platform](https://github.com/api-platform/core/blob/main/src/Bridge/Symfony/Bundle/Resources/views/SwaggerUi/index.html.twig) and customize it.
490+
You may want to copy the [one shipped with API Platform](https://github.com/api-platform/core/blob/2.6/src/Bridge/Symfony/Bundle/Resources/views/SwaggerUi/index.html.twig) and customize it.
489491

490492
## Compatibility Layer with Amazon API Gateway
491493

492494
[AWS API Gateway](https://aws.amazon.com/api-gateway/) supports OpenAPI partially, but it [requires some changes](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-known-issues.html).
493495
API Platform provides a way to be compatible with Amazon API Gateway.
494496

495497
To enable API Gateway compatibility on your OpenAPI docs, add `api_gateway=true` as query parameter: `http://www.example.com/docs.json?api_gateway=true`.
496-
The flag `--api-gateway` is also available through the command line.
498+
The flag `--api-gateway` is also available through the command-line.
497499

498500
## OAuth
499501

core/push-relations.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
API Platform leverages this capability by pushing relations of a resource to clients.
88

99
**Note:** We strongly recommend using [Vulcain](https://vulcain.rocks) instead of this feature.
10-
Vulcain is faster, cleaner, more flexible, and is supported out of the box in the API Platform distribution.
10+
Vulcain is faster, cleaner, more flexible, and is supported out of the box in [the API Platform distribution](../distribution/index.md).
1111

1212
```php
1313
<?php
@@ -28,7 +28,10 @@ class Book
2828
```
2929

3030
By setting the `push` attribute to `true` on a property holding a relation, API Platform will automatically add a valid `Link` HTTP header with the `preload` relation.
31-
According to the [Preload W3C Candidate Recommendation](https://www.w3.org/TR/preload/), web servers and proxy servers can read this header, fetch the related resource and send it to the client using Server Push.
31+
According to the [Preload W3C Candidate Recommendation](https://www.w3.org/TR/preload/#server-push-http-2), web servers and proxy servers can read this header, fetch the related resource and send it to the client using Server Push.
32+
33+
With the Caddy web server (the server shipped as part of the [distribution](../distribution/index.md)), you must add [the `push` directive](https://caddyserver.com/docs/caddyfile/directives/push) to your `Caddyfile` to be able to use this feature.
34+
3235
[NGINX](https://www.nginx.com/blog/nginx-1-13-9-http2-server-push/), [Apache](https://httpd.apache.org/docs/current/howto/http2.html#push), [Cloudflare](https://www.cloudflare.com/website-optimization/http2/serverpush/), [Fastly](https://docs.fastly.com/guides/performance-tuning/http2-server-push) and [Akamai](https://blogs.akamai.com/2017/03/http2-server-push-the-what-how-and-why.html)
3336
honor this header.
3437

core/serialization.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -696,7 +696,7 @@ class Book
696696
/**
697697
* This field can be managed only by an admin
698698
*/
699-
#[Groups(['book:output', 'admin:input'}])]
699+
#[Groups(['book:output', 'admin:input'])]
700700
public bool $active = false;
701701
702702
/**

core/validation.md

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,89 @@ class Greeting
369369
}
370370
```
371371

372+
## Validating Delete Operations
373+
374+
By default, validation rules that are specified on the API resource are not evaluated during DELETE operations. You need to trigger the validation in your code, if needed.
375+
376+
Assume that you have the following entity that uses a custom delete validator:
377+
378+
```php
379+
<?php
380+
// api/src/Entity/MyEntity.php
381+
382+
namespace App\Entity;
383+
384+
use ApiPlatform\Core\Annotation\ApiResource;
385+
use App\Validator\AssertCanDelete;
386+
use Doctrine\ORM\Mapping as ORM;
387+
388+
#[ORM\Entity]
389+
#[ApiResource(
390+
itemOperations: [
391+
'delete' => [
392+
'validation_groups' => ['deleteValidation']
393+
]
394+
]
395+
)]
396+
#[AssertCanDelete(groups: ['deleteValidation'])]
397+
class MyEntity
398+
{
399+
#[ORM\Id, ORM\Column, ORM\GeneratedValue]
400+
private ?int $id = null;
401+
402+
#[ORM\Column]
403+
public string $name = '';
404+
}
405+
```
406+
407+
Create a data persister, which decorates the default data persister, where you will trigger the validation:
408+
409+
```php
410+
<?php
411+
// api/src/DataPersister/MyEntityDataPersister.php
412+
413+
namespace App\DataPersister;
414+
415+
use ApiPlatform\Core\DataPersister\DataPersisterInterface;
416+
use ApiPlatform\Core\Validator\ValidatorInterface;
417+
use App\Entity\MyEntity;
418+
419+
class MyEntityDataPersister implements DataPersisterInterface
420+
{
421+
public function __construct(
422+
private DataPersisterInterface $decoratedDoctrineDataPersister,
423+
private ValidatorInterface $validator,
424+
) {
425+
}
426+
427+
public function persist($data): void {
428+
$this->decoratedDoctrineDataPersister->persist($data);
429+
}
430+
431+
public function remove($data): void {
432+
$this->validator->validate(
433+
$data,
434+
['groups' => ['deleteValidation']]
435+
);
436+
$this->decoratedDoctrineDataPersister->remove($data);
437+
}
438+
439+
public function supports($data): bool {
440+
return $data instanceof MyEntity;
441+
}
442+
}
443+
```
444+
445+
Register the new data persister in `api/config/services.yaml`:
446+
447+
```yaml
448+
# api/config/services.yaml
449+
services:
450+
_defaults:
451+
bind:
452+
$decoratedDoctrineDataPersister: '@api_platform.doctrine.orm.data_persister'
453+
```
454+
372455
## Error Levels and Payload Serialization
373456
374457
As stated in the [Symfony documentation](https://symfony.com/doc/current/validation/severity.html), you can use the payload field to define error levels.

distribution/caddy.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Configuring the Caddy Web Server
2+
3+
[The API Platform distribution](index.md) is shipped with [the Caddy web server](https://caddyserver.com).
4+
The build contains the [Mercure](../core/mercure.md) and the [Vulcain](https://vulcain.rocks) Caddy modules.
5+
6+
Caddy is positioned in front of the web API and of the Progressive Web App.
7+
It routes requests to either service depending on the value of the `Accept` HTTP header or the extension
8+
of the requested file.
9+
10+
Using the same domain to serve the API and the PWA [improves performance by preventing unnecessary CORS preflight requests
11+
and encourages embracing the REST principles](https://dunglas.fr/2022/01/preventing-cors-preflight-requests-using-content-negotiation/).
12+
13+
By default, requests having an `Accept` request header containing the `text/html` media type are routed to the Next.js application,
14+
except for some paths known to be resources served by the API (e.g. the Swagger UI documentation, static files provided by bundles...).
15+
Other requests are routed to the API.
16+
17+
Sometimes, you may want to let the PHP application generate HTML responses.
18+
For instance, when you create your own Symfony controllers serving HTML pages,
19+
or when using bundles such as EasyAdmin or SonataAdmin.
20+
21+
To do so, you have to tweak the rules used to route the requests.
22+
Open `api-platform/api/docker/caddy/Caddyfile` and modify the expression.
23+
You can use [any CEL (Common Expression Language) expression](https://caddyserver.com/docs/caddyfile/matchers#expression) supported by Caddy.
24+
25+
For instance, if you want to route all requests to a path starting with `/admin` to the API, modify the existing expression like this:
26+
27+
```patch
28+
# Matches requests for HTML documents, for static files and for Next.js files,
29+
# except for known API paths and paths with extensions handled by API Platform
30+
@pwa expression `(
31+
{header.Accept}.matches("\\btext/html\\b")
32+
- && !{path}.matches("(?i)(?:^/docs|^/graphql|^/bundles/|^/_profiler|^/_wdt|\\.(?:json|html$|csv$|ya?ml$|xml$))")
33+
+ && !{path}.matches("(?i)(?:^/admin|^/docs|^/graphql|^/bundles/|^/_profiler|^/_wdt|\\.(?:json|html$|csv$|ya?ml$|xml$))")
34+
)`
35+
```

0 commit comments

Comments
 (0)