Skip to content
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
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@
"symfony/web-profiler-bundle": "^6.1",
"symfony/yaml": "^6.1",
"twig/twig": "^1.42.3 || ^2.12 || ^3.0",
"webonyx/graphql-php": "^14.0"
"webonyx/graphql-php": "^14.0 || ^15.0"
},
"conflict": {
"doctrine/common": "<3.2.2",
Expand All @@ -95,7 +95,7 @@
"doctrine/mongodb-odm": "<2.4",
"doctrine/persistence": "<1.3",
"symfony/service-contracts": "<3",
"symfony/var-exporter" : "<6.1.1",
"symfony/var-exporter" : "<6.1.1",
"phpunit/phpunit": "<9.5",
"phpspec/prophecy": "<1.15",
"elasticsearch/elasticsearch": ">=8.0"
Expand Down
8 changes: 0 additions & 8 deletions features/graphql/authorization.feature
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ Feature: Authorization checking
And the response should be in JSON
And the header "Content-Type" should be equal to "application/json"
And the JSON node "errors[0].extensions.status" should be equal to 403
And the JSON node "errors[0].extensions.category" should be equal to user
And the JSON node "errors[0].message" should be equal to "Access Denied."
And the JSON node "data.securedDummy" should be null

Expand All @@ -42,7 +41,6 @@ Feature: Authorization checking
And the response should be in JSON
And the header "Content-Type" should be equal to "application/json"
And the JSON node "errors[0].extensions.status" should be equal to 403
And the JSON node "errors[0].extensions.category" should be equal to user
And the JSON node "errors[0].message" should be equal to "Access Denied."
And the JSON node "data.securedDummies" should be null

Expand Down Expand Up @@ -87,7 +85,6 @@ Feature: Authorization checking
And the header "Content-Type" should be equal to "application/json"
And the JSON node "data.securedDummies" should be null
And the JSON node "errors[0].extensions.status" should be equal to 403
And the JSON node "errors[0].extensions.category" should be equal to user
And the JSON node "errors[0].message" should be equal to "Access Denied."
And the JSON node "data.securedDummies" should be null

Expand All @@ -107,7 +104,6 @@ Feature: Authorization checking
And the response should be in JSON
And the header "Content-Type" should be equal to "application/json"
And the JSON node "errors[0].extensions.status" should be equal to 403
And the JSON node "errors[0].extensions.category" should be equal to user
And the JSON node "errors[0].message" should be equal to "Only admins can create a secured dummy."
And the JSON node "data.createSecuredDummy" should be null

Expand Down Expand Up @@ -206,7 +202,6 @@ Feature: Authorization checking
And the response should be in JSON
And the header "Content-Type" should be equal to "application/json"
And the JSON node "errors[0].extensions.status" should be equal to 403
And the JSON node "errors[0].extensions.category" should be equal to user
And the JSON node "errors[0].message" should be equal to "Access Denied."
And the JSON node "data.relatedSecuredDummy" should be null

Expand All @@ -228,7 +223,6 @@ Feature: Authorization checking
And the response should be in JSON
And the header "Content-Type" should be equal to "application/json"
And the JSON node "errors[0].extensions.status" should be equal to 403
And the JSON node "errors[0].extensions.category" should be equal to user
And the JSON node "errors[0].message" should be equal to "Access Denied."
And the JSON node "data.relatedSecuredDummies" should be null

Expand Down Expand Up @@ -427,7 +421,6 @@ Feature: Authorization checking
And the response should be in JSON
And the header "Content-Type" should be equal to "application/json"
And the JSON node "errors[0].extensions.status" should be equal to 403
And the JSON node "errors[0].extensions.category" should be equal to user
And the JSON node "errors[0].message" should be equal to "Access Denied."
And the JSON node "data.securedDummy" should be null

Expand Down Expand Up @@ -560,7 +553,6 @@ Feature: Authorization checking
And the response should be in JSON
And the header "Content-Type" should be equal to "application/json"
And the JSON node "errors[0].extensions.status" should be equal to 403
And the JSON node "errors[0].extensions.category" should be equal to user
And the JSON node "errors[0].message" should be equal to "Access Denied."
And the JSON node "data.updateSecuredDummy" should be null

Expand Down
38 changes: 3 additions & 35 deletions features/graphql/input_output.feature
Original file line number Diff line number Diff line change
Expand Up @@ -143,15 +143,12 @@ Feature: GraphQL DTO input and output
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 should be equal to:
And the JSON should be a superset of:
"""
{
"errors": [
{
"message": "Cannot query field \"id\" on type \"DummyDtoNoOutput\".",
"extensions": {
"category": "graphql"
},
"locations": [
{
"line": 4,
Expand All @@ -175,37 +172,8 @@ Feature: GraphQL DTO input and output
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 should be equal to:
"""
{
"errors": [
{
"message": "Field \"lorem\" is not defined by type createDummyDtoNoInputInput.",
"extensions": {
"category": "graphql"
},
"locations": [
{
"line": 2,
"column": 33
}
]
},
{
"message": "Field \"ipsum\" is not defined by type createDummyDtoNoInputInput.",
"extensions": {
"category": "graphql"
},
"locations": [
{
"line": 2,
"column": 53
}
]
}
]
}
"""
And the JSON node "errors[0].message" should match '/^Field "lorem" is not defined by type "?createDummyDtoNoInputInput"?\.$/'
And the JSON node "errors[1].message" should match '/^Field "ipsum" is not defined by type "?createDummyDtoNoInputInput"?\.$/'

Scenario: Use messenger with GraphQL and an input where the handler gives a synchronous result
When I send the following GraphQL request:
Expand Down
3 changes: 1 addition & 2 deletions features/graphql/introspection.feature
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ Feature: GraphQL introspection support
And the response should be in JSON
And the header "Content-Type" should be equal to "application/json"
And the JSON node "errors[0].extensions.status" should be equal to 400
And the JSON node "errors[0].extensions.category" should be equal to user
And the JSON node "errors[0].message" should be equal to "GraphQL query is not valid."

Scenario: Introspect the GraphQL schema
Expand Down Expand Up @@ -563,7 +562,7 @@ Feature: GraphQL introspection support
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 "errors[0].debugMessage" should be equal to 'Type with id "VoDummyInspectionCursorConnection" is not present in the types container'
And the GraphQL debug message 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"

Expand Down
2 changes: 1 addition & 1 deletion features/graphql/mutation.feature
Original file line number Diff line number Diff line change
Expand Up @@ -673,7 +673,7 @@ Feature: GraphQL mutation support
}
}
"""
And the response should be in JSON
Then the response should be in JSON
And the header "Content-Type" should be equal to "application/json"
And the JSON node "data.createWritableId.writableId.id" should be equal to "/writable_ids/c6b722fe-0331-48c4-a214-f81f9f1ca082"
And the JSON node "data.createWritableId.writableId._id" should be equal to "c6b722fe-0331-48c4-a214-f81f9f1ca082"
Expand Down
49 changes: 26 additions & 23 deletions features/graphql/query.feature
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ Feature: GraphQL query support
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 "errors[0].debugMessage" should be equal to 'No route matches "/foo/1".'
And the GraphQL debug message should be equal to 'No route matches "/foo/1".'
And the JSON should be valid according to this schema:
"""
{
Expand All @@ -289,35 +289,38 @@ Feature: GraphQL query support
"items": {
"type": "object",
"properties": {
"debugMessage": {"type": "string"},
"message": {"type": "string"},
"extensions": {"type": "object"},
"extensions": {
"type": "object",
"properties": {
"debugMessage": {"type": "string"},
"file": {"type": "string"},
"line": {"type": "integer"},
"trace": {
"type": "array",
"items": {
"type": "object",
"properties": {
"file": {"type": "string"},
"line": {"type": "integer"},
"call": {"type": ["string", "null"]},
"function": {"type": ["string", "null"]}
},
"additionalProperties": false
},
"minItems": 1
}
}
},
"locations": {"type": "array"},
"path": {"type": "array"},
"trace": {
"type": "array",
"items": {
"type": "object",
"properties": {
"file": {"type": "string"},
"line": {"type": "integer"},
"call": {"type": ["string", "null"]},
"function": {"type": ["string", "null"]}
},
"additionalProperties": false
},
"minItems": 1
}
"path": {"type": "array"}
},
"required": [
"debugMessage",
"message",
"extensions",
"locations",
"path",
"trace"
],
"additionalProperties": false
"path"
]
},
"minItems": 1,
"maxItems": 1
Expand Down
79 changes: 51 additions & 28 deletions features/graphql/schema.feature
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ Feature: GraphQL schema-related features
###The dummy name###
name: String!
}

"""
And the command output should contain:
"""
###Cursor connection for DummyFriend.###
type DummyFriendCursorConnection {
edges: [DummyFriendEdge]
Expand All @@ -37,41 +39,62 @@ Feature: GraphQL schema-related features
hasPreviousPage: Boolean!
}
"""
And the command output should contain:
"""
###Updates a DummyFriend.###
updateDummyFriend(input: updateDummyFriendInput!): updateDummyFriendPayload

Scenario: Export the GraphQL schema in SDL with comment descriptions
When I run the command "api:graphql:export" with options:
| --comment-descriptions | true |
Then the command output should contain:
###Deletes a DummyFriend.###
deleteDummyFriend(input: deleteDummyFriendInput!): deleteDummyFriendPayload

###Creates a DummyFriend.###
createDummyFriend(input: createDummyFriendInput!): createDummyFriendPayload
"""
# Dummy Friend.
type DummyFriend implements Node {
And the command output should contain:
"""
###Updates a DummyFriend.###
input updateDummyFriendInput {
id: ID!

# The id
_id: Int!

# The dummy name
name: String!
###The dummy name###
name: String
clientMutationId: String
}

# Cursor connection for DummyFriend.
type DummyFriendCursorConnection {
edges: [DummyFriendEdge]
pageInfo: DummyFriendPageInfo!
totalCount: Int!
"""
And the command output should contain:
"""
###Updates a DummyFriend.###
type updateDummyFriendPayload {
dummyFriend: DummyFriend
clientMutationId: String
}
"""
And the command output should contain:
"""
###Deletes a DummyFriend.###
input deleteDummyFriendInput {
id: ID!
clientMutationId: String
}

# Edge of DummyFriend.
type DummyFriendEdge {
node: DummyFriend
cursor: String!
###Deletes a DummyFriend.###
type deleteDummyFriendPayload {
dummyFriend: DummyFriend
clientMutationId: String
}
"""
And the command output should contain:
"""
###Creates a DummyFriend.###
input createDummyFriendInput {
###The dummy name###
name: String!
clientMutationId: String
}

# Information about the current page.
type DummyFriendPageInfo {
endCursor: String
startCursor: String
hasNextPage: Boolean!
hasPreviousPage: Boolean!
###Creates a DummyFriend.###
type createDummyFriendPayload {
dummyFriend: DummyFriend
clientMutationId: String
}
"""
3 changes: 2 additions & 1 deletion features/graphql/type.feature
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,5 @@ Feature: GraphQL type support
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 "errors[0].message" should be equal to 'Variable "$itemDate" got invalid value "bad date"; Expected type DateTime; DateTime cannot represent non date value: "bad date"'
And the JSON node "errors[0].message" should contain 'Variable "$itemDate" got invalid value "bad date";'
And the JSON node "errors[0].message" should contain 'DateTime cannot represent non date value: "bad date"'
6 changes: 2 additions & 4 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,6 @@ parameters:
-
message: '#Call to an undefined method Negotiation\\AcceptHeader::getType\(\)\.#'
path: src/Symfony/EventListener/AddFormatListener.php
- '#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\.#'
-
message: '#Parameter \#1 \$objectValue of method GraphQL\\Type\\Definition\\InterfaceType::resolveType\(\) expects object, array(<string, string>)? given.#'
path: tests/GraphQl/Type/TypeBuilderTest.php
# https://github.com/phpstan/phpstan-symfony/issues/76
-
message: '#Service "test" is not registered in the container\.#'
Expand All @@ -78,6 +74,8 @@ parameters:
-
message: "#Call to function method_exists\\(\\) with ApiPlatform\\\\JsonApi\\\\Serializer\\\\ItemNormalizer and 'setCircularReferenc…' will always evaluate to false\\.#"
path: tests/JsonApi/Serializer/ItemNormalizerTest.php
- '#Method GraphQL\\Type\\Definition\\WrappingType::getWrappedType\(\) invoked with 1 parameter, 0 required\.#'
- '#Access to an undefined property GraphQL\\Type\\Definition\\NamedType&GraphQL\\Type\\Definition\\Type::\$name\.#'
# See https://github.com/phpstan/phpstan-symfony/issues/27
-
message: '#^Service "[^"]+" is private.$#'
Expand Down
5 changes: 4 additions & 1 deletion src/GraphQl/Serializer/Exception/HttpExceptionNormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,10 @@ public function normalize(mixed $object, string $format = null, array $context =
$error = FormattedError::createFromException($object);
$error['message'] = $httpException->getMessage();
$error['extensions']['status'] = $statusCode = $httpException->getStatusCode();
$error['extensions']['category'] = $statusCode < 500 ? 'user' : Error::CATEGORY_INTERNAL;
// graphql-php < 15
if (\defined(Error::class.'::CATEGORY_INTERNAL')) {
$error['extensions']['category'] = $statusCode < 500 ? 'user' : Error::CATEGORY_INTERNAL;
}

return $error;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,10 @@ public function normalize(mixed $object, string $format = null, array $context =
}
}
$error['extensions']['status'] = $statusCode;
$error['extensions']['category'] = 'user';
// graphql-php < 15
if (\defined(Error::class.'::CATEGORY_INTERNAL')) {
$error['extensions']['category'] = 'user';
}
$error['extensions']['violations'] = [];

/** @var ConstraintViolation $violation */
Expand Down
Loading