From b2e9018bd684d93340dd33b85aca96c35fc63910 Mon Sep 17 00:00:00 2001 From: Sabina Talipova Date: Mon, 27 Nov 2023 08:04:02 +1300 Subject: [PATCH] MNT Add test cases for SortPlugin --- src/Schema/Plugin/SortPlugin.php | 55 +++++++- tests/Schema/IntegrationTest.php | 133 +++++++++++++++++- .../models.yml | 25 ++++ 3 files changed, 205 insertions(+), 8 deletions(-) create mode 100644 tests/Schema/_testFilterAndSortOnlyRead_SortPlugin/models.yml diff --git a/src/Schema/Plugin/SortPlugin.php b/src/Schema/Plugin/SortPlugin.php index 98a2fa06..0a30db6e 100644 --- a/src/Schema/Plugin/SortPlugin.php +++ b/src/Schema/Plugin/SortPlugin.php @@ -17,6 +17,7 @@ use SilverStripe\GraphQL\Schema\Type\InputType; use SilverStripe\ORM\Sortable; use Closure; +use GraphQL\Type\Definition\ResolveInfo; class SortPlugin implements FieldPlugin, SchemaUpdater { @@ -86,17 +87,67 @@ 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; } - $sortArgs = $args[$fieldName] ?? []; + $sortArgs = static::getSortArgs($info, $args, $fieldName); $list = $list->sort($sortArgs); return $list; }; } + 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 []; + } + /** * @throws SchemaBuilderException */ diff --git a/tests/Schema/IntegrationTest.php b/tests/Schema/IntegrationTest.php index 2afacf9c..987cfb58 100644 --- a/tests/Schema/IntegrationTest.php +++ b/tests/Schema/IntegrationTest.php @@ -685,6 +685,111 @@ 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' => << [ + ["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' => << [ + ["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' => << [ + ["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' => << [ + ["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' => << [ + ["myField" => "test1", "files" => [["title" => "file2"],[ "title" => "file4"],["title" => "file1"],["title" => "file3"]]], + ["myField" => "test2", "files" => []], + ["myField" => "test3", "files" => []], + ], ], ]; } @@ -692,7 +797,7 @@ public function provideFilterAndSortOnlyRead(): array /** * @dataProvider provideFilterAndSortOnlyRead */ - public function testFilterAndSortOnlyRead($fixture, $query) + public function testFilterAndSortOnlyRead(string $fixture, string $query, array $expected) { $author = Member::create(['FirstName' => 'tester1']); $author->write(); @@ -709,6 +814,26 @@ 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); @@ -716,11 +841,7 @@ public function testFilterAndSortOnlyRead($fixture, $query) $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() diff --git a/tests/Schema/_testFilterAndSortOnlyRead_SortPlugin/models.yml b/tests/Schema/_testFilterAndSortOnlyRead_SortPlugin/models.yml new file mode 100644 index 00000000..4c5a7a88 --- /dev/null +++ b/tests/Schema/_testFilterAndSortOnlyRead_SortPlugin/models.yml @@ -0,0 +1,25 @@ +SilverStripe\GraphQL\Tests\Fake\DataObjectFake: + operations: + read: + plugins: + sort: + before: paginateList + fields: + myField: true + AuthorID: true + author: + fields: + firstName: true + files: + fields: + id: true + name: true + title: true + ParentID: true + plugins: + sorter: + fields: + id: true + title: true + name: true + ParentID: true