Skip to content
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

FIX Add test cases for SortPlugin #564

Merged
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
55 changes: 4 additions & 51 deletions src/Schema/DataObject/Plugin/QuerySort.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,15 @@
use SilverStripe\ORM\Sortable;
use Exception;
use GraphQL\Type\Definition\ResolveInfo;
use SilverStripe\GraphQL\Schema\Traits\SortTrait;

/**
* Adds a sort parameter to a DataObject query
*/
class QuerySort extends AbstractQuerySortPlugin
{
use SortTrait;

const IDENTIFIER = 'sort';

public function getIdentifier(): string
Expand Down Expand Up @@ -105,7 +108,7 @@ public static function sort(array $context): closure
return $list;
}

$sortArgs = static::getSortArgs($info, $args, $rootType, $fieldName);
$sortArgs = self::getSortArgs($info, $args, $fieldName);
$paths = NestedInputBuilder::buildPathsFromArgs($sortArgs);
if (empty($paths)) {
return $list;
Expand Down Expand Up @@ -138,56 +141,6 @@ public static function sort(array $context): closure
};
}

private static function getSortArgs(ResolveInfo $info, array $args, string $rootType, string $fieldName): array
{
$sortArgs = [];
$sortOrder = self::getSortOrder($info, $fieldName);

foreach ($sortOrder as $orderName) {
if (!isset($args[$fieldName][$orderName])) {
continue;
}
$sortArgs[$orderName] = $args[$fieldName][$orderName];
unset($args[$fieldName][$orderName]);
}

return array_merge($sortArgs, $args[$fieldName]);
}

/**
* Gets the original order of fields to be sorted based on the query args order.
*
* This is necessary because the underlying GraphQL implementation we're using ignores the
* order of query args, and uses the order that fields are defined in the schema instead.
*/
private static function getSortOrder(ResolveInfo $info, string $fieldName)
{
$relevantNode = $info->fieldDefinition->getName();

// Find the query field node that matches the schema
foreach ($info->fieldNodes as $node) {
if ($node->name->value !== $relevantNode) {
continue;
}

// Find the sort arg
foreach ($node->arguments as $arg) {
if ($arg->name->value !== $fieldName) {
continue;
}

// Get the sort order from the query
$sortOrder = [];
foreach ($arg->value->fields as $field) {
$sortOrder[] = $field->name->value;
}
return $sortOrder;
}
}

return [];
}

/**
* @param NestedInputBuilder $builder
*/
Expand Down
12 changes: 10 additions & 2 deletions src/Schema/Plugin/SortPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,17 @@
use SilverStripe\GraphQL\Schema\Resolver\ResolverReference;
use SilverStripe\GraphQL\Schema\Schema;
use SilverStripe\GraphQL\Schema\Services\NestedInputBuilder;
use SilverStripe\GraphQL\Schema\Traits\SortTrait;
use SilverStripe\GraphQL\Schema\Type\InputType;
use SilverStripe\ORM\Sortable;
use Closure;
use GraphQL\Type\Definition\ResolveInfo;

class SortPlugin implements FieldPlugin, SchemaUpdater
{
use Configurable;
use Injectable;
use SortTrait;

const IDENTIFIER = 'sorter';

Expand Down Expand Up @@ -86,11 +89,16 @@ public function apply(Field $field, Schema $schema, array $config = []): void
public static function sort(array $context): Closure
{
$fieldName = $context['fieldName'];
return function (?Sortable $list, array $args) use ($fieldName) {
return function (?Sortable $list, array $args, array $context, ResolveInfo $info) use ($fieldName) {
if ($list === null) {
return null;
}
GuySartorelli marked this conversation as resolved.
Show resolved Hide resolved
$sortArgs = $args[$fieldName] ?? [];

if (!isset($args[$fieldName])) {
return $list;
}

$sortArgs = self::getSortArgs($info, $args, $fieldName);
$list = $list->sort($sortArgs);

return $list;
Expand Down
58 changes: 58 additions & 0 deletions src/Schema/Traits/SortTrait.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

namespace SilverStripe\GraphQL\Schema\Traits;
GuySartorelli marked this conversation as resolved.
Show resolved Hide resolved

use GraphQL\Type\Definition\ResolveInfo;

trait SortTrait
{
private static function getSortArgs(ResolveInfo $info, array $args, string $fieldName): array
{
$sortArgs = [];
$sortOrder = self::getSortOrder($info, $fieldName);

foreach ($sortOrder as $orderName) {
if (!isset($args[$fieldName][$orderName])) {
continue;
}
$sortArgs[$orderName] = $args[$fieldName][$orderName];
unset($args[$fieldName][$orderName]);
}

return array_merge($sortArgs, $args[$fieldName]);
}

/**
* Gets the original order of fields to be sorted based on the query args order.
*
* This is necessary because the underlying GraphQL implementation we're using ignores the
* order of query args, and uses the order that fields are defined in the schema instead.
*/
private static function getSortOrder(ResolveInfo $info, string $fieldName)
{
$relevantNode = $info->fieldDefinition->getName();

// Find the query field node that matches the schema
foreach ($info->fieldNodes as $node) {
if ($node->name->value !== $relevantNode) {
continue;
}

// Find the sort arg
foreach ($node->arguments as $arg) {
if ($arg->name->value !== $fieldName) {
continue;
}

// Get the sort order from the query
$sortOrder = [];
foreach ($arg->value->fields as $field) {
$sortOrder[] = $field->name->value;
}
return $sortOrder;
}
}

return [];
}
}
133 changes: 127 additions & 6 deletions tests/Schema/IntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -685,14 +685,119 @@ public function provideFilterAndSortOnlyRead(): array
}
}
GRAPHQL,
'expected' => [
["myField" => "test2", "author" => ["firstName" => "tester2"]],
["myField" => "test3", "author" => ["firstName" => "tester2"]],
["myField" => "test1", "author" => ["firstName" => "tester1"]],
],
],
'read with sorter files title DESC' => [
'fixture' => '_SortPlugin',
'query' => <<<GRAPHQL
query {
readDataObjectFakes(sort: { myField: ASC }) {
nodes {
myField
files(sort: { title: DESC }) {
title
}
}
}
}
GRAPHQL,
'expected' => [
["myField" => "test1", "files" => [["title" => "file4"], ["title" => "file3"], ["title" => "file2"], ["title" => "file1"]]],
["myField" => "test2", "files" => []],
["myField" => "test3", "files" => []],
],
],
'read with sorter files ParentID ACS, name DESC' => [
'fixture' => '_SortPlugin',
'query' => <<<GRAPHQL
query {
readDataObjectFakes(sort: { myField: ASC }) {
nodes {
myField
files(sort: { ParentID: ASC, name: DESC }) {
title
}
}
}
}
GRAPHQL,
'expected' => [
["myField" => "test1", "files" => [["title" => "file2"],["title" => "file1"], ["title" => "file4"],["title" => "file3"]]],
["myField" => "test2", "files" => []],
["myField" => "test3", "files" => []],
],
],
'read with sorter files ParentID DESC, name ASC' => [
'fixture' => '_SortPlugin',
'query' => <<<GRAPHQL
query {
readDataObjectFakes(sort: { myField: ASC }) {
nodes {
myField
files(sort: { ParentID: DESC, name: ASC }) {
title
}
}
}
}
GRAPHQL,
'expected' => [
["myField" => "test1", "files" => [["title" => "file3"],["title" => "file4"], ["title" => "file1"],["title" => "file2"]]],
["myField" => "test2", "files" => []],
["myField" => "test3", "files" => []],
],
],
'read with sorter files name ASC, ParentID DESC' => [
'fixture' => '_SortPlugin',
'query' => <<<GRAPHQL
query {
readDataObjectFakes(sort: { myField: ASC }) {
nodes {
myField
files(sort: { name: ASC, ParentID: DESC }) {
title
}
}
}
}
GRAPHQL,
'expected' => [
["myField" => "test1", "files" => [["title" => "file3"],["title" => "file1"], ["title" => "file4"],["title" => "file2"]]],
["myField" => "test2", "files" => []],
["myField" => "test3", "files" => []],
],
],
'read with sorter files name DESC, ParentID ASC' => [
'fixture' => '_SortPlugin',
'query' => <<<GRAPHQL
query {
readDataObjectFakes(sort: { myField: ASC }) {
nodes {
myField
files(sort: { name: DESC, ParentID: ASC }) {
title
}
}
}
}
GRAPHQL,
'expected' => [
["myField" => "test1", "files" => [["title" => "file2"],[ "title" => "file4"],["title" => "file1"],["title" => "file3"]]],
["myField" => "test2", "files" => []],
["myField" => "test3", "files" => []],
],
],
];
}

/**
* @dataProvider provideFilterAndSortOnlyRead
*/
public function testFilterAndSortOnlyRead($fixture, $query)
public function testFilterAndSortOnlyRead(string $fixture, string $query, array $expected)
{
$author = Member::create(['FirstName' => 'tester1']);
$author->write();
Expand All @@ -709,18 +814,34 @@ public function testFilterAndSortOnlyRead($fixture, $query)
$dataObject3 = DataObjectFake::create(['MyField' => 'test3', 'AuthorID' => $author2->ID]);
$dataObject3->write();

$file1 = File::create(['Title' => 'file1', 'Name' => 'asc_name']);
$file1->ParentID = 1;
$file1->write();

$file2 = File::create(['Title' => 'file2', 'Name' => 'desc_name']);
$file2->ParentID = 1;
$file2->write();

$file3 = File::create(['Title' => 'file3', 'Name' => 'asc_name']);
$file3->ParentID = 2;
$file3->write();

$file4 = File::create(['Title' => 'file4', 'Name' => 'desc_name']);
$file4->ParentID = 2;
$file4->write();

$dataObject1->Files()->add($file1);
$dataObject1->Files()->add($file2);
$dataObject1->Files()->add($file3);
$dataObject1->Files()->add($file4);

$factory = new TestSchemaBuilder(['_' . __FUNCTION__ . $fixture]);
$schema = $this->createSchema($factory);

$result = $this->querySchema($schema, $query);
$this->assertSuccess($result);
$records = $result['data']['readDataObjectFakes']['nodes'] ?? [];
$this->assertResults([
["myField" => "test2", "author" => ["firstName" => "tester2"]],
["myField" => "test3", "author" => ["firstName" => "tester2"]],
["myField" => "test1", "author" => ["firstName" => "tester1"]],
], $records);
$this->assertResults($expected, $records);
}

public function testAggregateProperties()
Expand Down
22 changes: 22 additions & 0 deletions tests/Schema/_testFilterAndSortOnlyRead_SortPlugin/models.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
SilverStripe\GraphQL\Tests\Fake\DataObjectFake:
operations:
read:
plugins:
sort:
before: paginateList
fields:
myField: true
AuthorID: true
author:
fields:
firstName: true
files:
fields:
title: true
plugins:
sorter:
fields:
id: true
title: true
name: true
ParentID: true