From baa5fdad954706e001ba9e50cec41f12e4734d52 Mon Sep 17 00:00:00 2001 From: Alexander Skvortsov Date: Fri, 13 Nov 2020 02:56:13 -0500 Subject: [PATCH] Add extender and tests for filter --- src/Extend/Filter.php | 65 ++++++++++ src/Filter/Filterer.php | 20 +-- tests/integration/extenders/FilterTest.php | 138 +++++++++++++++++++++ 3 files changed, 215 insertions(+), 8 deletions(-) create mode 100644 src/Extend/Filter.php create mode 100644 tests/integration/extenders/FilterTest.php diff --git a/src/Extend/Filter.php b/src/Extend/Filter.php new file mode 100644 index 000000000..65c3156ab --- /dev/null +++ b/src/Extend/Filter.php @@ -0,0 +1,65 @@ +resource = $resource; + } + + /** + * Add a filter to run when the resource is filtered + * + * @param string $filterClass: The ::class attribute of the filter you are adding. + */ + public function addFilter(string $filterClass) + { + $this->filters[] = $filterClass; + + return $this; + } + + /** + * Add a callback through which to run all filter queries after filters have been applied. + */ + public function addFilterMutator($callback) + { + $this->filterMutators[] = $callback; + + return $this; + } + + public function extend(Container $container, Extension $extension = null) + { + + foreach ($this->filters as $filter) { + Filterer::addFilter($this->resource, $container->make($filter)); + } + + foreach ($this->filterMutators as $mutator) { + Filterer::addFilterMutator($this->resource, ContainerUtil::wrapCallback($mutator, $container)); + } + } +} diff --git a/src/Filter/Filterer.php b/src/Filter/Filterer.php index cd844fcde..0d77efca3 100644 --- a/src/Filter/Filterer.php +++ b/src/Filter/Filterer.php @@ -21,13 +21,17 @@ class Filterer protected static $filterMutators = []; - public static function addFilter($resource, $filterKey, $filter) + public static function addFilter($resource, FilterInterface $filter) { if (!array_key_exists($resource, static::$filters)) { static::$filters[$resource] = []; } - static::$filters[$resource][$filterKey] = $filter; + if (!array_key_exists($filter->getKey(), static::$filters[$resource])) { + static::$filters[$resource][$filter->getKey()] = []; + } + + static::$filters[$resource][$filter->getKey()][] = $filter; } public static function addFilterMutator($resource, $mutator) @@ -52,20 +56,20 @@ class Filterer $query->whereVisibleTo($actor); - foreach (Arr::get(static::$filters, $resource, []) as $filterKey => $filterCallback) { - if (array_key_exists($filterKey, $filters)) { - $filterCallback($query, $filters[$filterKey]); + $wrappedFilter = new WrappedFilter($query->getQuery(), $actor); + + foreach ($filters as $filterKey => $filterValue) { + foreach (Arr::get(static::$filters, "$resource.$filterKey", []) as $filter) { + $filter->apply($wrappedFilter, $filterValue); } } - $wrappedFilter = new WrappedFilter($query->getQuery(), $actor); - $this->applySort($wrappedFilter, $sort); $this->applyOffset($wrappedFilter, $offset); $this->applyLimit($wrappedFilter, $limit + 1); foreach (Arr::get(static::$filterMutators, $resource, []) as $mutator) { - $mutator($wrappedFilter, $filters, $sort); + $mutator($query, $actor, $filters, $sort); } // Execute the filter query and retrieve the results. We get one more diff --git a/tests/integration/extenders/FilterTest.php b/tests/integration/extenders/FilterTest.php new file mode 100644 index 000000000..5ac67ba7d --- /dev/null +++ b/tests/integration/extenders/FilterTest.php @@ -0,0 +1,138 @@ +prepareDatabase([ + 'discussions' => [ + ['id' => 1, 'title' => 'DISCUSSION 1', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'first_post_id' => 1, 'comment_count' => 1], + ['id' => 2, 'title' => 'DISCUSSION 2', 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'first_post_id' => 2, 'comment_count' => 1], + ], + 'posts' => [ + ['id' => 1, 'discussion_id' => 1, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'type' => 'comment', 'content' => '

foo bar

'], + ['id' => 2, 'discussion_id' => 2, 'created_at' => Carbon::now()->toDateTimeString(), 'user_id' => 2, 'type' => 'comment', 'content' => '

foo bar not the same

'], + ], + 'users' => [ + $this->adminUser(), + $this->normalUser(), + ], + ]); + } + + public function filterDiscussions($filters, $limit = null) + { + $response = $this->send( + $this->request('GET', '/api/discussions', [ + 'authenticatedAs' => 1, + ])->withQueryParams([ + 'filter' => $filters, + 'include' => 'mostRelevantPost', + ]) + ); + + return json_decode($response->getBody()->getContents(), true)['data']; + } + + /** + * @test + */ + public function works_as_expected_with_no_modifications() + { + $this->prepDb(); + + $searchForAll = json_encode($this->filterDiscussions([], 5)); + $this->assertContains('DISCUSSION 1', $searchForAll); + $this->assertContains('DISCUSSION 2', $searchForAll); + } + + /** + * @test + */ + public function custom_filter_gambit_has_effect_if_added() + { + $this->extend((new Extend\Filter(Discussion::class))->addFilter(NoResultFilter::class)); + + $this->prepDb(); + + $withResultSearch = json_encode($this->filterDiscussions(['noResult' => 0], 5)); + $this->assertContains('DISCUSSION 1', $withResultSearch); + $this->assertContains('DISCUSSION 2', $withResultSearch); + $this->assertEquals([], $this->filterDiscussions(['noResult' => 1], 5)); + } + + /** + * @test + */ + public function filter_mutator_has_effect_if_added() + { + $this->extend((new Extend\Filter(Discussion::class))->addFilterMutator(function ($query, $actor, $filters, $sort) { + $query->getQuery()->whereRaw('1=0'); + })); + + $this->prepDb(); + + $this->assertEquals([], $this->filterDiscussions([], 5)); + } + + /** + * @test + */ + public function filter_mutator_has_effect_if_added_with_invokable_class() + { + $this->extend((new Extend\Filter(Discussion::class))->addFilterMutator(CustomFilterMutator::class)); + + $this->prepDb(); + + $this->assertEquals([], $this->filterDiscussions([], 5)); + } +} + +class NoResultFilter implements FilterInterface +{ + public function getKey(): string + { + return 'noResult'; + } + + /** + * {@inheritdoc} + */ + public function apply(WrappedFilter $wrappedFilter, $filterValue) + { + if ($filterValue) { + $wrappedFilter->getQuery() + ->whereRaw('0=1'); + } + } +} + +class CustomFilterMutator +{ + public function __invoke($query, $actor, $filters, $sort) + { + $query->getQuery()->whereRaw('1=0'); + } +}