Skip to content

Commit a77a314

Browse files
authored
Merge pull request #968 from bravik/fix_validator_namespace_collisions
Fix validation constraints namespace conflicts in generated definition files
2 parents 43b99b7 + 803af2d commit a77a314

File tree

7 files changed

+181
-6
lines changed

7 files changed

+181
-6
lines changed

src/Generator/TypeBuilder.php

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@
4444
use function reset;
4545
use function rtrim;
4646
use function strpos;
47-
use function strrchr;
4847
use function strtolower;
4948
use function substr;
5049

@@ -617,20 +616,18 @@ protected function buildConstraints(array $constraints = [], bool $inClosure = t
617616
if (false !== strpos($name, '\\')) {
618617
// Custom constraint
619618
$fqcn = ltrim($name, '\\');
620-
$name = ltrim(strrchr($name, '\\'), '\\');
621-
$this->file->addUse($fqcn);
619+
$instance = Instance::new("@\\$fqcn");
622620
} else {
623621
// Symfony constraint
624-
$this->file->addUseGroup(static::CONSTRAINTS_NAMESPACE, $name);
625622
$fqcn = static::CONSTRAINTS_NAMESPACE."\\$name";
623+
$this->file->addUse(static::CONSTRAINTS_NAMESPACE.' as SymfonyConstraints');
624+
$instance = Instance::new("@SymfonyConstraints\\$name");
626625
}
627626

628627
if (!class_exists($fqcn)) {
629628
throw new GeneratorException("Constraint class '$fqcn' doesn't exist.");
630629
}
631630

632-
$instance = Instance::new($name);
633-
634631
if (is_array($args)) {
635632
if (isset($args[0]) && is_array($args[0])) {
636633
// Nested instance
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Overblog\GraphQLBundle\Tests\Functional\App\Validator\CustomValidator1;
6+
7+
use Overblog\GraphQLBundle\Tests\Functional\App\Validator\MockValidator;
8+
use Symfony\Component\Validator\Constraint as BaseConstraint;
9+
10+
/**
11+
* This and CustomValidator2/Constraint should be named same,
12+
* to test that generated type class doesn't include them both into use statement,
13+
* which produced a namespace conflict
14+
*
15+
* @Annotation
16+
*/
17+
class Constraint extends BaseConstraint
18+
{
19+
public string $message = 'Mock constraint';
20+
21+
/**
22+
* @return class-string
23+
*/
24+
public function validatedBy(): string
25+
{
26+
return MockValidator::class;
27+
}
28+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Overblog\GraphQLBundle\Tests\Functional\App\Validator\CustomValidator2;
6+
7+
use Overblog\GraphQLBundle\Tests\Functional\App\Validator\MockValidator;
8+
use Symfony\Component\Validator\Constraint as BaseConstraint;
9+
10+
/**
11+
* @Annotation
12+
*/
13+
class Constraint extends BaseConstraint
14+
{
15+
public string $message = 'Mock constraint';
16+
17+
/**
18+
* @return class-string
19+
*/
20+
public function validatedBy(): string
21+
{
22+
return MockValidator::class;
23+
}
24+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Overblog\GraphQLBundle\Tests\Functional\App\Validator;
6+
7+
use Symfony\Component\Validator\Constraint;
8+
use Symfony\Component\Validator\ConstraintValidator;
9+
10+
/**
11+
* Just a dummy validator
12+
*/
13+
class MockValidator extends ConstraintValidator
14+
{
15+
public function validate($value, Constraint $constraint): void
16+
{
17+
}
18+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
imports:
2+
- { resource: ../config.yml }
3+
4+
framework:
5+
annotations: true
6+
validation:
7+
enabled: true
8+
enable_annotations: true
9+
10+
overblog_graphql:
11+
definitions:
12+
config_validation: false
13+
class_namespace: "Overblog\\GraphQLBundle\\Validator\\__DEFINITIONS__"
14+
schema:
15+
query: Mutation
16+
mutation: Mutation
17+
mappings:
18+
types:
19+
- type: yaml
20+
dir: "%kernel.project_dir%/config/conflictingValidatorNamespaces/mapping"
21+
22+
services:
23+
Overblog\GraphQLBundle\Tests\Functional\App\Mutation\InputValidatorMutation:
24+
tags:
25+
- { name: "overblog_graphql.mutation", alias: "mutation_mock", method: "mutationMock" }
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
Mutation:
2+
type: object
3+
config:
4+
fields:
5+
conflictingValidatorNamespaces:
6+
type: Boolean
7+
resolve: '@=m("mutation_mock", args, validator)'
8+
args:
9+
# Covers case where Symfony's Symfony\Component\Validator\Constraints\Type constraint
10+
# conflicts with GraphQL\Type\Definition\Type
11+
# in generated definition file
12+
test:
13+
type: "String"
14+
validation:
15+
- Type:
16+
type: numeric
17+
# Following two args cover the case, where custom constraints with same class name
18+
# but different FQCN conflict with each other
19+
# in generated definition file
20+
test2:
21+
type: "String"
22+
validation:
23+
- Overblog\GraphQLBundle\Tests\Functional\App\Validator\CustomValidator1\Constraint: ~
24+
test3:
25+
type: "String"
26+
validation:
27+
- Overblog\GraphQLBundle\Tests\Functional\App\Validator\CustomValidator2\Constraint: ~

tests/Functional/Generator/TypeGeneratorTest.php

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
namespace Overblog\GraphQLBundle\Tests\Functional\Generator;
66

77
use Overblog\GraphQLBundle\Generator\Exception\GeneratorException;
8+
use Overblog\GraphQLBundle\Tests\Functional\App\Validator;
89
use Overblog\GraphQLBundle\Tests\Functional\TestCase;
10+
use Symfony\Component\Validator\Validation;
911
use function json_decode;
1012

1113
class TypeGeneratorTest extends TestCase
@@ -94,4 +96,58 @@ public function testInjectValidatorWithoutConstraintsThrowsException(): void
9496
parent::setUp();
9597
static::bootKernel(['test_case' => 'validatorWithoutConstraints']);
9698
}
99+
100+
/**
101+
* In generated type definitions:
102+
* 1. Custom constraints should be used by FQCN, to ensure no namespace conflicts occur
103+
* 2. Default Symfony validators should be used with aliased namespace to ensure that no
104+
* namespace conflicts occur with Graphql bundle classes (Type for example).
105+
*
106+
* @runInSeparateProcess
107+
*/
108+
public function testConflictingValidatorNamespaces(): void
109+
{
110+
if (!class_exists(Validation::class)) {
111+
$this->markTestSkipped('Symfony validator component is not installed');
112+
}
113+
114+
parent::setUp();
115+
$kernel = static::bootKernel(['test_case' => 'conflictingValidatorNamespaces']);
116+
117+
// this is the part is why we must make this test run in separate process
118+
$query = <<<'EOF'
119+
mutation {
120+
conflictingValidatorNamespaces(test: "123", test2: "1", test3: "4")
121+
}
122+
EOF;
123+
124+
$response = static::query(
125+
$query,
126+
self::USER_RYAN,
127+
'conflictingValidatorNamespaces'
128+
)->getResponse()->getContent();
129+
130+
$this->assertEquals('{"data":{"conflictingValidatorNamespaces":true}}', $response);
131+
// end part
132+
133+
// Validate definition file
134+
/** @var string $definitionFile */
135+
$definitionFile = file_get_contents($kernel->getCacheDir().'/overblog/graphql-bundle/__definitions__/MutationType.php');
136+
137+
$this->assertStringContainsString(
138+
'use Symfony\Component\Validator\Constraints as SymfonyConstraints;',
139+
$definitionFile,
140+
'Generated definition file should contain import of Symfony\Component\Validator\Constraints aliased as SymfonyConstraints'
141+
);
142+
$this->assertStringNotContainsString(
143+
'use '.Validator\CustomValidator1\Constraint::class,
144+
$definitionFile,
145+
'Generated definition file should not contain imports of custom constraints, FQCN should be used instead'
146+
);
147+
$this->assertStringNotContainsString(
148+
'use '.Validator\CustomValidator2\Constraint::class,
149+
$definitionFile,
150+
'Generated definition file should not contain imports of custom constraints, FQCN should be used instead'
151+
);
152+
}
97153
}

0 commit comments

Comments
 (0)