Skip to content

Commit b27d221

Browse files
committed
feat(metadata): Resource Collection
1 parent 29e0fb8 commit b27d221

File tree

229 files changed

+12578
-1337
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

229 files changed

+12578
-1337
lines changed

.github/workflows/ci.yml

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -247,17 +247,27 @@ jobs:
247247
if: (startsWith(matrix.php, '8.0'))
248248
run: rm -Rf tests/Fixtures/app/var/cache/*
249249
- name: Run Behat tests
250+
if: (!startsWith(matrix.php, '8.0'))
250251
run: |
251252
mkdir -p build/logs/behat
252253
if [ "$COVERAGE" = '1' ]; then
253-
vendor/bin/behat --out=std --format=progress --format=junit --out=build/logs/behat/junit --profile=default-coverage --no-interaction
254+
vendor/bin/behat --out=std --format=progress --format=junit --out=build/logs/behat/junit --profile=default-coverage --no-interaction --tags='~@php8'
254255
else
255256
if [ "${{ matrix.php }}" = '7.1' ]; then
256-
vendor/bin/behat --out=std --format=progress --format=junit --out=build/logs/behat/junit --profile=default --no-interaction --tags='~@symfony/uid'
257+
vendor/bin/behat --out=std --format=progress --format=junit --out=build/logs/behat/junit --profile=default --no-interaction --tags='~@symfony/uid&&~@php8'
257258
else
258-
vendor/bin/behat --out=std --format=progress --format=junit --out=build/logs/behat/junit --profile=default --no-interaction
259+
vendor/bin/behat --out=std --format=progress --format=junit --out=build/logs/behat/junit --profile=default --no-interaction --tags='~@php8'
259260
fi
260261
fi
262+
- name: Run Behat tests
263+
if: (startsWith(matrix.php, '8.0'))
264+
run: |
265+
mkdir -p build/logs/behat
266+
if [ "$COVERAGE" = '1' ]; then
267+
vendor/bin/behat --out=std --format=progress --format=junit --out=build/logs/behat/junit --profile=default-coverage --no-interaction
268+
else
269+
vendor/bin/behat --out=std --format=progress --format=junit --out=build/logs/behat/junit --profile=default --no-interaction
270+
fi
261271
- name: Merge code coverage reports
262272
if: matrix.coverage
263273
run: |
@@ -389,7 +399,7 @@ jobs:
389399
run: tests/Fixtures/app/console cache:clear --ansi
390400
- name: Run Behat tests
391401
# @TODO remove the tag "@symfony/uid" in 3.0
392-
run: vendor/bin/behat --out=std --format=progress --profile=default --no-interaction --tags='~@symfony/uid'
402+
run: vendor/bin/behat --out=std --format=progress --profile=default --no-interaction --tags='~@symfony/uid&&~php8'
393403

394404
postgresql:
395405
name: Behat (PHP ${{ matrix.php }}) (PostgreSQL)
@@ -440,7 +450,7 @@ jobs:
440450
run: tests/Fixtures/app/console cache:clear --ansi
441451
- name: Run Behat tests
442452
run: |
443-
vendor/bin/behat --out=std --format=progress --profile=postgres --no-interaction -vv
453+
vendor/bin/behat --out=std --format=progress --profile=postgres --no-interaction -vv --tags='~php8'
444454
445455
mysql:
446456
name: Behat (PHP ${{ matrix.php }}) (MySQL)
@@ -500,7 +510,7 @@ jobs:
500510
strategy:
501511
matrix:
502512
php:
503-
- '7.4'
513+
- '8'
504514
fail-fast: false
505515
env:
506516
APP_ENV: mongodb
@@ -711,7 +721,7 @@ jobs:
711721
php:
712722
- '8.0'
713723
symfony:
714-
- '5.4'
724+
- '5.3'
715725
fail-fast: false
716726
steps:
717727
- name: Checkout
@@ -858,5 +868,10 @@ jobs:
858868
- name: Clear test app cache
859869
run: tests/Fixtures/app/console cache:clear --ansi
860870
- name: Run Behat tests
861-
run: vendor/bin/behat --out=std --format=progress --profile=default --no-interaction
871+
run: |
872+
if ( "${{ matrix.php }}" -eq '7.4' ) {
873+
vendor/bin/behat --out=std --format=progress --profile=default --no-interaction --tags='~@php8'
874+
} else {
875+
vendor/bin/behat --out=std --format=progress --profile=default --no-interaction
876+
}
862877

.php-cs-fixer.dist.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
$finder = PhpCsFixer\Finder::create()
1515
->in(__DIR__)
1616
->exclude([
17-
'src/Bridge/Symfony/Maker/Resources/skeleton',
17+
'src/Core/Bridge/Symfony/Maker/Resources/skeleton',
1818
'tests/Fixtures/app/var',
1919
])
2020
->notPath('src/Bridge/Symfony/Bundle/DependencyInjection/Configuration.php')

docs/adr/0002-resource-definition.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ In API Platform, this resource identifier is also named [IRI (Internationalized
2424
```php
2525
<?php
2626

27-
#[Get]
28-
#[Post]
27+
#[Resource]
2928
class Users
3029
{
3130
#[ApiProperty(iri="hydra:member")]
@@ -34,9 +33,11 @@ class Users
3433
public float $averageRate;
3534
}
3635

37-
#[Get]
38-
#[Put]
39-
#[Delete]
36+
#[Resource("/companies/{companyId}/users/{id}", normalization_context=["groups"= [....]]), operations={}]
37+
#[Resource(normalization_context=["groups"= [....]], operations=[
38+
new Get(),
39+
new Post(),
40+
])]
4041
class User
4142
{
4243
#[ApiProperty(identifier=true)]
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
Feature: Resource attributes
2+
In order to use the Resource attribute
3+
As a developer
4+
I should be able to fetch data from a state provider
5+
6+
@php8
7+
@!mysql
8+
@!mongodb
9+
Scenario: Retrieve a Resource collection
10+
When I add "Content-Type" header equal to "application/ld+json"
11+
And I send a "GET" request to "/attribute_resources"
12+
Then the response status code should be 200
13+
And the response should be in JSON
14+
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
15+
And the JSON should be equal to:
16+
"""
17+
{
18+
"@context": "/contexts/AttributeResources",
19+
"@id": "/attribute_resources",
20+
"@type": "hydra:Collection",
21+
"hydra:member": [
22+
{
23+
"@id": "/attribute_resources/1",
24+
"@type": "AttributeResource",
25+
"identifier": 1,
26+
"name": "Foo"
27+
},
28+
{
29+
"@id": "/attribute_resources/2",
30+
"@type": "AttributeResource",
31+
"identifier": 2,
32+
"name": "Bar"
33+
}
34+
]
35+
}
36+
"""
37+
38+
@php8
39+
@!mysql
40+
@!mongodb
41+
Scenario: Retrieve the first resource
42+
When I add "Content-Type" header equal to "application/ld+json"
43+
And I send a "GET" request to "/attribute_resources/1"
44+
Then the response status code should be 200
45+
And the response should be in JSON
46+
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
47+
And the JSON should be equal to:
48+
"""
49+
{
50+
"@context": "/contexts/AttributeResource",
51+
"@id": "/attribute_resources/1",
52+
"@type": "AttributeResource",
53+
"identifier": 1,
54+
"name": "Foo"
55+
}
56+
"""
57+
58+
@php8
59+
@!mysql
60+
@!mongodb
61+
Scenario: Retrieve the aliased resource
62+
When I add "Content-Type" header equal to "application/ld+json"
63+
And I send a "GET" request to "/dummy/1/attribute_resources/2"
64+
Then the response status code should be 301
65+
And the header "Location" should be equal to "/attribute_resources/2"
66+
And the response should be in JSON
67+
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
68+
And the JSON should be equal to:
69+
"""
70+
{
71+
"@context": "/contexts/AttributeResource",
72+
"@id": "/attribute_resources/2",
73+
"@type": "AttributeResource",
74+
"identifier": 2,
75+
"dummy": "/dummies/1",
76+
"name": "Foo"
77+
}
78+
"""
79+
80+
@php8
81+
@!mysql
82+
@!mongodb
83+
Scenario: Patch the aliased resource
84+
When I add "Content-Type" header equal to "application/merge-patch+json"
85+
And I send a "PATCH" request to "/dummy/1/attribute_resources/2" with body:
86+
"""
87+
{"name": "Patched"}
88+
"""
89+
Then the response status code should be 301
90+
And the header "Location" should be equal to "/attribute_resources/2"
91+
And the response should be in JSON
92+
And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8"
93+
And the JSON should be equal to:
94+
"""
95+
{
96+
"@context": "/contexts/AttributeResource",
97+
"@id": "/attribute_resources/2",
98+
"@type": "AttributeResource",
99+
"identifier": 2,
100+
"dummy": "/dummies/1",
101+
"name": "Patched"
102+
}
103+
"""

phpstan.neon.dist

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,38 +11,41 @@ parameters:
1111
bootstrapFiles:
1212
- vendor/bin/.phpunit/phpunit/vendor/autoload.php
1313
# We're aliasing classes for phpunit in this file, it needs to be added here see phpstan/#2194
14-
- src/Bridge/Symfony/Bundle/Test/Constraint/ArraySubset.php
14+
- src/Core/Bridge/Symfony/Bundle/Test/Constraint/ArraySubset.php
1515
- tests/Fixtures/app/AppKernel.php
1616
excludes_analyse:
1717
# Symfony cache
1818
- tests/Fixtures/app/var/cache
1919
# Deprecated integrations (will be removed in API Platform 3)
20-
- src/Bridge/NelmioApiDoc/*
20+
- src/Core/Bridge/NelmioApiDoc/*
2121
- tests/Bridge/NelmioApiDoc/*
22-
- src/Bridge/FosUser/*
22+
- src/Core/Bridge/FosUser/*
2323
# BC layer
2424
- tests/Bridge/Symfony/Bundle/DependencyInjection/Compiler/AnnotationFilterPassTest.php
2525
- tests/Annotation/ApiResourceTest.php
2626
- tests/Annotation/ApiPropertyTest.php
2727
- tests/Metadata/Resource/Factory/AnnotationResourceMetadataFactoryTest.php
2828
- tests/Fixtures/TestBundle/BrowserKit/Client.php
2929
# The Symfony Configuration API isn't good enough to be analysed
30-
- src/Bridge/Symfony/Bundle/DependencyInjection/Configuration.php
30+
- src/Core/Bridge/Symfony/Bundle/DependencyInjection/Configuration.php
3131
# Phpstan runs on phpunit > 9, a signature changed in this file
32-
- src/Bridge/Symfony/Bundle/Test/Constraint/ArraySubsetLegacy.php
32+
- src/Core/Bridge/Symfony/Bundle/Test/Constraint/ArraySubsetLegacy.php
3333
# Imported code (temporary)
34-
- src/Bridge/Symfony/Bundle/Test/BrowserKitAssertionsTrait.php
34+
- src/Core/Bridge/Symfony/Bundle/Test/BrowserKitAssertionsTrait.php
3535
- tests/Bridge/Symfony/Bundle/Test/WebTestCaseTest.php
3636
- tests/ProphecyTrait.php
3737
- tests/Behat/CoverageContext.php
3838
- tests/Fixtures/TestBundle/Security/AbstractSecurityUser.php
3939
# Templates for Maker
40-
- src/Bridge/Symfony/Maker/Resources/skeleton
40+
- src/Core/Bridge/Symfony/Maker/Resources/skeleton
4141
earlyTerminatingMethodCalls:
4242
PHPUnit\Framework\Constraint\Constraint:
4343
- fail
44+
ApiPlatform\Metadata\Resource\ResourceMetadataCollection:
45+
- handleNotFound
4446
ignoreErrors:
4547
# False positives
48+
- '#Parameter \#1 \$callback of function call_user_func expects callable\(\): mixed, non-empty-string given\.#'
4649
-
4750
message: "#Parameter \\#2 \\$dqlPart of method Doctrine\\\\ORM\\\\QueryBuilder::add\\(\\) expects array<'join'\\|int, array<int\\|string, object>\\|string>\\|object\\|string, array\\('o' => Doctrine\\\\ORM\\\\Query\\\\Expr\\\\Join\\) given\\.#"
4851
paths:
@@ -51,15 +54,15 @@ parameters:
5154
-
5255
message: '#Strict comparison using !== between .+ and .+ will always evaluate to false\.#'
5356
paths:
54-
- src/Bridge/Doctrine/Common/PropertyHelperTrait.php
57+
- src/Core/Bridge/Doctrine/Common/PropertyHelperTrait.php
5558
- '#Access to an undefined property Prophecy\\Prophecy\\ObjectProphecy<(\\?[a-zA-Z0-9_]+)+>::\$[a-zA-Z0-9_]+#'
5659
-
5760
message: '#Call to an undefined method Doctrine\\Persistence\\ObjectManager::getConnection\(\)#'
58-
path: src/Bridge/Doctrine/Common/Util/IdentifierManagerTrait.php
61+
path: src/Core/Bridge/Doctrine/Common/Util/IdentifierManagerTrait.php
5962
# https://github.com/willdurand/Negotiation/issues/89#issuecomment-513283286
6063
-
6164
message: '#Call to an undefined method Negotiation\\AcceptHeader::getType\(\)\.#'
62-
path: src/EventListener/AddFormatListener.php
65+
path: src/Core/EventListener/AddFormatListener.php
6366
- '#Parameter \#1 \$vars of class GraphQL\\Language\\AST\\(IntValue|ObjectField|ObjectValue|BooleanValue|ListValue|StringValue)Node constructor expects array<bool\|float\|GraphQL\\Language\\AST\\Location\|GraphQL\\Language\\AST\\NameNode\|GraphQL\\Language\\AST\\NodeList\|GraphQL\\Language\\AST\\SelectionSetNode\|int\|string\|null>, array<string, .+> given\.#'
6467
- '#Parameter \#1 \$defaultContext of class Symfony\\Component\\Serializer\\Encoder\\Json(De|En)code constructor expects array, (int|true) given\.#'
6568
- '#Parameter \#(2|3) \$(resourceMetadataFactory|pagination) of class ApiPlatform\\Core\\Bridge\\Doctrine\\Orm\\Extension\\PaginationExtension constructor expects (ApiPlatform\\Core\\Metadata\\Resource\\Factory\\ResourceMetadataFactoryInterface\|Symfony\\Component\\HttpFoundation\\RequestStack|ApiPlatform\\Core\\DataProvider\\Pagination\|ApiPlatform\\Core\\Metadata\\Resource\\Factory\\ResourceMetadataFactoryInterface), stdClass given\.#'
@@ -76,7 +79,7 @@ parameters:
7679
# https://github.com/phpstan/phpstan-doctrine/issues/115
7780
-
7881
message: '#Property ApiPlatform\\Core\\Test\\DoctrineMongoDbOdmFilterTestCase::\$repository \(Doctrine\\ODM\\MongoDB\\Repository\\DocumentRepository\) does not accept Doctrine\\ORM\\EntityRepository<ApiPlatform\\Core\\Tests\\Fixtures\\TestBundle\\Document\\Dummy>\.#'
79-
path: src/Test/DoctrineMongoDbOdmFilterTestCase.php
82+
path: src/Core/Test/DoctrineMongoDbOdmFilterTestCase.php
8083
-
8184
message: "#Call to method PHPUnit\\\\Framework\\\\Assert::assertSame\\(\\) with array\\('(collection_context|item_context|subresource_context)'\\) and array<Symfony\\\\Component\\\\VarDumper\\\\Cloner\\\\Data>\\|bool\\|float\\|int\\|string\\|null will always evaluate to false\\.#"
8285
path: tests/Bridge/Symfony/Bundle/DataCollector/RequestDataCollectorTest.php

phpunit.xml.dist

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
<testsuites>
1717
<testsuite name="Project Test Suite">
1818
<directory>tests</directory>
19+
<directory phpVersion="8.0" phpVersionOperator=">=">tests/Metadata/Resource/Factory</directory>
20+
<exclude>tests/Metadata/Resource/Factory</exclude>
1921
</testsuite>
2022
</testsuites>
2123

0 commit comments

Comments
 (0)