Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
db380b7
ignore api_platform.state.item_provider when Doctrine is not enabled …
dgoosens Sep 17, 2022
d565f95
chore: update branch-alias (#5030)
dunglas Sep 29, 2022
5ced1c6
Merge 3.0 onto main
soyuka Sep 29, 2022
8108899
Merge pull request #5033 from soyuka/main
soyuka Sep 29, 2022
886cbda
Merge branch 3.0 into main
soyuka Oct 10, 2022
1a3ae34
Merge pull request #5061 from soyuka/main
soyuka Oct 11, 2022
c493676
chore: adding of stale bot (#5087)
davy-beauzil Nov 4, 2022
ac70771
ci: add automatic release notes (#5119)
soyuka Nov 4, 2022
6abd0fe
Merge 3.0 into main (#5121)
soyuka Nov 4, 2022
d44ddb1
Revert "Merge 3.0 into main (#5121)"
soyuka Nov 4, 2022
1f747a4
Merge branch 3.0 into main
soyuka Nov 4, 2022
142c604
Merge pull request #5122 from soyuka/revert-5121-3.0
soyuka Nov 4, 2022
114b31e
ci: remove autorelease, improve stalebot (#5166)
soyuka Nov 6, 2022
f1ecc30
feat(openapi): add backed enum support (#5120)
alanpoulain Nov 7, 2022
36d930e
feat(graphql): enable profiler panel when using graphql (#5072)
ArnoudThibaut Nov 10, 2022
692c6c3
Merge branch 3.0 into main
soyuka Nov 15, 2022
52a7274
Merge branch 3.0 into main
soyuka Nov 15, 2022
40b637f
Merge branch '3.0'
soyuka Nov 25, 2022
a828af0
feat: use phpdoc-parser instead of phpdocumentor (#5214)
alanpoulain Nov 29, 2022
8744857
feat: add GraphQL enum support (#5199)
alanpoulain Nov 29, 2022
87e64e1
fix: not initialized params in PhpDocResourceMetadataCollectionFactor…
alanpoulain Dec 2, 2022
7670df1
Merge branch '3.0'
alanpoulain Dec 2, 2022
a5e0616
Merge pull request #5250 from alanpoulain/chore/merge-3.0
alanpoulain Dec 2, 2022
06185b7
feat: add groups filter whitelist info to swagger (#5244)
SpartakusMd Dec 7, 2022
d0fcd70
fix(graphql): remove inline styles and add twig blocks to aid overrid…
moismailzai Dec 9, 2022
7fa9ca5
fix: do not use api_graphql_graphiql route when graphiQl is disabled …
MatTheCat Dec 14, 2022
7f09a26
fix(jsonschema): remove @id @type @context from input schema (#5267)
benblub Dec 16, 2022
2dcddb4
Merge branch 3.0
soyuka Dec 16, 2022
ac71153
Merge pull request #5274 from soyuka/main
soyuka Dec 16, 2022
c145ec7
feat(openapi): add ApiResource::openapi and deprecate openapiContext …
vincentchalamon Dec 16, 2022
5a084f8
test wil check elasticsearch as true before check the indice
rapaelector Dec 16, 2022
30b7082
Merge branch 'api-platform:main' into patch-2
rapaelector Dec 16, 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
20 changes: 20 additions & 0 deletions .github/stale.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 60
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- Hacktoberfest
- bug
- enhancement
- RFC
- ⭐ EU-FOSSA Hackathon
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false
3 changes: 3 additions & 0 deletions .php-cs-fixer.dist.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@
->notPath('src/Annotation/ApiResource.php') // temporary
->notPath('src/Annotation/ApiSubresource.php') // temporary
->notPath('tests/Fixtures/TestBundle/Entity/DummyPhp8.php') // temporary
->notPath('tests/Fixtures/TestBundle/Enum/EnumWithDescriptions.php') // PHPDoc on enum cases
->notPath('tests/Fixtures/TestBundle/Enum/GamePlayMode.php') // PHPDoc on enum cases
->notPath('tests/Fixtures/TestBundle/Enum/GenderTypeEnum.php') // PHPDoc on enum cases
->append([
'tests/Fixtures/app/console',
]);
Expand Down
7 changes: 3 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,9 @@
"guzzlehttp/guzzle": "^6.0 || ^7.0",
"jangregor/phpstan-prophecy": "^1.0",
"justinrainbow/json-schema": "^5.2.1",
"phpdocumentor/reflection-docblock": "^3.0 || ^4.0 || ^5.1",
"phpdocumentor/type-resolver": "^0.3 || ^0.4 || ^1.4",
"phpspec/prophecy-phpunit": "^2.0",
"phpstan/extension-installer": "^1.1",
"phpstan/phpdoc-parser": "^1.13",
"phpstan/phpstan": "^1.1",
"phpstan/phpstan-doctrine": "^1.0",
"phpstan/phpstan-phpunit": "^1.0",
Expand Down Expand Up @@ -101,7 +100,7 @@
"doctrine/mongodb-odm-bundle": "To support MongoDB. Only versions 4.0 and later are supported.",
"elasticsearch/elasticsearch": "To support Elasticsearch.",
"ocramius/package-versions": "To display the API Platform's version in the debug bar.",
"phpdocumentor/reflection-docblock": "To support extracting metadata from PHPDoc.",
"phpstan/phpdoc-parser": "To support extracting metadata from PHPDoc.",
"psr/cache-implementation": "To use metadata caching.",
"ramsey/uuid": "To support Ramsey's UUID identifiers.",
"symfony/cache": "To have metadata caching when using Symfony integration.",
Expand Down Expand Up @@ -138,7 +137,7 @@
},
"extra": {
"branch-alias": {
"dev-main": "3.0.x-dev"
"dev-main": "3.1.x-dev"
},
"symfony": {
"require": "^6.1"
Expand Down
55 changes: 55 additions & 0 deletions features/graphql/introspection.feature
Original file line number Diff line number Diff line change
Expand Up @@ -566,3 +566,58 @@ Feature: GraphQL introspection support
And the JSON node "errors[0].debugMessage" should be equal to 'Type with id "VoDummyInspectionCursorConnection" is not present in the types container'
And the JSON node "data.typeNotAvailable" should be null
And the JSON node "data.typeOwner.fields[1].type.name" should be equal to "VoDummyInspectionCursorConnection"

Scenario: Introspect an enum
When I send the following GraphQL request:
"""
{
person: __type(name: "Person") {
name
fields {
name
type {
name
description
enumValues {
name
description
}
}
}
}
}
"""
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/json"
And the JSON node "data.person.fields[1].type.name" should be equal to "GenderTypeEnum"
#And the JSON node "data.person.fields[1].type.description" should be equal to "An enumeration of genders."
And the JSON node "data.person.fields[1].type.enumValues[0].name" should be equal to "MALE"
#And the JSON node "data.person.fields[1].type.enumValues[0].description" should be equal to "The male gender."
And the JSON node "data.person.fields[1].type.enumValues[1].name" should be equal to "FEMALE"
And the JSON node "data.person.fields[1].type.enumValues[1].description" should be equal to "The female gender."

Scenario: Introspect an enum resource
When I send the following GraphQL request:
"""
{
videoGame: __type(name: "VideoGame") {
name
fields {
name
type {
name
kind
ofType {
name
kind
}
}
}
}
}
"""
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/json"
And the JSON node "data.videoGame.fields[3].type.ofType.name" should be equal to "GamePlayMode"
63 changes: 63 additions & 0 deletions features/graphql/mutation.feature
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,69 @@ Feature: GraphQL mutation support
And the JSON node "data.createDummy.dummy.arrayData[1]" should be equal to baz
And the JSON node "data.createDummy.clientMutationId" should be equal to "myId"

Scenario: Create an item with an enum
When I send the following GraphQL request:
"""
mutation {
createPerson(input: {name: "Mob", genderType: FEMALE}) {
person {
id
name
genderType
}
}
}
"""
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/json"
And the JSON node "data.createPerson.person.id" should be equal to "/people/1"
And the JSON node "data.createPerson.person.name" should be equal to "Mob"
And the JSON node "data.createPerson.person.genderType" should be equal to "FEMALE"

Scenario: Create an item with an enum as a resource
When I send the following GraphQL request:
"""
{
gamePlayModes {
id
name
}
gamePlayMode(id: "/game_play_modes/SINGLE_PLAYER") {
name
}
}
"""
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/json"
And the JSON node "data.gamePlayModes" should have 3 elements
And the JSON node "data.gamePlayModes[2].id" should be equal to "/game_play_modes/SINGLE_PLAYER"
And the JSON node "data.gamePlayModes[2].name" should be equal to "SINGLE_PLAYER"
And the JSON node "data.gamePlayMode.name" should be equal to "SINGLE_PLAYER"
When I send the following GraphQL request:
"""
mutation {
createVideoGame(input: {name: "Baten Kaitos", playMode: "/game_play_modes/SINGLE_PLAYER"}) {
videoGame {
id
name
playMode {
id
name
}
}
}
}
"""
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/json"
And the JSON node "data.createVideoGame.videoGame.id" should be equal to "/video_games/1"
And the JSON node "data.createVideoGame.videoGame.name" should be equal to "Baten Kaitos"
And the JSON node "data.createVideoGame.videoGame.playMode.id" should be equal to "/game_play_modes/SINGLE_PLAYER"
And the JSON node "data.createVideoGame.videoGame.playMode.name" should be equal to "SINGLE_PLAYER"

Scenario: Delete an item through a mutation
When I send the following GraphQL request:
"""
Expand Down
24 changes: 24 additions & 0 deletions features/openapi/docs.feature
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ Feature: Documentation support
And the OpenAPI class "OverriddenOperationDummy-overridden_operation_dummy_put" exists
And the OpenAPI class "OverriddenOperationDummy-overridden_operation_dummy_read" exists
And the OpenAPI class "OverriddenOperationDummy-overridden_operation_dummy_write" exists
And the OpenAPI class "Person" exists
And the OpenAPI class "RelatedDummy" exists
And the OpenAPI class "NoCollectionDummy" exists
And the OpenAPI class "RelatedToDummyFriend" exists
Expand All @@ -57,6 +58,29 @@ Feature: Documentation support
# Properties
And the "id" property exists for the OpenAPI class "Dummy"
And the "name" property is required for the OpenAPI class "Dummy"
And the "genderType" property exists for the OpenAPI class "Person"
And the "genderType" property for the OpenAPI class "Person" should be equal to:
"""
{
"default": "male",
"example": "male",
"type": "string",
"enum": [
"male",
"female",
null
],
"nullable": true
}
"""
And the "playMode" property exists for the OpenAPI class "VideoGame"
And the "playMode" property for the OpenAPI class "VideoGame" should be equal to:
"""
{
"type": "string",
"format": "iri-reference"
}
"""
# Enable these tests when SF 4.4 / PHP 7.1 support is dropped
#And the "isDummyBoolean" property exists for the OpenAPI class "DummyBoolean"
#And the "isDummyBoolean" property is not read only for the OpenAPI class "DummyBoolean"
Expand Down
2 changes: 2 additions & 0 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,5 @@ parameters:
-
message: '#^Property .+ is unused.$#'
path: tests/Doctrine/Odm/PropertyInfo/Fixtures/DoctrineDummy.php
# Waiting for https://github.com/laminas/laminas-code/pull/150
- '#Call to an undefined method ReflectionEnum::.+#'
5 changes: 5 additions & 0 deletions src/Api/FilterInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ interface FilterInterface
* 'type' => 'integer',
* ]
* ]
* - schema (optional): schema definition,
* e.g. 'schema' => [
* 'type' => 'string',
* 'enum' => ['value_1', 'value_2'],
* ]
* The description can contain additional data specific to a filter.
*
* @see \ApiPlatform\OpenApi\Factory\OpenApiFactory::getFiltersParameters
Expand Down
17 changes: 13 additions & 4 deletions src/Doctrine/Odm/PropertyInfo/DoctrineExtractor.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ public function getTypes($class, $property, array $context = []): ?array
if ($metadata->hasField($property)) {
$typeOfField = $metadata->getTypeOfField($property);
$nullable = $metadata instanceof MongoDbClassMetadata && $metadata->isNullable($property);
$enumType = null;
if (null !== $enumClass = $metadata instanceof MongoDbClassMetadata ? $metadata->getFieldMapping($property)['enumType'] ?? null : null) {
$enumType = new Type(Type::BUILTIN_TYPE_OBJECT, $nullable, $enumClass);
}

switch ($typeOfField) {
case MongoDbType::DATE:
Expand All @@ -102,11 +106,16 @@ public function getTypes($class, $property, array $context = []): ?array
return [new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true)];
case MongoDbType::COLLECTION:
return [new Type(Type::BUILTIN_TYPE_ARRAY, $nullable, null, true, new Type(Type::BUILTIN_TYPE_INT))];
default:
$builtinType = $this->getPhpType($typeOfField);

return $builtinType ? [new Type($builtinType, $nullable)] : null;
case MongoDbType::INT:
case MongoDbType::STRING:
if ($enumType) {
return [$enumType];
}
}

$builtinType = $this->getPhpType($typeOfField);

return $builtinType ? [new Type($builtinType, $nullable)] : null;
}

return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ public function create(string $resourceClass): ResourceMetadataCollection

private function hasIndices(Operation $operation): bool
{
if (false === $operation->getElasticsearch() || null === $operation->getElasticsearch()) {
if (false !== $operation->getElasticsearch()) {
return false;
}

Expand Down
10 changes: 1 addition & 9 deletions src/GraphQl/Resolver/Factory/CollectionResolverFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
use ApiPlatform\Util\CloneTrait;
use GraphQL\Type\Definition\ResolveInfo;
use Psr\Container\ContainerInterface;
use Symfony\Component\HttpFoundation\RequestStack;

/**
* Creates a function retrieving a collection to resolve a GraphQL query or a field returned by a mutation.
Expand All @@ -35,7 +34,7 @@ final class CollectionResolverFactory implements ResolverFactoryInterface
{
use CloneTrait;

public function __construct(private readonly ReadStageInterface $readStage, private readonly SecurityStageInterface $securityStage, private readonly SecurityPostDenormalizeStageInterface $securityPostDenormalizeStage, private readonly SerializeStageInterface $serializeStage, private readonly ContainerInterface $queryResolverLocator, private readonly ?RequestStack $requestStack = null)
public function __construct(private readonly ReadStageInterface $readStage, private readonly SecurityStageInterface $securityStage, private readonly SecurityPostDenormalizeStageInterface $securityPostDenormalizeStage, private readonly SerializeStageInterface $serializeStage, private readonly ContainerInterface $queryResolverLocator)
{
}

Expand All @@ -47,13 +46,6 @@ public function __invoke(?string $resourceClass = null, ?string $rootClass = nul
return null;
}

if ($this->requestStack && null !== $request = $this->requestStack->getCurrentRequest()) {
$request->attributes->set(
'_graphql_collections_args',
[$resourceClass => $args] + $request->attributes->get('_graphql_collections_args', [])
);
}

$resolverContext = ['source' => $source, 'args' => $args, 'info' => $info, 'is_collection' => true, 'is_mutation' => false, 'is_subscription' => false];

$collection = ($this->readStage)($resourceClass, $rootClass, $operation, $resolverContext);
Expand Down
41 changes: 38 additions & 3 deletions src/GraphQl/Type/FieldsBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,16 @@
*
* @author Alan Poulain <[email protected]>
*/
final class FieldsBuilder implements FieldsBuilderInterface
final class FieldsBuilder implements FieldsBuilderInterface, FieldsBuilderEnumInterface
{
public function __construct(private readonly PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, private readonly PropertyMetadataFactoryInterface $propertyMetadataFactory, private readonly ResourceMetadataCollectionFactoryInterface $resourceMetadataCollectionFactory, private readonly ResourceClassResolverInterface $resourceClassResolver, private readonly TypesContainerInterface $typesContainer, private readonly TypeBuilderInterface $typeBuilder, private readonly TypeConverterInterface $typeConverter, private readonly ResolverFactoryInterface $itemResolverFactory, private readonly ResolverFactoryInterface $collectionResolverFactory, private readonly ResolverFactoryInterface $itemMutationResolverFactory, private readonly ResolverFactoryInterface $itemSubscriptionResolverFactory, private readonly ContainerInterface $filterLocator, private readonly Pagination $pagination, private readonly ?NameConverterInterface $nameConverter, private readonly string $nestingSeparator)
private readonly TypeBuilderEnumInterface|TypeBuilderInterface $typeBuilder;

public function __construct(private readonly PropertyNameCollectionFactoryInterface $propertyNameCollectionFactory, private readonly PropertyMetadataFactoryInterface $propertyMetadataFactory, private readonly ResourceMetadataCollectionFactoryInterface $resourceMetadataCollectionFactory, private readonly ResourceClassResolverInterface $resourceClassResolver, private readonly TypesContainerInterface $typesContainer, TypeBuilderEnumInterface|TypeBuilderInterface $typeBuilder, private readonly TypeConverterInterface $typeConverter, private readonly ResolverFactoryInterface $itemResolverFactory, private readonly ResolverFactoryInterface $collectionResolverFactory, private readonly ResolverFactoryInterface $itemMutationResolverFactory, private readonly ResolverFactoryInterface $itemSubscriptionResolverFactory, private readonly ContainerInterface $filterLocator, private readonly Pagination $pagination, private readonly ?NameConverterInterface $nameConverter, private readonly string $nestingSeparator)
{
if ($typeBuilder instanceof TypeBuilderInterface) {
@trigger_error(sprintf('$typeBuilder argument of FieldsBuilder implementing "%s" is deprecated since API Platform 3.1. It has to implement "%s" instead.', TypeBuilderInterface::class, TypeBuilderEnumInterface::class), \E_USER_DEPRECATED);
}
$this->typeBuilder = $typeBuilder;
}

/**
Expand Down Expand Up @@ -226,6 +232,26 @@ public function getResourceObjectTypeFields(?string $resourceClass, Operation $o
return $fields;
}

/**
* {@inheritdoc}
*/
public function getEnumFields(string $enumClass): array
{
$rEnum = new \ReflectionEnum($enumClass);

$enumCases = [];
foreach ($rEnum->getCases() as $rCase) {
$enumCase = ['value' => $rCase->getBackingValue()];
$propertyMetadata = $this->propertyMetadataFactory->create($enumClass, $rCase->getName());
if ($enumCaseDescription = $propertyMetadata->getDescription()) {
$enumCase['description'] = $enumCaseDescription;
}
$enumCases[$rCase->getName()] = $enumCase;
}

return $enumCases;
}

/**
* {@inheritdoc}
*/
Expand Down Expand Up @@ -481,7 +507,16 @@ private function convertType(Type $type, bool $input, Operation $resourceOperati
}

if ($this->typeBuilder->isCollection($type)) {
return $this->pagination->isGraphQlEnabled($resourceOperation) && !$input ? $this->typeBuilder->getResourcePaginatedCollectionType($graphqlType, $resourceClass, $resourceOperation) : GraphQLType::listOf($graphqlType);
if (!$input && $this->pagination->isGraphQlEnabled($resourceOperation)) {
// Deprecated path, to remove in API Platform 4.
if ($this->typeBuilder instanceof TypeBuilderInterface) {
return $this->typeBuilder->getResourcePaginatedCollectionType($graphqlType, $resourceClass, $resourceOperation);
}

return $this->typeBuilder->getPaginatedCollectionType($graphqlType, $resourceOperation);
}

return GraphQLType::listOf($graphqlType);
}

return $forceNullable || !$graphqlType instanceof NullableType || $type->isNullable() || ($rootOperation instanceof Mutation && 'update' === $rootOperation->getName())
Expand Down
Loading