Skip to content

Commit

Permalink
Merge pull request #128 from TomHAnderson/feature/include-criteria
Browse files Browse the repository at this point in the history
Created ExcludeCriteria trait and added includeCriteria to all attributes
  • Loading branch information
TomHAnderson authored Feb 4, 2023
2 parents 07a001a + 201e5ea commit 488b222
Show file tree
Hide file tree
Showing 9 changed files with 257 additions and 21 deletions.
14 changes: 7 additions & 7 deletions src/Attribute/Association.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,18 @@
#[Attribute(Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)]
class Association
{
/** @param string[] $excludeCriteria */
use ExcludeCriteria;

/**
* @param string[] $excludeCriteria
* @param string[] $includeCriteria
*/
public function __construct(
protected string $group = 'default',
protected string|null $strategy = null,
protected string|null $description = null,
protected array $excludeCriteria = [],
protected array $includeCriteria = [],
protected string|null $filterCriteriaEventName = null,
) {
}
Expand All @@ -34,12 +40,6 @@ public function getDescription(): string|null
return $this->description;
}

/** @return string[] */
public function getExcludeCriteria(): array
{
return $this->excludeCriteria;
}

public function getFilterCriteriaEventName(): string|null
{
return $this->filterCriteriaEventName;
Expand Down
10 changes: 4 additions & 6 deletions src/Attribute/Entity.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
#[Attribute(Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)]
final class Entity
{
use ExcludeCriteria;

/** @var string The GraphQL group */
private string $group;

Expand All @@ -30,6 +32,7 @@ final class Entity
/**
* @param mixed[] $filters
* @param string[] $excludeCriteria
* @param string[] $includeCriteria
*/
public function __construct(
string $group = 'default',
Expand All @@ -39,6 +42,7 @@ public function __construct(
array $filters = [],
private string|null $namingStrategy = null,
private array $excludeCriteria = [],
private array $includeCriteria = [],
) {
$this->group = $group;
$this->byValue = $byValue;
Expand Down Expand Up @@ -76,10 +80,4 @@ public function getNamingStrategy(): string|null
{
return $this->namingStrategy;
}

/** @return string[] */
public function getExcludeCriteria(): array
{
return $this->excludeCriteria;
}
}
30 changes: 30 additions & 0 deletions src/Attribute/ExcludeCriteria.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

declare(strict_types=1);

namespace ApiSkeletons\Doctrine\GraphQL\Attribute;

use ApiSkeletons\Doctrine\GraphQL\Criteria\Filters;
use Exception;

use function array_diff;
use function array_intersect;

trait ExcludeCriteria
{
/** @return string[] */
public function getExcludeCriteria(): array
{
if ($this->includeCriteria && $this->excludeCriteria) {
throw new Exception('includeCriteria and excludeCriteria are mutually exclusive.');
}

if ($this->includeCriteria) {
$this->excludeCriteria = array_diff(Filters::toArray(), $this->includeCriteria);
} elseif ($this->excludeCriteria) {
$this->excludeCriteria = array_intersect(Filters::toArray(), $this->excludeCriteria);
}

return $this->excludeCriteria;
}
}
14 changes: 7 additions & 7 deletions src/Attribute/Field.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,19 @@
#[Attribute(Attribute::TARGET_PROPERTY | Attribute::IS_REPEATABLE)]
class Field
{
/** @param string[] $excludeCriteria */
use ExcludeCriteria;

/**
* @param string[] $excludeCriteria
* @param string[] $includeCriteria
*/
public function __construct(
protected string $group = 'default',
protected string|null $strategy = null,
protected string|null $description = null,
protected string|null $type = null,
private array $excludeCriteria = [],
private array $includeCriteria = [],
) {
}

Expand All @@ -38,10 +44,4 @@ public function getType(): string|null
{
return $this->type;
}

/** @return string[] */
public function getExcludeCriteria(): array
{
return $this->excludeCriteria;
}
}
4 changes: 3 additions & 1 deletion test/Entity/Artist.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace ApiSkeletonsTest\Doctrine\GraphQL\Entity;

use ApiSkeletons\Doctrine\GraphQL\Attribute as GraphQL;
use ApiSkeletons\Doctrine\GraphQL\Criteria\Filters;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
Expand Down Expand Up @@ -45,7 +46,8 @@ class Artist

/** @var Collection<id, Performance> */
#[GraphQL\Association(description: 'Performances')]
#[GraphQL\Association(group: 'ExcludeCriteriaTest', excludeCriteria: ['neq'])]
#[GraphQL\Association(group: 'ExcludeCriteriaTest', excludeCriteria: [Filters::NEQ])]
#[GraphQL\Association(group: 'IncludeCriteriaTest', includeCriteria: [Filters::EQ])]
#[GraphQL\Association(group: 'DuplicateGroup')]
#[GraphQL\Association(group: 'DuplicateGroup')]
#[GraphQL\Association(group: 'DuplicateGroupAssociation')]
Expand Down
25 changes: 25 additions & 0 deletions test/Entity/Performance.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace ApiSkeletonsTest\Doctrine\GraphQL\Entity;

use ApiSkeletons\Doctrine\GraphQL\Attribute as GraphQL;
use ApiSkeletons\Doctrine\GraphQL\Criteria\Filters;
use DateTime;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
Expand All @@ -15,23 +16,45 @@
*/
#[GraphQL\Entity(typeName: 'performance', description: 'Performances')]
#[GraphQL\Entity(group: 'ExcludeCriteriaTest', excludeCriteria: ['contains'])]
#[GraphQL\Entity(group: 'IncludeCriteriaTest', includeCriteria: [
Filters::EQ,
Filters::NEQ,
Filters::CONTAINS,
])]
#[GraphQL\Entity(
group: 'IncludeExcludeCriteriaTest',
excludeCriteria: [Filters::IN],
includeCriteria: [
Filters::EQ,
Filters::NEQ,
Filters::CONTAINS,
],
)]
#[GraphQL\Entity(group: 'FilterCriteriaEvent')]
#[ORM\Entity]
class Performance
{
#[GraphQL\Field(description: 'Venue name')]
#[GraphQL\Field(description: 'Venue name', group: 'ExcludeCriteriaTest')]
#[GraphQL\Field(group: 'IncludeCriteriaTest')]
#[GraphQL\Field(group: 'FilterCriteriaEvent')]
#[ORM\Column(type: 'string', nullable: true)]
private string|null $venue = null;

#[GraphQL\Field(description: 'City name')]
#[GraphQL\Field(group: 'FilterCriteriaEvent')]
#[GraphQL\Field(group: 'IncludeCriteriaTest', includeCriteria: [
Filters::EQ,
Filters::NEQ,
])]
#[ORM\Column(type: 'string', nullable: true)]
private string|null $city = null;

#[GraphQL\Field(description: 'State name')]
#[GraphQL\Field(group: 'FilterCriteriaEvent')]
#[GraphQL\Field(group: 'IncludeCriteriaTest', excludeCriteria: [
Filters::EQ,
])]
#[ORM\Column(type: 'string', nullable: true)]
private string|null $state = null;

Expand All @@ -41,13 +64,15 @@ class Performance

#[GraphQL\Field(description: 'Primary key')]
#[GraphQL\Field(group: 'ExcludeCriteriaTest')]
#[GraphQL\Field(group: 'IncludeCriteriaTest')]
#[ORM\Id]
#[ORM\Column(type: 'integer')]
#[ORM\GeneratedValue(strategy: 'AUTO')]
private int $id;

/** @var Collection<id, Recording> */
#[GraphQL\Association(description: 'Recordings by artist')]
#[GraphQL\Association(group: 'IncludeCriteriaTest', includeCriteria: [Filters::CONTAINS])]
#[ORM\OneToMany(targetEntity: 'ApiSkeletonsTest\Doctrine\GraphQL\Entity\Recording', mappedBy: 'performance')]
private Collection $recordings;

Expand Down
2 changes: 2 additions & 0 deletions test/Entity/Recording.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@
*/
#[GraphQL\Entity(typeName: 'recording', description: 'Performance recordings')]
#[GraphQL\Entity(typeName: 'entitytestrecording', description: 'Entity Test Recordings', group: 'entityTest')]
#[GraphQL\Entity(group: 'IncludeCriteriaTest')]
#[GraphQL\Entity(group: 'CustomFieldStrategyTest')]
#[ORM\Entity]
class Recording
{
#[GraphQL\Field(description: 'Source')]
#[GraphQL\Field(description: 'Entity Test Source', group: 'entityTest')]
#[GraphQL\Field(group: 'CustomFieldStrategyTest')]
#[GraphQL\Field(group: 'IncludeCriteriaTest')]
#[ORM\Column(type: 'text', nullable: false)]
private string $source;

Expand Down
137 changes: 137 additions & 0 deletions test/Feature/Criteria/IncludeCriteriaTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
<?php

declare(strict_types=1);

namespace ApiSkeletonsTest\Doctrine\GraphQL\Feature\Criteria;

use ApiSkeletons\Doctrine\GraphQL\Config;
use ApiSkeletons\Doctrine\GraphQL\Driver;
use ApiSkeletonsTest\Doctrine\GraphQL\AbstractTest;
use ApiSkeletonsTest\Doctrine\GraphQL\Entity\Performance;
use GraphQL\GraphQL;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Schema;

class IncludeCriteriaTest extends AbstractTest
{
public function testExcludeCriteria(): void
{
$config = new Config(['group' => 'IncludeCriteriaTest']);

$driver = new Driver($this->getEntityManager(), $config);

$schema = new Schema([
'query' => new ObjectType([
'name' => 'query',
'fields' => [
'performances' => [
'type' => $driver->connection($driver->type(Performance::class)),
'args' => [
'filter' => $driver->filter(Performance::class),
'pagination' => $driver->pagination(),
],
'resolve' => $driver->resolve(Performance::class),
],
],
]),
]);

// Test entity level included filters
$query = '{ performances (filter: { venue: {contains: "Fillmore" } } ) { edges { node { venue } } } }';
$result = GraphQL::executeQuery($schema, $query);
$data = $result->toArray()['data'];
$this->assertEquals('Fillmore Auditorium', $data['performances']['edges'][0]['node']['venue']);

$query = '{ performances (filter: { venue: {eq: "Fillmore Auditorium" } } ) { edges { node { venue } } } }';
$result = GraphQL::executeQuery($schema, $query);
$data = $result->toArray()['data'];
$this->assertEquals('Fillmore Auditorium', $data['performances']['edges'][0]['node']['venue']);

// Test entity level excluded filters
$query = '{ performances (filter: { venue: {in: ["Fillmore Auditorium"] } } ) { edges { node { venue } } } }';
$result = GraphQL::executeQuery($schema, $query);
foreach ($result->errors as $error) {
$this->assertEquals('Field "in" is not defined by type "ApiSkeletonsTest_Doctrine_GraphQL_Entity_Performance_IncludeCriteriaTest_filter_venue_filters".', $error->getMessage());
}

// Test entity>field level included filters
$query = '{ performances (filter: { city: {eq: "Salt Lake City" } } ) { edges { node { city } } } }';
$result = GraphQL::executeQuery($schema, $query);
$data = $result->toArray()['data'];
$this->assertEquals('Salt Lake City', $data['performances']['edges'][0]['node']['city']);

// Test entity>field level excluded filters
$query = '{ performances (filter: { city: {contains: "Salt Lake City" } } ) { edges { node { city } } } }';
$result = GraphQL::executeQuery($schema, $query);
foreach ($result->errors as $error) {
$this->assertEquals('Field "contains" is not defined by type "ApiSkeletonsTest_Doctrine_GraphQL_Entity_Performance_IncludeCriteriaTest_filter_city_filters".', $error->getMessage());
}

// Test entity>field level included filters excluded by field level exclude
$query = '{ performances (filter: { state: { eq: "UT" } } ) { edges { node { state } } } }';
$result = GraphQL::executeQuery($schema, $query);
foreach ($result->errors as $error) {
$this->assertEquals('Field "eq" is not defined by type "ApiSkeletonsTest_Doctrine_GraphQL_Entity_Performance_IncludeCriteriaTest_filter_state_filters". Did you mean "neq"?', $error->getMessage());
}

// Test entity>association level included filters
$query = '{
performances (
filter: {
venue: { eq: "Delta Center" }
}
) {
edges {
node {
recordings(
filter: {
source: { contains: "DSBD" }
}
) {
edges {
node {
source
}
}
}
}
}
}
}';
$result = GraphQL::executeQuery($schema, $query);
$data = $result->toArray()['data'];
$this->assertEquals(
'DSBD > 1C > DAT; Seeded to etree by Dan Stephens',
$data['performances']['edges'][0]['node']['recordings']['edges'][0]['node']['source'],
);

// Test entity>association level included filters
$query = '{
performances (
filter: {
venue: { eq: "Delta Center" }
}
) {
edges {
node {
recordings(
filter: {
source: { eq: "DSBD" }
}
) {
edges {
node {
source
}
}
}
}
}
}
}';
$result = GraphQL::executeQuery($schema, $query);
foreach ($result->errors as $error) {
$this->assertEquals('Field "eq" is not defined by type "ApiSkeletonsTest_Doctrine_GraphQL_Entity_Performance_IncludeCriteriaTest_recordings_filter_source_filters".', $error->getMessage());
}
}
}
Loading

0 comments on commit 488b222

Please sign in to comment.