From 68e1ad733330c5ec855d282dc86fb64785f00c68 Mon Sep 17 00:00:00 2001 From: Kevin Martin Date: Tue, 26 May 2020 17:53:50 -0500 Subject: [PATCH 1/2] Add collection filtering support --- lib/query.dart | 1 + lib/src/query/filters.dart | 30 ++++++++++++++++++++++++++++++ test/unit/query/filters_test.dart | 23 +++++++++++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 lib/src/query/filters.dart create mode 100644 test/unit/query/filters_test.dart diff --git a/lib/query.dart b/lib/query.dart index 5b2f4c8..1c59baa 100644 --- a/lib/query.dart +++ b/lib/query.dart @@ -1,6 +1,7 @@ library query; export 'package:json_api/src/query/fields.dart'; +export 'package:json_api/src/query/filters.dart'; export 'package:json_api/src/query/include.dart'; export 'package:json_api/src/query/page.dart'; export 'package:json_api/src/query/query_parameters.dart'; diff --git a/lib/src/query/filters.dart b/lib/src/query/filters.dart new file mode 100644 index 0000000..bd54dcb --- /dev/null +++ b/lib/src/query/filters.dart @@ -0,0 +1,30 @@ +import 'package:json_api/src/query/query_parameters.dart'; + +/// Query parameters defining Filters +/// @see https://jsonapi.org/recommendations/#filtering +class Filters extends QueryParameters { + /// The [filter] argument maps the resource type to a list of filters. + /// + /// Example: + /// ```dart + /// Filters({'articles': ['title', 'body'], 'people': ['name']}).addTo(url); + /// ``` + /// encodes to + /// ``` + /// ?filter[articles]=title,body&filter[people]=name + /// ``` + Filters(Map> filter) + : _filter = {...filter}, + super(filter.map((k, v) => MapEntry('filter[$k]', v.join(',')))); + + /// Extracts the requested filter from the [uri]. + static Filters fromUri(Uri uri) => Filters(uri.queryParameters + .map((k, v) => MapEntry(_regex.firstMatch(k)?.group(1), v.split(','))) + ..removeWhere((k, v) => k == null)); + + List operator [](String key) => _filter[key]; + + static final _regex = RegExp(r'^filter\[(.+)\]$'); + + final Map> _filter; +} \ No newline at end of file diff --git a/test/unit/query/filters_test.dart b/test/unit/query/filters_test.dart new file mode 100644 index 0000000..18cab09 --- /dev/null +++ b/test/unit/query/filters_test.dart @@ -0,0 +1,23 @@ +import 'package:json_api/src/query/filters.dart'; +import 'package:test/test.dart'; + +void main() { + test('Can decode url', () { + final uri = Uri.parse( + '/articles?include=author&filter%5Barticles%5D=title%2Cbody&filter%5Bpeople%5D=name'); + final filter = Filters.fromUri(uri); + expect(filter['articles'], ['title', 'body']); + expect(filter['people'], ['name']); + }); + + test('Can add to uri', () { + final filter = Filters({ + 'articles': ['title', 'body'], + 'people': ['name'] + }); + final uri = Uri.parse('/articles'); + + expect(filter.addToUri(uri).toString(), + '/articles?filter%5Barticles%5D=title%2Cbody&filter%5Bpeople%5D=name'); + }); +} \ No newline at end of file From dc365933046605031472714bae743d90929bbd9e Mon Sep 17 00:00:00 2001 From: Kevin Martin Date: Mon, 1 Jun 2020 13:06:01 -0500 Subject: [PATCH 2/2] Rename Filters to Filter for consistency --- lib/query.dart | 2 +- lib/src/query/{filters.dart => filter.dart} | 12 ++++++------ .../query/{filters_test.dart => filter_test.dart} | 8 ++++---- 3 files changed, 11 insertions(+), 11 deletions(-) rename lib/src/query/{filters.dart => filter.dart} (74%) rename test/unit/query/{filters_test.dart => filter_test.dart} (82%) diff --git a/lib/query.dart b/lib/query.dart index 1c59baa..9fa7b08 100644 --- a/lib/query.dart +++ b/lib/query.dart @@ -1,7 +1,7 @@ library query; export 'package:json_api/src/query/fields.dart'; -export 'package:json_api/src/query/filters.dart'; +export 'package:json_api/src/query/filter.dart'; export 'package:json_api/src/query/include.dart'; export 'package:json_api/src/query/page.dart'; export 'package:json_api/src/query/query_parameters.dart'; diff --git a/lib/src/query/filters.dart b/lib/src/query/filter.dart similarity index 74% rename from lib/src/query/filters.dart rename to lib/src/query/filter.dart index bd54dcb..105a6fe 100644 --- a/lib/src/query/filters.dart +++ b/lib/src/query/filter.dart @@ -1,24 +1,24 @@ import 'package:json_api/src/query/query_parameters.dart'; -/// Query parameters defining Filters +/// Query parameters defining Filter /// @see https://jsonapi.org/recommendations/#filtering -class Filters extends QueryParameters { +class Filter extends QueryParameters { /// The [filter] argument maps the resource type to a list of filters. /// /// Example: /// ```dart - /// Filters({'articles': ['title', 'body'], 'people': ['name']}).addTo(url); + /// Filter({'articles': ['title', 'body'], 'people': ['name']}).addTo(url); /// ``` /// encodes to /// ``` /// ?filter[articles]=title,body&filter[people]=name /// ``` - Filters(Map> filter) + Filter(Map> filter) : _filter = {...filter}, super(filter.map((k, v) => MapEntry('filter[$k]', v.join(',')))); /// Extracts the requested filter from the [uri]. - static Filters fromUri(Uri uri) => Filters(uri.queryParameters + static Filter fromUri(Uri uri) => Filter(uri.queryParameters .map((k, v) => MapEntry(_regex.firstMatch(k)?.group(1), v.split(','))) ..removeWhere((k, v) => k == null)); @@ -27,4 +27,4 @@ class Filters extends QueryParameters { static final _regex = RegExp(r'^filter\[(.+)\]$'); final Map> _filter; -} \ No newline at end of file +} diff --git a/test/unit/query/filters_test.dart b/test/unit/query/filter_test.dart similarity index 82% rename from test/unit/query/filters_test.dart rename to test/unit/query/filter_test.dart index 18cab09..3661dd0 100644 --- a/test/unit/query/filters_test.dart +++ b/test/unit/query/filter_test.dart @@ -1,17 +1,17 @@ -import 'package:json_api/src/query/filters.dart'; +import 'package:json_api/src/query/filter.dart'; import 'package:test/test.dart'; void main() { test('Can decode url', () { final uri = Uri.parse( '/articles?include=author&filter%5Barticles%5D=title%2Cbody&filter%5Bpeople%5D=name'); - final filter = Filters.fromUri(uri); + final filter = Filter.fromUri(uri); expect(filter['articles'], ['title', 'body']); expect(filter['people'], ['name']); }); test('Can add to uri', () { - final filter = Filters({ + final filter = Filter({ 'articles': ['title', 'body'], 'people': ['name'] }); @@ -20,4 +20,4 @@ void main() { expect(filter.addToUri(uri).toString(), '/articles?filter%5Barticles%5D=title%2Cbody&filter%5Bpeople%5D=name'); }); -} \ No newline at end of file +}