Skip to content

Commit 81c7dcb

Browse files
committed
refactor(llc): Type-safe filter fields in queries
This commit introduces type-safe filter fields across various query types by parameterizing the `Filter` class with specific `FilterField` extension types. Key changes: - **Parameterized `Filter`:** The `Filter` class is now generic `Filter<T extends FilterField>`, ensuring that filters are constructed with valid fields for the specific query type. - **`FilterField` interface:** A new `FilterField` extension type acts as a base for all specific filter field types (e.g., `ActivitiesFilterField`, `FeedsFilterField`). - **Updated Query Classes:** All query classes (e.g., `ActivitiesQuery`, `FeedsQuery`, `CommentsQuery`, etc.) now use the parameterized `Filter` with their respective `FilterField` types. This provides compile-time safety for filter field names. - **Filter Field Extension Types:** Existing string-based filter field constants are now extension types implementing `FilterField`. This maintains compatibility while enabling type safety. - **`FilterRequestExtension`:** The `toRequest()` method in `FilterRequestExtension` now returns `Map<String, Object?>` to reflect that filter values can be null. - **Code Snippet Updates:** Documentation code snippets have been updated to reflect the new type-safe filter field usage. - **Dependency Update:** `stream_core` dependency has been updated to `280b104`. This refactoring enhances code maintainability and reduces the likelihood of runtime errors caused by incorrect filter field names.
1 parent 7ea10bc commit 81c7dcb

34 files changed

+115
-113
lines changed

docs_code_snippets/03_02_querying_activities.dart

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// ignore_for_file: unused_local_variable, file_names
1+
// ignore_for_file: unused_local_variable, file_names, avoid_redundant_argument_values
22

33
import 'package:stream_feeds/stream_feeds.dart';
44

@@ -7,7 +7,7 @@ late Feed feed;
77

88
Future<void> activitySearchAndQueries() async {
99
final query = ActivitiesQuery(
10-
filter: Filter.equal(ActivitiesFilterField.type, 'post'),
10+
filter: const Filter.equal(ActivitiesFilterField.type, 'post'),
1111
sort: [ActivitiesSort.desc(ActivitiesSortField.createdAt)],
1212
limit: 10,
1313
);
@@ -18,7 +18,7 @@ Future<void> activitySearchAndQueries() async {
1818

1919
Future<void> queryActivitiesByText() async {
2020
// search for activities where the text includes the word 'popularity'.
21-
final query = ActivitiesQuery(
21+
const query = ActivitiesQuery(
2222
filter: Filter.equal(ActivitiesFilterField.text, 'popularity'),
2323
);
2424

@@ -28,7 +28,10 @@ Future<void> queryActivitiesByText() async {
2828

2929
Future<void> queryActivitiesBySearchData() async {
3030
// search for activities associated with the campaign ID 'spring-sale-2025'
31-
final searchValue = {'campaign': {'id': 'spring-sale-2025'}};
31+
final searchValue = {
32+
'campaign': {'id': 'spring-sale-2025'},
33+
};
34+
3235
final query = ActivitiesQuery(
3336
filter: Filter.contains(ActivitiesFilterField.searchData, searchValue),
3437
);
@@ -37,8 +40,11 @@ Future<void> queryActivitiesBySearchData() async {
3740
final activities = await activityList.get();
3841

3942
// search for activities where the campaign took place in a mall
40-
final query2 = ActivitiesQuery(
41-
filter: Filter.pathExists(ActivitiesFilterField.searchData, 'campaign.location.mall'),
43+
const query2 = ActivitiesQuery(
44+
filter: Filter.pathExists(
45+
ActivitiesFilterField.searchData,
46+
'campaign.location.mall',
47+
),
4248
);
4349

4450
final activityList2 = client.activityList(query2);

docs_code_snippets/04_01_feeds.dart

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,12 @@ Future<void> readingAFeed() async {
3636
}
3737

3838
Future<void> readingAFeedMoreOptions() async {
39-
final query = FeedQuery(
40-
fid: const FeedId(group: 'user', id: 'john'),
39+
const query = FeedQuery(
40+
fid: FeedId(group: 'user', id: 'john'),
4141
// filter activities with filter tag green
4242
activityFilter: Filter.in_(
4343
ActivitiesFilterField.filterTags,
44-
const ['green'],
44+
['green'],
4545
),
4646
activityLimit: 10,
4747
// additional data used for ranking
@@ -108,11 +108,9 @@ Future<void> filteringExamples() async {
108108
],
109109
);
110110
// Now read the feed, this will fetch activity 1 and 2
111-
final query = FeedQuery(
111+
const query = FeedQuery(
112112
fid: feedId,
113-
activityFilter: Filter.in_(ActivitiesFilterField.filterTags, const [
114-
'blue',
115-
]),
113+
activityFilter: Filter.in_(ActivitiesFilterField.filterTags, ['blue']),
116114
);
117115

118116
final feed = client.feedFromQuery(query);
@@ -123,20 +121,21 @@ Future<void> filteringExamples() async {
123121

124122
Future<void> moreComplexFilterExamples() async {
125123
// Get all the activities where tags contain "green" and type is "post" or tag contains "orange" and type is "activity"
126-
final query = FeedQuery(
127-
fid: const FeedId(group: 'user', id: 'john'),
124+
const query = FeedQuery(
125+
fid: FeedId(group: 'user', id: 'john'),
128126
activityFilter: Filter.or([
129127
Filter.and([
130-
Filter.in_(ActivitiesFilterField.filterTags, const ['green']),
128+
Filter.in_(ActivitiesFilterField.filterTags, ['green']),
131129
Filter.equal(ActivitiesFilterField.type, 'post'),
132130
]),
133131
Filter.and([
134-
Filter.in_(ActivitiesFilterField.filterTags, const ['orange']),
132+
Filter.in_(ActivitiesFilterField.filterTags, ['orange']),
135133
Filter.equal(ActivitiesFilterField.type, 'activity'),
136134
]),
137135
]),
138136
);
139137

138+
final feed = client.feedFromQuery(query);
140139
await feed.getOrCreate();
141140
final activities = feed.state.activities;
142141
}
@@ -199,7 +198,7 @@ Future<void> memberInvites() async {
199198

200199
Future<void> queryMyFeeds() async {
201200
final query = FeedsQuery(
202-
filter: Filter.equal(FeedsFilterField.createdById, 'john'),
201+
filter: const Filter.equal(FeedsFilterField.createdById, 'john'),
203202
sort: [FeedsSort.desc(FeedsSortField.createdAt)],
204203
limit: 10,
205204
watch: true,
@@ -217,15 +216,15 @@ Future<void> queryMyFeeds() async {
217216
}
218217

219218
Future<void> queryFeedsWhereIAmAMember() async {
220-
final query = FeedsQuery(
219+
const query = FeedsQuery(
221220
filter: Filter.contains(FeedsFilterField.members, 'john'),
222221
);
223222
final feedList = client.feedList(query);
224223
final feeds = await feedList.get();
225224
}
226225

227226
Future<void> queryFeedsByNameOrVisibility() async {
228-
final sportsQuery = FeedsQuery(
227+
const sportsQuery = FeedsQuery(
229228
filter: Filter.and([
230229
Filter.equal(FeedsFilterField.visibility, 'public'),
231230
Filter.query(FeedsFilterField.name, 'Sports'),
@@ -235,7 +234,7 @@ Future<void> queryFeedsByNameOrVisibility() async {
235234
final sportsFeedList = client.feedList(sportsQuery);
236235
final sportsFeeds = await sportsFeedList.get();
237236

238-
final techQuery = FeedsQuery(
237+
const techQuery = FeedsQuery(
239238
filter: Filter.and([
240239
Filter.equal(FeedsFilterField.visibility, 'public'),
241240
Filter.autoComplete(FeedsFilterField.description, 'tech'),
@@ -247,7 +246,7 @@ Future<void> queryFeedsByNameOrVisibility() async {
247246
}
248247

249248
Future<void> queryFeedsByCreatorName() async {
250-
final query = FeedsQuery(
249+
const query = FeedsQuery(
251250
filter: Filter.and([
252251
Filter.equal(FeedsFilterField.visibility, 'public'),
253252
Filter.query(FeedsFilterField.createdByName, 'Thompson'),

melos.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ command:
4646
stream_core:
4747
git:
4848
url: https://github.com/GetStream/stream-core-flutter.git
49-
ref: 5acdcb8e378548372f7dbb6326c518edcf832d10
49+
ref: 280b1045e39388668fd060439259831611b51b5a
5050
path: packages/stream_core
5151

5252
# List of all the dev_dependencies used in the project.

packages/stream_feeds/lib/src/models/query_configuration.freezed.dart

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/stream_feeds/lib/src/repository/activities_repository.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ class ActivitiesRepository {
109109

110110
/// Deletes multiple activities.
111111
///
112-
/// Deletes the provided [deleteActivitiesRequest.ids] in a single batch operation.
112+
/// Deletes the provided [deleteActivitiesRequest] in a single batch operation.
113113
///
114114
/// Returns a [Result] containing the list of deleted activity ids or an error.
115115
Future<Result<api.DeleteActivitiesResponse>> deleteActivities({

packages/stream_feeds/lib/src/state/query/activities_query.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class ActivitiesQuery with _$ActivitiesQuery {
3535
///
3636
/// Use [ActivitiesFilterField] for type-safe field references.
3737
@override
38-
final Filter? filter;
38+
final Filter<ActivitiesFilterField>? filter;
3939

4040
/// The sorting criteria for this query.
4141
@override
@@ -57,7 +57,7 @@ class ActivitiesQuery with _$ActivitiesQuery {
5757
// region Filter
5858

5959
/// Represents a field that can be used in activities filtering.
60-
extension type const ActivitiesFilterField(String field) implements String {
60+
extension type const ActivitiesFilterField(String _) implements FilterField {
6161
/// Filter by the creation timestamp of the activity.
6262
///
6363
/// **Supported operators:** `.equal`, `.greaterThan`, `.lessThan`, `.greaterThanOrEqual`, `.lessThanOrEqual`
@@ -134,7 +134,7 @@ class ActivitiesSort extends Sort<ActivityData> {
134134
/// This extension type provides specific fields for sorting activity data.
135135
/// Each field corresponds to a property of the ActivityData model, allowing for flexible
136136
/// sorting options when querying activities.
137-
extension type const ActivitiesSortField(SortField<ActivityData> field)
137+
extension type const ActivitiesSortField(SortField<ActivityData> _)
138138
implements SortField<ActivityData> {
139139
/// Sort by the creation timestamp of the activity.
140140
/// This field allows sorting activities by when they were created (newest/oldest first).

packages/stream_feeds/lib/src/state/query/activities_query.freezed.dart

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/stream_feeds/lib/src/state/query/activity_reactions_query.dart

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ class ActivityReactionsQuery with _$ActivityReactionsQuery {
4545
///
4646
/// Use [ActivityReactionsFilterField] for type-safe field references.
4747
@override
48-
final Filter? filter;
48+
final Filter<ActivityReactionsFilterField>? filter;
4949

5050
/// Array of sorting criteria for this query.
5151
///
@@ -80,8 +80,8 @@ class ActivityReactionsQuery with _$ActivityReactionsQuery {
8080
///
8181
/// This extension type provides a type-safe way to specify which field should be used
8282
/// when creating filters for activity reactions queries.
83-
extension type const ActivityReactionsFilterField(String field)
84-
implements String {
83+
extension type const ActivityReactionsFilterField(String _)
84+
implements FilterField {
8585
/// Filter by the reaction type (e.g., "like", "love", "angry").
8686
///
8787
/// **Supported operators:** `.equal`, `.in`
@@ -124,8 +124,7 @@ class ActivityReactionsSort extends Sort<FeedsReactionData> {
124124
/// Defines the fields by which activity reactions can be sorted.
125125
///
126126
/// This extension type provides specific fields for sorting activity reaction data.
127-
extension type const ActivityReactionsSortField(
128-
SortField<FeedsReactionData> field)
127+
extension type const ActivityReactionsSortField(SortField<FeedsReactionData> _)
129128
implements SortField<FeedsReactionData> {
130129
/// Sort by the creation timestamp of the reaction.
131130
/// This field allows sorting reactions by when they were created (newest/oldest first).

packages/stream_feeds/lib/src/state/query/activity_reactions_query.freezed.dart

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/stream_feeds/lib/src/state/query/bookmark_folders_query.dart

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class BookmarkFoldersQuery with _$BookmarkFoldersQuery {
3939
///
4040
/// Use [BookmarkFoldersFilterField] for type-safe field references.
4141
@override
42-
final Filter? filter;
42+
final Filter<BookmarkFoldersFilterField>? filter;
4343

4444
/// Array of sorting criteria for this query.
4545
///
@@ -69,8 +69,8 @@ class BookmarkFoldersQuery with _$BookmarkFoldersQuery {
6969
///
7070
/// This extension type provides a type-safe way to specify which field should be used
7171
/// when creating filters for bookmark folders queries.
72-
extension type const BookmarkFoldersFilterField(String field)
73-
implements String {
72+
extension type const BookmarkFoldersFilterField(String _)
73+
implements FilterField {
7474
/// Filter by the unique identifier of the bookmark folder.
7575
///
7676
/// **Supported operators:** `.equal`, `.in`
@@ -123,8 +123,7 @@ class BookmarkFoldersSort extends Sort<BookmarkFolderData> {
123123
/// Defines the fields by which bookmark folders can be sorted.
124124
///
125125
/// This extension type provides specific fields for sorting bookmark folder data.
126-
extension type const BookmarkFoldersSortField(
127-
SortField<BookmarkFolderData> field)
126+
extension type const BookmarkFoldersSortField(SortField<BookmarkFolderData> _)
128127
implements SortField<BookmarkFolderData> {
129128
/// Sort by the creation timestamp of the bookmark folder.
130129
/// This field allows sorting bookmark folders by when they were created (newest/oldest first).

0 commit comments

Comments
 (0)