Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(symfony): request and view kernel listeners #6102

Merged
merged 1 commit into from
Jan 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1127,6 +1127,8 @@ jobs:
run: vendor/bin/simple-phpunit --version
- name: Clear test app cache
run: tests/Fixtures/app/console cache:clear --ansi
- name: Use legacy ignored deprecations
run: cp tests/.ignored-deprecations-legacy-events tests/.ignored-deprecations
- name: Run PHPUnit tests
run: |
mkdir -p build/logs/phpunit
Expand Down Expand Up @@ -1229,3 +1231,72 @@ jobs:
name: openapi-docs-php${{ matrix.php }}
path: build/out/openapi
continue-on-error: true

behat_listeners:
name: Behat event listeners (PHP ${{ matrix.php }})
env:
USE_SYMFONY_LISTENERS: 1
runs-on: ubuntu-latest
timeout-minutes: 20
strategy:
matrix:
php:
- '8.3'
fail-fast: false
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
tools: pecl, composer
extensions: intl, bcmath, curl, openssl, mbstring, pdo_sqlite
coverage: pcov
ini-values: memory_limit=-1
- name: Get composer cache directory
id: composercache
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache dependencies
uses: actions/cache@v3
with:
path: ${{ steps.composercache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
restore-keys: ${{ runner.os }}-composer-
- name: Update project dependencies
run: composer update --no-interaction --no-progress --ansi
- name: Install PHPUnit
run: vendor/bin/simple-phpunit --version
- name: Clear test app cache
run: tests/Fixtures/app/console cache:clear --ansi
- name: Run Behat tests (PHP 8)
run: |
mkdir -p build/logs/behat
vendor/bin/behat --out=std --format=progress --format=junit --out=build/logs/behat/junit --profile=symfony_listeners --no-interaction
- name: Upload test artifacts
if: always()
uses: actions/upload-artifact@v3
with:
name: behat-logs-php${{ matrix.php }}
path: build/logs/behat
continue-on-error: true
- name: Export OpenAPI documents
run: |
mkdir -p build/out/openapi
tests/Fixtures/app/console api:openapi:export -o build/out/openapi/openapi_v3.json
tests/Fixtures/app/console api:openapi:export --yaml -o build/out/openapi/openapi_v3.yaml
- name: Setup node
uses: actions/setup-node@v3
with:
node-version: '14'
- name: Validate OpenAPI documents
run: |
npx git+https://github.com/soyuka/swagger-cli#master validate build/out/openapi/openapi_v3.json
npx git+https://github.com/soyuka/swagger-cli#master validate build/out/openapi/openapi_v3.yaml
- name: Upload OpenAPI artifacts
if: always()
uses: actions/upload-artifact@v3
with:
name: openapi-docs-php${{ matrix.php }}
path: build/out/openapi
continue-on-error: true
36 changes: 35 additions & 1 deletion behat.yml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ mongodb:
- 'Behat\MinkExtension\Context\MinkContext'
- 'behatch:context:rest'
filters:
tags: '~@sqlite&&~@elasticsearch&&~@!mongodb&&~@mercure'
tags: '~@sqlite&&~@elasticsearch&&~@!mongodb&&~@mercure&&~@controller'

mercure:
suites:
Expand Down Expand Up @@ -220,3 +220,37 @@ legacy:
symfony: ~
'Behatch\Extension': ~

symfony_listeners:
suites:
default:
contexts:
- 'ApiPlatform\Tests\Behat\CommandContext'
- 'ApiPlatform\Tests\Behat\DoctrineContext'
- 'ApiPlatform\Tests\Behat\GraphqlContext'
- 'ApiPlatform\Tests\Behat\JsonContext'
- 'ApiPlatform\Tests\Behat\HydraContext'
- 'ApiPlatform\Tests\Behat\OpenApiContext'
- 'ApiPlatform\Tests\Behat\HttpCacheContext'
- 'ApiPlatform\Tests\Behat\JsonApiContext'
- 'ApiPlatform\Tests\Behat\JsonHalContext'
- 'ApiPlatform\Tests\Behat\MercureContext'
- 'ApiPlatform\Tests\Behat\XmlContext'
- 'Behat\MinkExtension\Context\MinkContext'
- 'behatch:context:rest'
filters:
tags: '~@postgres&&~@mongodb&&~@elasticsearch&&~@mercure'
extensions:
'FriendsOfBehat\SymfonyExtension':
bootstrap: 'tests/Fixtures/app/bootstrap.php'
kernel:
environment: 'test'
debug: true
class: AppKernel
path: 'tests/Fixtures/app/AppKernel.php'
'Behat\MinkExtension':
base_url: 'http://example.com/'
files_path: 'features/files'
sessions:
default:
symfony: ~
'Behatch\Extension': ~
2 changes: 2 additions & 0 deletions features/jsonld/input_output.feature
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ Feature: JSON-LD DTO input and output
"""

@createSchema
@controller
Scenario: Create a resource with no input
When I send a "POST" request to "/dummy_dto_no_inputs"
Then the response status code should be 201
Expand All @@ -210,6 +211,7 @@ Feature: JSON-LD DTO input and output
}
"""

@controller
Scenario: Update a resource with no input
When I send a "POST" request to "/dummy_dto_no_inputs/1/double_bat"
Then the response status code should be 200
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
@controller
Feature: Custom operation
As a client software developer
I need to be able to create custom operations
Expand Down
1 change: 1 addition & 0 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,4 @@ parameters:
- '#Call to method hasCacheableSupportsMethod\(\) on an unknown class Symfony\\Component\\Serializer\\Normalizer\\CacheableSupportsMethodInterface\.#'
- '#Class Symfony\\Component\\Serializer\\Normalizer\\CacheableSupportsMethodInterface not found\.#'
- '#Access to undefined constant Symfony\\Component\\HttpKernel\\HttpKernelInterface::MASTER_REQUEST\.#'
- '#Attribute class PHPUnit\\Framework\\Attributes\\DataProvider does not exist.#'
8 changes: 7 additions & 1 deletion src/Documentation/Action/EntrypointAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,13 @@ public function __invoke(Request $request)
'spec_version' => (string) $request->query->get(LegacyOpenApiNormalizer::SPEC_VERSION),
];
$request->attributes->set('_api_platform_disable_listeners', true);
$operation = new Get(outputFormats: $this->documentationFormats, read: true, serialize: true, class: Entrypoint::class, provider: [self::class, 'provide']);
$operation = new Get(
outputFormats: $this->documentationFormats,
read: true,
serialize: true,
class: Entrypoint::class,
provider: [self::class, 'provide']
);
$request->attributes->set('_api_operation', $operation);
$body = $this->provider->provide($operation, [], $context);
$operation = $request->attributes->get('_api_operation');
Expand Down
2 changes: 2 additions & 0 deletions src/Hydra/EventListener/AddLinkHeaderListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
/**
* Adds the HTTP Link header pointing to the Hydra documentation.
*
* @deprecated use ApiPlatform\Hydra\State\HydraLinkProcessor instead
*
* @author Kévin Dunglas <[email protected]>
*/
final class AddLinkHeaderListener
Expand Down
10 changes: 5 additions & 5 deletions src/State/Processor/SerializeProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,16 @@
final class SerializeProcessor implements ProcessorInterface
{
/**
* @param ProcessorInterface<T1, T2> $processor
* @param ProcessorInterface<mixed, mixed>|null $processor
*/
public function __construct(private readonly ProcessorInterface $processor, private readonly SerializerInterface $serializer, private readonly SerializerContextBuilderInterface $serializerContextBuilder)
public function __construct(private readonly ?ProcessorInterface $processor, private readonly SerializerInterface $serializer, private readonly SerializerContextBuilderInterface $serializerContextBuilder)
{
}

public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = [])
{
if ($data instanceof Response || !$operation->canSerialize() || !($request = $context['request'] ?? null)) {
return $this->processor->process($data, $operation, $uriVariables, $context);
return $this->processor ? $this->processor->process($data, $operation, $uriVariables, $context) : $data;
}

// @see ApiPlatform\State\Processor\RespondProcessor
Expand All @@ -59,7 +59,7 @@
$serializerContext['uri_variables'] = $uriVariables;

if (isset($serializerContext['output']) && \array_key_exists('class', $serializerContext['output']) && null === $serializerContext['output']['class']) {
return $this->processor->process(null, $operation, $uriVariables, $context);
return $this->processor ? $this->processor->process(null, $operation, $uriVariables, $context) : null;

Check warning on line 62 in src/State/Processor/SerializeProcessor.php

View check run for this annotation

Codecov / codecov/patch

src/State/Processor/SerializeProcessor.php#L62

Added line #L62 was not covered by tests
}

$resources = new ResourceList();
Expand All @@ -80,6 +80,6 @@
$request->attributes->set('_api_platform_links', $linkProvider);
}

return $this->processor->process($serialized, $operation, $uriVariables, $context);
return $this->processor ? $this->processor->process($serialized, $operation, $uriVariables, $context) : $serialized;
}
}
12 changes: 7 additions & 5 deletions src/State/Processor/WriteProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ final class WriteProcessor implements ProcessorInterface
use ClassInfoTrait;

/**
* @param ProcessorInterface<T1, T2> $processor
* @param ProcessorInterface<T1, T2> $callableProcessor
* @param ProcessorInterface<mixed, mixed> $processor
* @param ProcessorInterface<mixed, mixed> $callableProcessor
*/
public function __construct(private readonly ProcessorInterface $processor, private readonly ProcessorInterface $callableProcessor)
public function __construct(private readonly ?ProcessorInterface $processor, private readonly ProcessorInterface $callableProcessor)
{
}

Expand All @@ -48,9 +48,11 @@ public function process(mixed $data, Operation $operation, array $uriVariables =
|| !($operation->canWrite() ?? true)
|| !$operation->getProcessor()
) {
return $this->processor->process($data, $operation, $uriVariables, $context);
return $this->processor ? $this->processor->process($data, $operation, $uriVariables, $context) : $data;
}

return $this->processor->process($this->callableProcessor->process($data, $operation, $uriVariables, $context), $operation, $uriVariables, $context);
$data = $this->callableProcessor->process($data, $operation, $uriVariables, $context);

return $this->processor ? $this->processor->process($data, $operation, $uriVariables, $context) : $data;
}
}
6 changes: 3 additions & 3 deletions src/State/Provider/ContentNegotiationProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,15 @@
* @param array<string, string[]> $formats
* @param array<string, string[]> $errorFormats
*/
public function __construct(private readonly ProviderInterface $decorated, Negotiator $negotiator = null, private readonly array $formats = [], private readonly array $errorFormats = [])
public function __construct(private readonly ?ProviderInterface $decorated = null, Negotiator $negotiator = null, private readonly array $formats = [], private readonly array $errorFormats = [])
{
$this->negotiator = $negotiator ?? new Negotiator();
}

public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null
{
if (!($request = $context['request'] ?? null) || !$operation instanceof HttpOperation) {
return $this->decorated->provide($operation, $uriVariables, $context);
return $this->decorated?->provide($operation, $uriVariables, $context);

Check warning on line 41 in src/State/Provider/ContentNegotiationProvider.php

View check run for this annotation

Codecov / codecov/patch

src/State/Provider/ContentNegotiationProvider.php#L41

Added line #L41 was not covered by tests
}

$isErrorOperation = $operation instanceof ErrorOperation;
Expand All @@ -53,7 +53,7 @@
$request->setRequestFormat($this->getRequestFormat($request, $formats, false));
}

return $this->decorated->provide($operation, $uriVariables, $context);
return $this->decorated?->provide($operation, $uriVariables, $context);
}

/**
Expand Down
8 changes: 4 additions & 4 deletions src/State/Provider/DeserializeProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@

final class DeserializeProvider implements ProviderInterface
{
public function __construct(private readonly ProviderInterface $decorated, private readonly SerializerInterface $serializer, private readonly SerializerContextBuilderInterface $serializerContextBuilder, private ?TranslatorInterface $translator = null)
public function __construct(private readonly ?ProviderInterface $decorated, private readonly SerializerInterface $serializer, private readonly SerializerContextBuilderInterface $serializerContextBuilder, private ?TranslatorInterface $translator = null)
{
if (null === $this->translator) {
$this->translator = new class() implements TranslatorInterface, LocaleAwareInterface {
Expand All @@ -44,13 +44,13 @@

public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null
{
$data = $this->decorated->provide($operation, $uriVariables, $context);

// We need request content
if (!$operation instanceof HttpOperation || !($request = $context['request'] ?? null)) {
return $data;
return $this->decorated?->provide($operation, $uriVariables, $context);

Check warning on line 49 in src/State/Provider/DeserializeProvider.php

View check run for this annotation

Codecov / codecov/patch

src/State/Provider/DeserializeProvider.php#L49

Added line #L49 was not covered by tests
}

$data = $this->decorated ? $this->decorated->provide($operation, $uriVariables, $context) : $request->attributes->get('data');

if (!$operation->canDeserialize()) {
return $data;
}
Expand Down
Loading
Loading