Skip to content

Support of graphqlite 7 and symfony 7 #70

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

Merged
merged 6 commits into from
Apr 13, 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
11 changes: 5 additions & 6 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,16 @@
],
"require" : {
"php" : ">=8.1",
"thecodingmachine/graphqlite" : "^6.0 || ^7.0",
"symfony/validator": "^6" ,
"doctrine/annotations": "^1.13 || ^2.0.1"
"thecodingmachine/graphqlite" : "^7.0",
"symfony/validator": "^7"
},
"require-dev": {
"phpunit/phpunit": "^9.6.5 || ^10.0.0",
"mouf/picotainer": "^1.1",
"phpstan/phpstan": "^1.8",
"php-coveralls/php-coveralls": "^2.1.0",
"symfony/translation": "^6",
"doctrine/coding-standard": "^11.1 || ^12.0"
"symfony/translation": "^7",
"doctrine/coding-standard": "^11.1|^12.0"
},
"scripts": {
"phpstan": "phpstan analyse src/ -c phpstan.neon --level=7 --no-progress",
Expand All @@ -48,7 +47,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "6.0.x-dev"
"dev-master": "7.0.x-dev"
}
},
"minimum-stability": "dev",
Expand Down
2 changes: 1 addition & 1 deletion phpcs.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

<!-- Directories to be checked -->
<file>src</file>
<!-- <file>tests</file> -->
<file>tests</file>

<!-- Include full Doctrine Coding Standard -->
<rule ref="Doctrine">
Expand Down
33 changes: 21 additions & 12 deletions src/Annotations/Assertion.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace TheCodingMachine\GraphQLite\Validator\Annotations;

use Attribute;
use BadMethodCallException;
use Symfony\Component\Validator\Constraint;
use TheCodingMachine\GraphQLite\Annotations\ParameterAnnotationInterface;
Expand All @@ -21,26 +22,34 @@
* @Attribute("constraint", type = "Symfony\Component\Validator\Constraint[]|Symfony\Component\Validator\Constraint")
* })
*/
#[Attribute(Attribute::TARGET_METHOD)]
class Assertion implements ParameterAnnotationInterface
{
/** @var string */
private $for;
private string $for;
/** @var Constraint[] */
private $constraint;
private array $constraint;

/** @param array<string, mixed> $values */
public function __construct(array $values)
{
if (! isset($values['for'])) {
throw new BadMethodCallException('The @Assert annotation must be passed a target. For instance: "@Assert(for="$email", constraint=@Email)"');
/**
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a breaking change, no ? I mean people upgrading might get errors if they are using Annotations.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes that's BC to support symfony 7

* @param array<string, mixed> $values
* @param Constraint[]|Constraint|null $constraint
*/
public function __construct(
array $values = [],
string|null $for = null,
array|Constraint|null $constraint = null,
) {
$for = $for ?? $values['for'] ?? null;
$constraint = $constraint ?? $values['constraint'] ?? null;
if ($for === null) {
throw new BadMethodCallException('The Assert attribute must be passed a target. For instance: "#[Assert(for: "$email", constraint: new Email())"');
}

if (! isset($values['constraint'])) {
throw new BadMethodCallException('The @Assert annotation must be passed one or many constraints. For instance: "@Assert(for="$email", constraint=@Email)"');
if ($constraint === null) {
throw new BadMethodCallException('The Assert attribute must be passed one or many constraints. For instance: "#[Assert(for: "$email", constraint: new Email())"');
}

$this->for = ltrim($values['for'], '$');
$this->constraint = is_array($values['constraint']) ? $values['constraint'] : [$values['constraint']];
$this->for = ltrim($for, '$');
$this->constraint = is_array($constraint) ? $constraint : [$constraint];
}

public function getTarget(): string
Expand Down
10 changes: 10 additions & 0 deletions src/Mappers/Parameters/ParameterValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,14 @@ public function getDefaultValue(): mixed
{
return $this->parameter->getDefaultValue();
}

public function getName(): string
{
return $this->parameter->getName();
}

public function getDescription(): string
{
return $this->parameter->getDescription();
}
}
13 changes: 7 additions & 6 deletions tests/Annotations/AssertionTest.php
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
<?php

declare(strict_types=1);

namespace TheCodingMachine\GraphQLite\Validator\Annotations;

use BadMethodCallException;
use PHPUnit\Framework\TestCase;

class AssertionTest extends TestCase
{

public function testException1()
public function testException1(): void
{
$this->expectException(BadMethodCallException::class);
$this->expectExceptionMessage('The @Assert annotation must be passed a target. For instance: "@Assert(for="$email", constraint=@Email)"');
$this->expectExceptionMessage('The Assert attribute must be passed a target. For instance: "#[Assert(for: "$email", constraint: new Email())"');
new Assertion([]);
}

public function testException2()
public function testException2(): void
{
$this->expectException(BadMethodCallException::class);
$this->expectExceptionMessage('The @Assert annotation must be passed one or many constraints. For instance: "@Assert(for="$email", constraint=@Email)"');
new Assertion(['for'=>'foo']);
$this->expectExceptionMessage('The Assert attribute must be passed one or many constraints. For instance: "#[Assert(for: "$email", constraint: new Email())"');
new Assertion(['for' => 'foo']);
}
}
5 changes: 3 additions & 2 deletions tests/ConstraintValidationExceptionTest.php
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
<?php

declare(strict_types=1);

namespace TheCodingMachine\GraphQLite\Validator;

use PHPUnit\Framework\TestCase;
use Symfony\Component\Validator\ConstraintViolation;

class ConstraintValidationExceptionTest extends TestCase
{

public function testException()
public function testException(): void
{
$exception = new ConstraintViolationException(new ConstraintViolation('foo', 'foo {bar}', ['bar' => 'baz'], null, null, 'invalidValue', null, 'myCode'));
$this->assertSame(400, $exception->getCode());
Expand Down
22 changes: 7 additions & 15 deletions tests/Fixtures/Controllers/UserController.php
Original file line number Diff line number Diff line change
@@ -1,29 +1,24 @@
<?php

declare(strict_types=1);

namespace TheCodingMachine\GraphQLite\Validator\Fixtures\Controllers;


use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use TheCodingMachine\GraphQLite\Annotations\Mutation;
use TheCodingMachine\GraphQLite\Annotations\Query;
use TheCodingMachine\GraphQLite\Validator\Fixtures\Types\User;
use TheCodingMachine\GraphQLite\Validator\Annotations\Assertion;
use TheCodingMachine\GraphQLite\Validator\Fixtures\Types\User;
use TheCodingMachine\GraphQLite\Validator\ValidationFailedException;

class UserController
{
private $validator;

public function __construct(ValidatorInterface $validator)
public function __construct(private ValidatorInterface $validator)
{
$this->validator = $validator;
}

/**
* @Mutation()
*/
#[Mutation]
public function createUser(string $email, string $password): User
{
$user = new User($email, $password);
Expand All @@ -38,13 +33,10 @@ public function createUser(string $email, string $password): User
return $user;
}

/**
* @Query
* @Assertion(for="email", constraint=@Assert\Email())
*/
#[Query]
#[Assertion(for: '$email', constraint: new Assert\Email())]
public function findByMail(string $email = 'a@a.com'): User
{
$user = new User($email, 'foo');
return $user;
return new User($email, 'foo');
}
}
8 changes: 3 additions & 5 deletions tests/Fixtures/InvalidControllers/InvalidController.php
Original file line number Diff line number Diff line change
@@ -1,20 +1,18 @@
<?php

declare(strict_types=1);

namespace TheCodingMachine\GraphQLite\Validator\Fixtures\InvalidControllers;


use GraphQL\Type\Definition\ResolveInfo;
use Symfony\Component\Validator\Constraints as Assert;
use TheCodingMachine\GraphQLite\Annotations\Query;
use TheCodingMachine\GraphQLite\Validator\Annotations\Assertion;

class InvalidController
{
/**
* @Query
* @Assertion(for="$resolveInfo", constraint=@Assert\Email())
*/
#[Query]
#[Assertion(for: '$resolveInfo', constraint: new Assert\Email())]
public function invalid(ResolveInfo $resolveInfo): string
{
return 'foo';
Expand Down
24 changes: 9 additions & 15 deletions tests/Fixtures/Types/User.php
Original file line number Diff line number Diff line change
@@ -1,40 +1,34 @@
<?php

declare(strict_types=1);

namespace TheCodingMachine\GraphQLite\Validator\Fixtures\Types;

use Symfony\Component\Validator\Constraints as Assert;
use TheCodingMachine\GraphQLite\Annotations\Field;
use TheCodingMachine\GraphQLite\Annotations\Type;

/**
* @Type()
*/
#[Type]
class User
{
/**
* @Assert\Email(
* message = "The email '{{ value }}' is not a valid email."
* )
*/
private $email;
#[Assert\Email(message: "The email '{{ value }}' is not a valid email.")]
private string $email;

/**
* The NotCompromisedPassword assertion asks the "HaveIBeenPawned" service if your password has already leaked or not.
* @Assert\Length(min=8)
*/
private $password;
#[Assert\Length(min: 8)]
private string $password;

public function __construct(string $email, string $password)
{
$this->email = $email;
$this->password = $password;
}

/**
* @Field()
*/
#[Field]
public function getEmail(): string
{
return $this->email;
}
}
}
27 changes: 12 additions & 15 deletions tests/IntegrationTest.php
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
<?php

declare(strict_types=1);

namespace TheCodingMachine\GraphQLite\Validator;


use Doctrine\Common\Annotations\AnnotationReader;
use GraphQL\Error\DebugFlag;
use GraphQL\GraphQL;
use GraphQL\Type\Schema;
Expand All @@ -30,19 +29,19 @@ class IntegrationTest extends TestCase
private function getSchemaFactory(): SchemaFactory
{
$container = new Picotainer([
TranslatorInterface::class => function(ContainerInterface $container) {
TranslatorInterface::class => static function (ContainerInterface $container) {
return new Translator('fr_FR');
},
ValidatorInterface::class => function(ContainerInterface $container) {
ValidatorInterface::class => static function (ContainerInterface $container) {
$build = new ValidatorBuilder();
$build->enableAnnotationMapping();
$build->setDoctrineAnnotationReader(new AnnotationReader());
$build->enableAttributeMapping();
$build->setTranslator($container->get(TranslatorInterface::class));

return $build->getValidator();
},
UserController::class => function(ContainerInterface $container) {
UserController::class => static function (ContainerInterface $container) {
return new UserController($container->get(ValidatorInterface::class));
}
},
]);

$schemaFactory = new SchemaFactory(new Psr16Cache(new ArrayAdapter()), new BasicAutoWiringContainer($container));
Expand Down Expand Up @@ -73,7 +72,7 @@ public function testEndToEndThrowException(): void

$result = GraphQL::executeQuery(
$schema,
$queryString
$queryString,
);
$result->setErrorsHandler([WebonyxErrorHandler::class, 'errorHandler']);
$result->setErrorFormatter([WebonyxErrorHandler::class, 'errorFormatter']);
Expand Down Expand Up @@ -102,7 +101,7 @@ public function testEndToEndAssert(): void

$result = GraphQL::executeQuery(
$schema,
$queryString
$queryString,
);
$result->setErrorsHandler([WebonyxErrorHandler::class, 'errorHandler']);
$result->setErrorFormatter([WebonyxErrorHandler::class, 'errorFormatter']);
Expand All @@ -114,7 +113,6 @@ public function testEndToEndAssert(): void
$this->assertSame('email', $errors[0]['extensions']['field']);
$this->assertSame('Validate', $errors[0]['extensions']['category']);


$queryString = '
{
findByMail(email: "valid@valid.com") {
Expand All @@ -125,7 +123,7 @@ public function testEndToEndAssert(): void

$result = GraphQL::executeQuery(
$schema,
$queryString
$queryString,
);
$result->setErrorsHandler([WebonyxErrorHandler::class, 'errorHandler']);
$result->setErrorFormatter([WebonyxErrorHandler::class, 'errorFormatter']);
Expand All @@ -144,14 +142,13 @@ public function testEndToEndAssert(): void

$result = GraphQL::executeQuery(
$schema,
$queryString
$queryString,
);
$result->setErrorsHandler([WebonyxErrorHandler::class, 'errorHandler']);
$result->setErrorFormatter([WebonyxErrorHandler::class, 'errorFormatter']);

$data = $result->toArray(DebugFlag::RETHROW_UNSAFE_EXCEPTIONS)['data'];
$this->assertSame('a@a.com', $data['findByMail']['email']);

}

public function testException(): void
Expand All @@ -164,4 +161,4 @@ public function testException(): void
$this->expectExceptionMessage('In method TheCodingMachine\GraphQLite\Validator\Fixtures\InvalidControllers\InvalidController::invalid(), the @Assert annotation is targeting parameter "$resolveInfo". You cannot target this parameter because it is not part of the GraphQL Input type. You can only assert parameters coming from the end user.');
$schema->validate();
}
}
}
Loading