From aa700c0ba6c3f3c030d51fd0cbfe597f4de20dd7 Mon Sep 17 00:00:00 2001 From: Sebastian Jeltsch Date: Sat, 17 May 2025 16:49:54 +0200 Subject: [PATCH] Update dart client to new filter syntax. --- client/trailbase-dart/lib/src/client.dart | 87 ++++++++++++++++--- .../trailbase-dart/test/trailbase_test.dart | 19 +++- 2 files changed, 92 insertions(+), 14 deletions(-) diff --git a/client/trailbase-dart/lib/src/client.dart b/client/trailbase-dart/lib/src/client.dart index a856d607..44107157 100644 --- a/client/trailbase-dart/lib/src/client.dart +++ b/client/trailbase-dart/lib/src/client.dart @@ -282,6 +282,58 @@ class ErrorEvent extends Event { String toString() => 'ErrorEvent(${_error})'; } +enum CompareOp { + equal, + notEqual, + lessThan, + lessThanEqual, + greaterThan, + greaterThanEqual, + like, + regexp, +} + +String _opToSring(CompareOp op) { + return switch (op) { + CompareOp.equal => '\$eq', + CompareOp.notEqual => '\$ne', + CompareOp.lessThan => '\$lt', + CompareOp.lessThanEqual => '\$lte', + CompareOp.greaterThan => '\$gt', + CompareOp.greaterThanEqual => '\$gte', + CompareOp.like => '\$like', + CompareOp.regexp => '\$re', + }; +} + +sealed class FilterBase { + const FilterBase(); +} + +class Filter extends FilterBase { + final String column; + final CompareOp? op; + final String value; + + const Filter({ + required this.column, + required this.value, + this.op, + }); +} + +class And extends FilterBase { + final List filters; + + const And(this.filters); +} + +class Or extends FilterBase { + final List filters; + + const Or(this.filters); +} + class RecordApi { static const String _recordApi = 'api/records/v1'; @@ -293,7 +345,7 @@ class RecordApi { Future list({ Pagination? pagination, List? order, - List? filters, + List? filters, bool? count, List? expand, }) async { @@ -313,15 +365,30 @@ class RecordApi { if (count ?? false) params['count'] = 'true'; if (expand != null) params['expand'] = expand.join(','); - if (filters != null) { - for (final filter in filters) { - final (nameOp, value) = splitOnce(filter, '='); - if (value == null) { - throw Exception( - 'Filter "${filter}" does not match: "name[op]=value"'); - } - params[nameOp] = value; - } + void traverseFilters(String path, FilterBase filter) { + final _ = switch (filter) { + Filter(column: final c, op: final op, value: final v) => () { + if (op != null) { + params['${path}[${c}][${_opToSring(op)}]'] = v; + } else { + params['${path}[${c}]'] = v; + } + }(), + And(filters: final filters) => () { + filters.asMap().forEach((index, filter) { + traverseFilters('${path}[\$and][${index}]', filter); + }); + }(), + Or(filters: final filters) => () { + filters.asMap().forEach((index, filter) { + traverseFilters('${path}[\$or][${index}]', filter); + }); + }(), + }; + } + + for (final filter in filters ?? []) { + traverseFilters('filter', filter); } final response = await _client.fetch( diff --git a/client/trailbase-dart/test/trailbase_test.dart b/client/trailbase-dart/test/trailbase_test.dart index bc02705a..c123ed13 100644 --- a/client/trailbase-dart/test/trailbase_test.dart +++ b/client/trailbase-dart/test/trailbase_test.dart @@ -272,7 +272,7 @@ Future main() async { { final response = await api.list( - filters: ['text_not_null=${messages[0]}'], + filters: [Filter(column: 'text_not_null', value: messages[0])], ); expect(response.records.length, 1); expect(response.records[0]['text_not_null'], messages[0]); @@ -281,7 +281,12 @@ Future main() async { { final recordsAsc = (await api.list( order: ['+text_not_null'], - filters: ['text_not_null[like]=% =?&${now}'], + filters: [ + Filter( + column: 'text_not_null', + op: CompareOp.like, + value: '% =?&${now}') + ], )) .records; expect(recordsAsc.map((el) => el['text_not_null']), @@ -289,7 +294,10 @@ Future main() async { final recordsDesc = (await api.list( order: ['-text_not_null'], - filters: ['text_not_null[like]=%${now}'], + filters: [ + Filter( + column: 'text_not_null', op: CompareOp.like, value: '%${now}') + ], )) .records; expect(recordsDesc.map((el) => el['text_not_null']).toList().reversed, @@ -300,7 +308,10 @@ Future main() async { final response = (await api.list( pagination: Pagination(limit: 1), order: ['-text_not_null'], - filters: ['text_not_null[like]=%${now}'], + filters: [ + Filter( + column: 'text_not_null', op: CompareOp.like, value: '%${now}') + ], count: true, ));