Skip to content

Commit

Permalink
✨ Support for using attribute as a target to parameter. Deprecate usi…
Browse files Browse the repository at this point in the history
…ng as a target to the method. Add warning about next major release changes

Refs: thecodingmachine/graphqlite#708 (comment)
  • Loading branch information
lalshe committed Nov 18, 2024
1 parent 2f677f6 commit 14c12fc
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 20 deletions.
22 changes: 15 additions & 7 deletions src/Annotations/Assertion.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,21 @@
/**
* Use this annotation to validate a parameter for a query or mutation.
*
* Note 1: using this attribute as a target to the method (not parameter) is deprecated and will be removed in 8.0.
* Note 2: support for `doctrine/annotations` will be removed in 8.0.
* Note 3: this class won't implement `ParameterAnnotationInterface` in 8.0.
*
* @Annotation
* @Target({"METHOD"})
* @Attributes({
* @Attribute("for", type = "string"),
* @Attribute("constraint", type = "Symfony\Component\Validator\Constraint[]|Symfony\Component\Validator\Constraint")
* })
*/
#[Attribute(Attribute::TARGET_METHOD)]
#[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_PARAMETER)]
class Assertion implements ParameterAnnotationInterface
{
private string $for;
private ?string $for = null;
/** @var Constraint[] */
private array $constraint;

Expand All @@ -40,20 +44,24 @@ public function __construct(
) {
$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 ($constraint === null) {
throw new BadMethodCallException('The Assert attribute must be passed one or many constraints. For instance: "#[Assert(for: "$email", constraint: new Email())"');
throw new BadMethodCallException('The Assertion attribute must be passed one or many constraints. For instance: "#[Assertion(constraint: new Email())"');
}

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

if (null !== $for) {
$this->for = ltrim($for, '$');
}
}

public function getTarget(): string
{
if ($this->for === null) {
throw new BadMethodCallException('The Assertion attribute must be passed a target. For instance: "#[Assertion(for: "$email", constraint: new Email())"');
}

return $this->for;
}

Expand Down
9 changes: 1 addition & 8 deletions tests/Annotations/AssertionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,10 @@

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

public function testException2(): void
{
$this->expectException(BadMethodCallException::class);
$this->expectExceptionMessage('The Assert attribute must be passed one or many constraints. For instance: "#[Assert(for: "$email", constraint: new Email())"');
$this->expectExceptionMessage('The Assertion attribute must be passed one or many constraints. For instance: "#[Assertion(constraint: new Email())"');
new Assertion(['for' => 'foo']);
}
}
11 changes: 10 additions & 1 deletion tests/Fixtures/Controllers/UserController.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,17 @@ public function createUser(string $email, string $password): User

#[Query]
#[Assertion(for: '$email', constraint: new Assert\Email())]
public function findByMail(string $email = '[email protected]'): User
public function findByMailTargetMethod(string $email = '[email protected]'): User
{
// deprecated way of using the Assertion annotation - as a target of the method
return new User($email, 'foo');
}

#[Query]
public function findByMail(
#[Assertion(constraint: new Assert\Email())]
string $email = '[email protected]',
): User {
return new User($email, 'foo');
}
}
7 changes: 4 additions & 3 deletions tests/Fixtures/InvalidControllers/InvalidController.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@
class InvalidController
{
#[Query]
#[Assertion(for: '$resolveInfo', constraint: new Assert\Email())]
public function invalid(ResolveInfo $resolveInfo): string
{
public function invalid(
#[Assertion(for: '$resolveInfo', constraint: new Assert\Email())]
ResolveInfo $resolveInfo,
): string {
return 'foo';
}
}
65 changes: 64 additions & 1 deletion tests/IntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,6 @@ public function testEndToEndAssert(): void

$errors = $result->toArray(DebugFlag::RETHROW_UNSAFE_EXCEPTIONS)['errors'];

// TODO: find why message is not in French...
$this->assertSame('This value is not a valid email address.', $errors[0]['message']);
$this->assertSame('email', $errors[0]['extensions']['field']);
$this->assertSame('Validate', $errors[0]['extensions']['category']);
Expand Down Expand Up @@ -151,6 +150,70 @@ public function testEndToEndAssert(): void
$this->assertSame('[email protected]', $data['findByMail']['email']);
}

public function testEndToEndAssertForDeprecatedWay(): void
{
$schema = $this->getSchema();
$schema->assertValid();

$queryString = '
{
findByMailTargetMethod(email: "notvalid") {
email
}
}
';

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

$errors = $result->toArray(DebugFlag::RETHROW_UNSAFE_EXCEPTIONS)['errors'];

$this->assertSame('This value is not a valid email address.', $errors[0]['message']);
$this->assertSame('email', $errors[0]['extensions']['field']);
$this->assertSame('Validate', $errors[0]['extensions']['category']);

$queryString = '
{
findByMailTargetMethod(email: "[email protected]") {
email
}
}
';

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

$data = $result->toArray(DebugFlag::RETHROW_UNSAFE_EXCEPTIONS)['data'];
$this->assertSame('[email protected]', $data['findByMailTargetMethod']['email']);

// Test default parameter
$queryString = '
{
findByMailTargetMethod {
email
}
}
';

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

$data = $result->toArray(DebugFlag::RETHROW_UNSAFE_EXCEPTIONS)['data'];
$this->assertSame('[email protected]', $data['findByMailTargetMethod']['email']);
}

public function testException(): void
{
$schemaFactory = $this->getSchemaFactory();
Expand Down

0 comments on commit 14c12fc

Please sign in to comment.