Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
4680724
fix(metadata): resource name collection missing deprecation (#4953)
lchrusciel Sep 16, 2022
e3e81fb
docs: minor changelog fixes (#4949)
dunglas Sep 16, 2022
637c679
docs: fix markdown issue in CHANGELOG.md (#4948)
dunglas Sep 16, 2022
4092a8d
chore: drop dependency on fig/link-util (#4945)
nicolas-grekas Sep 16, 2022
c037806
fix(symfony): missing Elasticsearch DocumentMetadataFactoryInterface …
walva Sep 17, 2022
d099dd6
fix(symfony): upgrade command removes filters (#4970)
emmanuelballery Sep 17, 2022
11ed63d
fix(symfony): upgrade command requires phpunit (#4968)
soyuka Sep 17, 2022
530ef37
fix(graphql): remove unused service for ItemResolverFactory (#4976)
alanpoulain Sep 19, 2022
fc639ea
fix(metadata): do not auto-generate NotExposed operation when using c…
vincentchalamon Sep 19, 2022
56875b3
fix(symfony): exception_status bad merge (#4981)
soyuka Sep 19, 2022
b0730bb
fix(symfony): missing deprecations related to Ulid and Uuid normalize…
walva Sep 19, 2022
14c7cba
fix(symfony): resource class directories bc break (#4982)
soyuka Sep 20, 2022
8691fdf
chore: commitlint only the Pull Request HEAD (#4983)
soyuka Sep 20, 2022
693b03a
fix(symfony): bc layer broken for symfony/console lower than 5.3 (#4990)
deguif Sep 23, 2022
d265387
chore: add conflict on elasticsearch >= 8.0 (#5018)
alanpoulain Sep 27, 2022
4ca639c
fix(symfony): add missing dependency on symfony/deprecation-contracts…
GuilhemN Sep 27, 2022
44ba110
fix: replace FQCN service names by snake ones (#5019)
alanpoulain Sep 28, 2022
f40d57a
fix: compatibility with PHP 8.2 (#5024)
dunglas Sep 29, 2022
603551a
fix(metadata): fix missing `array` cast for RDF types in `ApiResource…
chalasr Sep 29, 2022
08450c2
fix(serializer): support empty array as object (#4999)
alanpoulain Sep 29, 2022
07afa77
fix: check fetch joined queries based on all aliases (#4974)
Sajito Sep 29, 2022
0f6d74c
fix(symfony): remove `_api_exception_to_status` leftovers (#4992)
chalasr Sep 29, 2022
b798211
fix(symfony): metadata cache array adapter dev only (#4986)
emmanuelballery Sep 29, 2022
9895e34
fix(symfony): don't use ArrayAdapter in production #4975 (#5025)
soyuka Sep 29, 2022
3a49669
Revert "fix(symfony): metadata cache array adapter dev only (#4986)" …
soyuka Sep 29, 2022
806775b
fix(symfony): metadata cache is broken in dev/prod (#5027)
soyuka Sep 29, 2022
1091938
chore: update swagger ui and javascript libraries (#5028)
soyuka Sep 29, 2022
e0a8a1a
Merge 2.7 into 3.0
soyuka Sep 29, 2022
e470b07
Merge pull request #5029 from soyuka/3.0
soyuka Sep 29, 2022
85b7dc9
chore: changelog 2.7.1
soyuka Sep 29, 2022
f532f37
Merge branch 2.7 into 3.0
soyuka Sep 29, 2022
d63b103
chore: changelog 3.0.1
soyuka Sep 29, 2022
0a3dee4
Merge pull request #5031 from soyuka/3.0
soyuka Sep 29, 2022
0a8815b
fix(doctrine): ability to use ORM and ODM (#5032)
soyuka Sep 29, 2022
fbc0760
chore: changelog & doctrine annotation issue
soyuka Sep 29, 2022
b1351c6
Merge 2.7 into 3.0
soyuka Sep 29, 2022
5ced1c6
Merge 3.0 onto main
soyuka Sep 29, 2022
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
2 changes: 1 addition & 1 deletion .commitlintrc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"defaultIgnores": true,
"parserPreset": "conventional-changelog-conventionalcommits",
"extends": "@commitlint/config-conventional",
"rules": {
"scope-enum": [
2,
Expand Down
15 changes: 13 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,24 @@ env:

jobs:
commitlint:
if: github.event_name == 'pull_request'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
configFile: .commitlintrc
- uses: wagoid/commitlint-github-action@v5
- name: Run commitlint
run: |
commit=$(gh api \
-H "Accept: application/vnd.github+json" \
/repos/${{ github.repository }}/pulls/${{github.event.number}}/commits \
| jq -r '.[0].commit.message')
# we can't use npx see https://github.com/conventional-changelog/commitlint/issues/613
echo '{}' > package.json
npm install --no-fund --no-audit @commitlint/config-conventional @commitlint/cli
echo $commit | ./node_modules/.bin/commitlint -g .commitlintrc
php-cs-fixer:
name: PHP-cs-fixer (PHP ${{ matrix.php }})
runs-on: ubuntu-latest
Expand Down
64 changes: 48 additions & 16 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Changelog
=======

## 3.0.1

* Symfony: don't use ArrayAdapter cache in production #5027
* Symfony: remove `_api_exception_to_status` leftovers (#4992)
* Serializer: support empty array as object (#4999)
* Chore: compatibility with PHP 8.2 (#5024)
* Symfony: resource class directories bc break (#4982)
* Symfony: exception_status bad merge (#4981)
* Graphql: remove unused service for ItemResolverFactory (#4976)
* Chore: document missing breaking changes on the 3.0.0-beta.1

## 3.0.0

Expand All @@ -13,19 +23,6 @@

Various cleanup in services and removal of backward compatibility layer.

## 2.7.0

* chore: remove @experimental phpdoc (#4933)
* Metadata: do not set id when identifier is `false` (#4880)
* Metadata: automatic GET operation when none is declared (#4881)
* Metadata: exception to status on operations (#4861)
* Serializer: adds the JSON_INVALID_UTF8_IGNORE flag to JsonEncode (#4741)
* Symfony: autoconfigure legacy Doctrine extensions (#4909)
* Elasticsearch: skip metadata without ES nodes (#4913)
* Symfony: deprecated the `$exceptionOnNoToken` parameter in `ResourceAccessChecker::__construct()` (#4900)

Various cs fixes and PHPDoc to help upgrading to 3.0.

## 3.0.0-rc.2

* JsonLd: correct the `api_jsonld_context` route format (#4844)
Expand All @@ -49,6 +46,41 @@ Breaking changes:
* Doctrine: `ContextAware` interfaces were merged with their child interfaces you can safely remove them (#4779)
* Metadata: the `Core` namespace got removed (#4805)
* Mercure: deprecation removed (#4805)
* Identifiers: using an object as identifier is supported only when this object is `Stringable`
* Serializer: `skip_null_values` now defaults to `true`
* Metadata: `Patch` is added to the automatic CRUD

## 2.7.1

* Chore: update swagger ui and javascript libraries (#5028)
* Symfony: don't use ArrayAdapter cache in production #4975 (#5025)
* Doctrine: check fetch joined queries based on all aliases (#4974)
* Metadata: fix missing `array` cast for RDF types in `ApiResource` & `ApiProperty` constructors (#5000)
* Symfony: replace FQCN service names by snake ones (#5019)
* Symfony: add missing dependency on symfony/deprecation-contracts (#5015)
* Chore: add conflict on elasticsearch >= 8.0 (#5018)
* Symfony: bc layer broken for symfony/console lower than 5.3 (#4990)
* Symfony: missing deprecations related to Ulid and Uuid normalize… (#4963)
* Metadata: do not auto-generate NotExposed operation when using custom operation classes
* Symfony: upgrade command requires phpunit (#4968)
* Symfony: upgrade command removes filters (#4970)
* Symfony: missing Elasticsearch DocumentMetadataFactoryInterface alias definition (#4962)
* Chore: drop dependency on fig/link-util (#4945)
* Metadata: resource name collection missing deprecation (#4953)
* Doctrine: ability to use ORM and ODM (#5032)

## 2.7.0

* chore: remove @experimental phpdoc (#4933)
* Metadata: do not set id when identifier is `false` (#4880)
* Metadata: automatic GET operation when none is declared (#4881)
* Metadata: exception to status on operations (#4861)
* Serializer: adds the JSON_INVALID_UTF8_IGNORE flag to JsonEncode (#4741)
* Symfony: autoconfigure legacy Doctrine extensions (#4909)
* Elasticsearch: skip metadata without ES nodes (#4913)
* Symfony: deprecated the `$exceptionOnNoToken` parameter in `ResourceAccessChecker::__construct()` (#4900)

Various cs fixes and PHPDoc to help upgrading to 3.0.

## 2.7.0-rc.2

Expand Down Expand Up @@ -141,7 +173,7 @@ Doctrine: new interfaces for Filters and Extensions ready, switch to the `ApiPla

* Swagger UI: Add `usePkceWithAuthorizationCodeGrant` to Swagger UI initOAuth (#4649)
* **BC**: `mapping.paths` in configuration should override bundles configuration (#4465)
* GraphQL: Add ability to use different pagination types for the queries of a resource (#4453)
* GraphQL: Add the ability to use different pagination types for the queries of a resource (#4453)
* Security: **BC** Fix `ApiProperty` `security` attribute expression being passed a class string for the `object` variable on updates/creates - null is now passed instead if the object is not available (#4184)
* Security: `ApiProperty` now supports a `security_post_denormalize` attribute, which provides access to the `object` variable for the object being updated/created and `previous_object` for the object before it was updated (#4184)
* Maker: Add `make:data-provider` and `make :data-persister` commands to generate a data provider / persister (#3850)
Expand Down Expand Up @@ -175,7 +207,7 @@ Doctrine: new interfaces for Filters and Extensions ready, switch to the `ApiPla
* Metadata: deprecation of `ApiPlatform\Core\Annotation` (#4351)
* Metadata: `ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface` is deprecated in favor of `ApiPlatform\Metadata\Resource\Factory\ResourceMetadataCollectionFactoryInterface` (#4351)
* Metadata: item and collection prefixes for operations are deprecated, as well as the `ApiPlatform\Core\Api\OperationType` class (#4351)
* Graphql: `ApiPlatform\Metadata\GraphQl` follow the same metadata conventions (a Subscription operation is available and isn't hidden behind an update Mutation anymore), interfaces got simplified (beeing @experimental) (#4351)
* Graphql: `ApiPlatform\Metadata\GraphQl` follow the same metadata conventions (a Subscription operation is available and isn't hidden behind an update Mutation anymore), interfaces got simplified (being @experimental) (#4351)
* IriConverter: new interface for `ApiPlatform\Bridge\Symfony\Routing\IriConverter` that adds an operationName, same for `ApiPlatform\Api\IdentifiersExtractor` (#4351)
* DataProvider: new `ApiPlatform\State\ProviderInterface` that replaces DataProviders (#4351)
* DataPersister: new `ApiPlatform\State\ProcessorInterface` that replaces DataPersisters (#4351)
Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
"require": {
"php": ">=8.1",
"doctrine/inflector": "^1.0 || ^2.0",
"fig/link-util": "^1.0",
"psr/cache": "^1.0 || ^2.0 || ^3.0",
"psr/container": "^1.0 || ^2.0",
"symfony/http-foundation": "^6.1",
Expand Down Expand Up @@ -100,7 +99,8 @@
"doctrine/common": "<2.7",
"doctrine/dbal": "<2.10",
"doctrine/mongodb-odm": "<2.2",
"doctrine/persistence": "<1.3"
"doctrine/persistence": "<1.3",
"elasticsearch/elasticsearch": ">=8.0"
},
"suggest": {
"doctrine/mongodb-odm-bundle": "To support MongoDB. Only versions 4.0 and later are supported.",
Expand Down
33 changes: 33 additions & 0 deletions features/main/exception_to_status.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
Feature: Using exception_to_status config
As an API developer
I can customize the status code returned if the application throws an exception

@createSchema
@!mongodb
Scenario: Configure status code via the operation exceptionToStatus to map custom NotFound error to 404
When I add "Content-Type" header equal to "application/ld+json"
And I send a "GET" request to "/dummy_exception_to_statuses/123"
Then the response status code should be 404
And the response should be in JSON
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"

@!mongodb
Scenario: Configure status code via the resource exceptionToStatus to map custom NotFound error to 400
When I add "Content-Type" header equal to "application/ld+json"
And I send a "PUT" request to "/dummy_exception_to_statuses/123" with body:
"""
{
"name": "black"
}
"""
Then the response status code should be 400
And the response should be in JSON
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"

@!mongodb
Scenario: Configure status code via the config file to map FilterValidationException to 400
When I add "Content-Type" header equal to "application/ld+json"
And I send a "GET" request to "/dummy_exception_to_statuses"
Then the response status code should be 400
And the response should be in JSON
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
33 changes: 33 additions & 0 deletions features/serializer/empty_array_as_object.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
Feature: Serialize empty array as object
In order to have a coherent JSON representation
As a developer
I should be able to serialize some empty array properties as objects

@createSchema
Scenario: Get a resource with empty array properties as objects
When I add "Content-Type" header equal to "application/ld+json"
And I send a "GET" request to "/empty_array_as_objects/5"
Then the response status code should be 200
And the response should be in JSON
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
And the JSON should be equal to:
"""
{
"@context": "/contexts/EmptyArrayAsObject",
"@id": "/empty_array_as_objects/6",
"@type": "EmptyArrayAsObject",
"id": 6,
"emptyArray": [],
"emptyArrayAsObject": {},
"arrayObjectAsArray": [],
"arrayObject": {},
"stringArray": [
"foo",
"bar"
],
"objectArray": {
"foo": 67,
"bar": "baz"
}
}
"""
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@

namespace ApiPlatform\Doctrine\Odm\Metadata\Resource;

use ApiPlatform\Doctrine\Common\State\PersistProcessor;
use ApiPlatform\Doctrine\Common\State\RemoveProcessor;
use ApiPlatform\Doctrine\Odm\State\CollectionProvider;
use ApiPlatform\Doctrine\Odm\State\ItemProvider;
use ApiPlatform\Metadata\ApiResource;
Expand Down Expand Up @@ -101,9 +99,9 @@ private function getProvider(Operation $operation): string
private function getProcessor(Operation $operation): string
{
if ($operation instanceof DeleteOperationInterface) {
return RemoveProcessor::class;
return 'api_platform.doctrine_mongodb.odm.state.remove_processor';
}

return PersistProcessor::class;
return 'api_platform.doctrine_mongodb.odm.state.persist_processor';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@

namespace ApiPlatform\Doctrine\Orm\Metadata\Resource;

use ApiPlatform\Doctrine\Common\State\PersistProcessor;
use ApiPlatform\Doctrine\Common\State\RemoveProcessor;
use ApiPlatform\Doctrine\Orm\State\CollectionProvider;
use ApiPlatform\Doctrine\Orm\State\ItemProvider;
use ApiPlatform\Metadata\ApiResource;
Expand Down Expand Up @@ -101,9 +99,9 @@ private function getProvider(Operation $operation): string
private function getProcessor(Operation $operation): string
{
if ($operation instanceof DeleteOperationInterface) {
return RemoveProcessor::class;
return 'api_platform.doctrine.orm.state.remove_processor';
}

return PersistProcessor::class;
return 'api_platform.doctrine.orm.state.persist_processor';
}
}
24 changes: 5 additions & 19 deletions src/Doctrine/Orm/Util/QueryChecker.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,30 +92,14 @@ public static function hasMaxResults(QueryBuilder $queryBuilder): bool
public static function hasOrderByOnFetchJoinedToManyAssociation(QueryBuilder $queryBuilder, ManagerRegistry $managerRegistry): bool
{
if (
0 === (is_countable($selectParts = $queryBuilder->getDQLPart('select')) ? \count($selectParts = $queryBuilder->getDQLPart('select')) : 0) ||
0 === (is_countable($queryBuilder->getDQLPart('join')) ? \count($queryBuilder->getDQLPart('join')) : 0) ||
0 === (is_countable($orderByParts = $queryBuilder->getDQLPart('orderBy')) ? \count($orderByParts = $queryBuilder->getDQLPart('orderBy')) : 0)
0 === \count($queryBuilder->getDQLPart('join')) ||
0 === \count($orderByParts = $queryBuilder->getDQLPart('orderBy'))
) {
return false;
}

$rootAliases = $queryBuilder->getRootAliases();

$selectAliases = [];

foreach ($selectParts as $select) {
foreach ($select->getParts() as $part) {
[$alias] = explode('.', (string) $part);

$selectAliases[] = $alias;
}
}

$selectAliases = array_diff($selectAliases, $rootAliases);
if (0 === \count($selectAliases)) {
return false;
}

$orderByAliases = [];

foreach ($orderByParts as $orderBy) {
Expand All @@ -133,11 +117,13 @@ public static function hasOrderByOnFetchJoinedToManyAssociation(QueryBuilder $qu
return false;
}

$allAliases = $queryBuilder->getAllAliases();

foreach ($orderByAliases as $orderByAlias) {
$inToManyContext = false;

foreach (QueryBuilderHelper::traverseJoins($orderByAlias, $queryBuilder, $managerRegistry) as $alias => [$metadata, $association]) {
if ($inToManyContext && \in_array($alias, $selectAliases, true)) {
if ($inToManyContext && \in_array($alias, $allAliases, true)) {
return true;
}

Expand Down
2 changes: 1 addition & 1 deletion src/GraphQl/Resolver/Stage/ReadStage.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public function __construct(private readonly IriConverterInterface $iriConverter
/**
* {@inheritdoc}
*/
public function __invoke(?string $resourceClass, ?string $rootClass, Operation $operation, array $context): iterable|object|null
public function __invoke(?string $resourceClass, ?string $rootClass, Operation $operation, array $context): object|array|null
{
if (!($operation->canRead() ?? true)) {
return $context['is_collection'] ? [] : null;
Expand Down
2 changes: 1 addition & 1 deletion src/GraphQl/Resolver/Stage/ReadStageInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,5 @@
*/
interface ReadStageInterface
{
public function __invoke(?string $resourceClass, ?string $rootClass, Operation $operation, array $context): iterable|object|null;
public function __invoke(?string $resourceClass, ?string $rootClass, Operation $operation, array $context): object|array|null;
}
2 changes: 1 addition & 1 deletion src/GraphQl/Resolver/Stage/SerializeStage.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public function __construct(private readonly NormalizerInterface $normalizer, pr
{
}

public function __invoke(iterable|object|null $itemOrCollection, string $resourceClass, Operation $operation, array $context): ?array
public function __invoke(object|array|null $itemOrCollection, string $resourceClass, Operation $operation, array $context): ?array
{
$isCollection = $operation instanceof CollectionOperationInterface;
$isMutation = $operation instanceof Mutation;
Expand Down
2 changes: 1 addition & 1 deletion src/GraphQl/Resolver/Stage/SerializeStageInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,5 @@
*/
interface SerializeStageInterface
{
public function __invoke(iterable|object|null $itemOrCollection, string $resourceClass, Operation $operation, array $context): ?array;
public function __invoke(object|array|null $itemOrCollection, string $resourceClass, Operation $operation, array $context): ?array;
}
4 changes: 2 additions & 2 deletions src/Hydra/EventListener/AddLinkHeaderListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@
use ApiPlatform\Api\UrlGeneratorInterface;
use ApiPlatform\JsonLd\ContextBuilder;
use ApiPlatform\Util\CorsTrait;
use Fig\Link\GenericLinkProvider;
use Fig\Link\Link;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\WebLink\GenericLinkProvider;
use Symfony\Component\WebLink\Link;

/**
* Adds the HTTP Link header pointing to the Hydra documentation.
Expand Down
21 changes: 2 additions & 19 deletions src/Hydra/Serializer/CollectionNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public function __construct(private readonly ContextBuilderInterface $contextBui
*/
public function supportsNormalization(mixed $data, string $format = null, array $context = []): bool
{
return self::FORMAT === $format && is_iterable($data);
return self::FORMAT === $format && is_iterable($data) && isset($context['resource_class']) && !isset($context['api_sub_level']);
}

/**
Expand All @@ -66,10 +66,6 @@ public function supportsNormalization(mixed $data, string $format = null, array
*/
public function normalize(mixed $object, string $format = null, array $context = []): array
{
if (!isset($context['resource_class']) || isset($context['api_sub_level'])) {
return $this->normalizeRawCollection($object, $format, $context);
}

$resourceClass = $this->resourceClassResolver->getResourceClass($object, $context['resource_class']);
$context = $this->initContext($resourceClass, $context);
$data = $this->addJsonLdContext($this->contextBuilder, $resourceClass, $context);
Expand Down Expand Up @@ -106,19 +102,6 @@ public function normalize(mixed $object, string $format = null, array $context =

public function hasCacheableSupportsMethod(): bool
{
return true;
}

/**
* Normalizes a raw collection (not API resources).
*/
private function normalizeRawCollection(iterable $object, ?string $format, array $context): array
{
$data = [];
foreach ($object as $index => $obj) {
$data[$index] = $this->normalizer->normalize($obj, $format, $context);
}

return $data;
return false;
}
}
Loading