Skip to content

Commit d7c8c71

Browse files
committed
Added ParseLiveQuerySubscription and refactored accordingly
1 parent e0d63ab commit d7c8c71

File tree

6 files changed

+405
-130
lines changed

6 files changed

+405
-130
lines changed

Parse/Abstractions/Platform/LiveQueries/IParseLiveQueryController.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
using System.Threading;
22
using System.Threading.Tasks;
3-
using Parse.Abstractions.Platform.Objects;
43

54
namespace Parse.Abstractions.Platform.LiveQueries;
65

6+
/// <summary>
7+
/// Defines an interface for managing LiveQuery connections, subscriptions, and updates
8+
/// in a Parse Server environment.
9+
/// </summary>
710
public interface IParseLiveQueryController
811
{
912
Task ConnectAsync(CancellationToken cancellationToken = default);
1013

11-
Task<int> SubscribeAsync<T>(ParseLiveQuery<T> liveQuery, CancellationToken cancellationToken = default) where T : ParseObject;
14+
Task<IParseLiveQuerySubscription> SubscribeAsync<T>(ParseLiveQuery<T> liveQuery, CancellationToken cancellationToken = default) where T : ParseObject;
1215

1316
Task UpdateSubscriptionAsync<T>(ParseLiveQuery<T> liveQuery, int requestId, CancellationToken cancellationToken = default) where T : ParseObject;
1417

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Threading;
4+
using System.Threading.Tasks;
5+
6+
namespace Parse.Abstractions.Platform.LiveQueries;
7+
8+
/// <summary>
9+
/// Represents a live query subscription that is used with Parse's Live Query service.
10+
/// It allows real-time monitoring and event handling for object changes that match
11+
/// a specified query.
12+
/// </summary>
13+
public interface IParseLiveQuerySubscription
14+
{
15+
/// <summary>
16+
/// Represents the Create event for a live query subscription.
17+
/// This event is triggered when a new object matching the subscription's query is created.
18+
/// </summary>
19+
public event EventHandler<IDictionary<string, object>> Create;
20+
21+
/// <summary>
22+
/// Represents the Enter event for a live query subscription.
23+
/// This event is triggered when an object that did not previously match the query (and was thus not part of the subscription)
24+
/// starts matching the query, typically due to an update.
25+
/// </summary>
26+
public event EventHandler<IDictionary<string, object>> Enter;
27+
28+
/// <summary>
29+
/// Represents the Update event for a live query subscription.
30+
/// This event is triggered when an existing object matching the subscription's query is updated.
31+
/// </summary>
32+
public event EventHandler<IDictionary<string, object>> Update;
33+
34+
/// <summary>
35+
/// Represents the Leave event for a live query subscription.
36+
/// This event is triggered when an object that previously matched the subscription's query
37+
/// no longer matches the criteria and is removed.
38+
/// </summary>
39+
public event EventHandler<IDictionary<string, object>> Leave;
40+
41+
/// <summary>
42+
/// Represents the Delete event for a live query subscription.
43+
/// This event is triggered when an object matching the subscription's query is deleted.
44+
/// </summary>
45+
public event EventHandler<IDictionary<string, object>> Delete;
46+
47+
/// <summary>
48+
/// Updates the current live query subscription with new query parameters,
49+
/// effectively modifying the subscription to reflect the provided live query.
50+
/// This allows adjustments to the filter or watched keys without unsubscribing
51+
/// and re-subscribing.
52+
/// </summary>
53+
/// <typeparam name="T">The type of the ParseObject associated with the subscription.</typeparam>
54+
/// <param name="liveQuery">The updated live query containing new parameters that
55+
/// will replace the existing ones for this subscription.</param>
56+
/// <param name="cancellationToken">A token to monitor for cancellation requests. If triggered,
57+
/// the update process will be halted.</param>
58+
/// <returns>A task that represents the asynchronous operation of updating
59+
/// the subscription with the new query parameters.</returns>
60+
Task UpdateAsync<T>(ParseLiveQuery<T> liveQuery, CancellationToken cancellationToken = default) where T : ParseObject;
61+
62+
/// <summary>
63+
/// Cancels the current live query subscription by unsubscribing from the Parse Live Query server.
64+
/// This ensures that the client will no longer receive real-time updates or notifications
65+
/// associated with this subscription.
66+
/// </summary>
67+
/// <param name="cancellationToken">A token to monitor for cancellation requests. If triggered, the cancellation process will halt.</param>
68+
/// <returns>A task that represents the asynchronous operation of canceling the subscription.</returns>
69+
Task CancelAsync(CancellationToken cancellationToken = default);
70+
}

Parse/Platform/LiveQueries/ParseLiveQuery.cs

Lines changed: 11 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,12 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Collections.ObjectModel;
4-
using System.Diagnostics;
54
using System.Linq;
6-
using System.Net.WebSockets;
7-
using System.Numerics;
8-
using System.Text;
95
using System.Threading;
106
using System.Threading.Tasks;
11-
using Microsoft.VisualBasic.CompilerServices;
127
using Parse.Abstractions.Infrastructure;
8+
using Parse.Abstractions.Platform.LiveQueries;
139
using Parse.Infrastructure.Data;
14-
using Parse.Infrastructure.Utilities;
1510

1611
namespace Parse;
1712

@@ -35,7 +30,7 @@ public class ParseLiveQuery<T> where T : ParseObject
3530
/// <summary>
3631
/// Serialized keys watched.
3732
/// </summary>
38-
ReadOnlyCollection<string> KeyWatch { get; }
33+
ReadOnlyCollection<string> KeyWatchers { get; }
3934

4035
internal string ClassName { get; }
4136

@@ -61,7 +56,7 @@ public ParseLiveQuery(IServiceHub serviceHub, string className, IDictionary<stri
6156

6257
if (watchedKeys is not null)
6358
{
64-
KeyWatch = new ReadOnlyCollection<string>(watchedKeys.ToList());
59+
KeyWatchers = new ReadOnlyCollection<string>(watchedKeys.ToList());
6560
}
6661
}
6762

@@ -70,7 +65,7 @@ public ParseLiveQuery(IServiceHub serviceHub, string className, IDictionary<stri
7065
/// but the remaining values can be null if they aren't changed in this
7166
/// composition.
7267
/// </summary>
73-
internal ParseLiveQuery(ParseLiveQuery<T> source, IEnumerable<string> watchedKeys = null)
68+
internal ParseLiveQuery(ParseLiveQuery<T> source, IEnumerable<string> watchedKeys = null, Func<IDictionary<string, object>> onCreate = null)
7469
{
7570
if (source == null)
7671
{
@@ -81,14 +76,15 @@ internal ParseLiveQuery(ParseLiveQuery<T> source, IEnumerable<string> watchedKey
8176
ClassName = source.ClassName;
8277
Filters = source.Filters;
8378
KeySelections = source.KeySelections;
79+
KeyWatchers = source.KeyWatchers;
8480

8581
if (watchedKeys is { })
8682
{
87-
KeyWatch = new ReadOnlyCollection<string>(MergeKeys(watchedKeys).ToList());
83+
KeyWatchers = new ReadOnlyCollection<string>(MergeWatchers(watchedKeys).ToList());
8884
}
8985
}
9086

91-
HashSet<string> MergeKeys(IEnumerable<string> selectedKeys) => new((KeySelections ?? Enumerable.Empty<string>()).Concat(selectedKeys));
87+
HashSet<string> MergeWatchers(IEnumerable<string> keys) => new((KeyWatchers ?? Enumerable.Empty<string>()).Concat(keys));
9288

9389
/// <summary>
9490
/// Add the provided key to the watched fields of returned ParseObjects.
@@ -106,23 +102,13 @@ internal IDictionary<string, object> BuildParameters(bool includeClassName = fal
106102
result["where"] = PointerOrLocalIdEncoder.Instance.Encode(Filters, Services);
107103
if (KeySelections != null)
108104
result["keys"] = String.Join(",", KeySelections.ToArray());
109-
if (KeyWatch != null)
110-
result["watch"] = String.Join(",", KeyWatch.ToArray());
105+
if (KeyWatchers != null)
106+
result["watch"] = String.Join(",", KeyWatchers.ToArray());
111107
if (includeClassName)
112108
result["className"] = ClassName;
113109
return result;
114110
}
115111

116-
/// <summary>
117-
/// Establishes a connection to the Parse Live Query server using the ClientWebSocket instance.
118-
/// Prepares and sends a connection message containing required identifiers such as application ID, client key, and session token.
119-
/// </summary>
120-
/// <returns>A Task representing the asynchronous operation, returning true if the connection attempt is initialized successfully, false otherwise.</returns>
121-
public async Task ConnectAsync()
122-
{
123-
await Services.LiveQueryController.ConnectAsync(CancellationToken.None);
124-
}
125-
126112
/// <summary>
127113
/// Subscribes to the live query, allowing the client to receive real-time updates
128114
/// for the query's results. This establishes a subscription with the Live Query service.
@@ -131,30 +117,8 @@ public async Task ConnectAsync()
131117
/// A task representing the asynchronous subscription operation. Upon completion
132118
/// of the task, the subscription is successfully registered.
133119
/// </returns>
134-
public async Task SubscribeAsync()
135-
{
136-
RequestId = await Services.LiveQueryController.SubscribeAsync(this, CancellationToken.None);
137-
}
138-
139-
/// <summary>
140-
/// Unsubscribes from the live query, stopping the client from receiving further updates related to the subscription.
141-
/// </summary>
142-
/// <returns>A task representing the asynchronous operation of unsubscribing from the live query.</returns>
143-
public async Task UnsubscribeAsync()
144-
{
145-
if (RequestId > 0)
146-
await Services.LiveQueryController.UnsubscribeAsync(RequestId, CancellationToken.None);
147-
}
148-
149-
/// <summary>
150-
/// Closes the connection to the live query server asynchronously.
151-
/// </summary>
152-
/// <returns>
153-
/// A task representing the asynchronous operation of closing the live query connection.
154-
/// </returns>
155-
public async Task CloseAsync()
120+
public async Task<IParseLiveQuerySubscription> SubscribeAsync()
156121
{
157-
await Services.LiveQueryController.CloseAsync(CancellationToken.None);
122+
return await Services.LiveQueryController.SubscribeAsync(this, CancellationToken.None);
158123
}
159-
160124
}

0 commit comments

Comments
 (0)