Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ script:
- (cd packages/dart && pub get)
- (cd packages/dart && dart run build_runner build --delete-conflicting-outputs)
- (cd packages/dart && pub run test)
- (cd packages/flutter && flutter pub remove parse_server_sdk)
- (cd packages/flutter && flutter pub add parse_server_sdk --path ../dart)
- (cd packages/flutter && flutter pub get)
- (cd packages/flutter && flutter test --no-pub test/)

Expand Down
2 changes: 1 addition & 1 deletion packages/dart/lib/src/base/parse_constants.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
part of flutter_parse_sdk;

// Library
const String keySdkVersion = '3.0.0';
const String keySdkVersion = '3.1.0';
const String keyLibraryName = 'Flutter Parse SDK';

// End Points
Expand Down
130 changes: 74 additions & 56 deletions packages/dart/lib/src/network/parse_live_query.dart
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,9 @@ class LiveQueryReconnectingController {
break;
case LiveQueryClientEvent.USER_DISCONNECTED:
_userDisconnected = true;
if (_currentTimer != null) {
_currentTimer!.cancel();
Timer? currentTimer = _currentTimer;
if (currentTimer != null) {
currentTimer.cancel();
_currentTimer = null;
}
break;
Expand Down Expand Up @@ -126,33 +127,38 @@ class LiveQueryReconnectingController {
class LiveQueryClient {
factory LiveQueryClient() => _getInstance();

LiveQueryClient._internal({bool? debug, bool? autoSendSessionId}) {
LiveQueryClient._internal(this._liveQueryURL,
{bool? debug, bool? autoSendSessionId}) {
_clientEventStreamController = StreamController<LiveQueryClientEvent>();
_clientEventStream =
_clientEventStreamController.stream.asBroadcastStream();

_debug = isDebugEnabled(objectLevelDebug: debug);
_sendSessionId =
autoSendSessionId ?? ParseCoreData().autoSendSessionId;
_liveQueryURL = ParseCoreData().liveQueryURL;
assert(_liveQueryURL != null,
'liveQueryUrl is not set. For how to setup Live Queries, see https://github.com/parse-community/Parse-SDK-Flutter/tree/master/packages/flutter#live-queries.');
if (_liveQueryURL!.contains('https')) {
_liveQueryURL = _liveQueryURL!.replaceAll('https', 'wss');
} else if (_liveQueryURL!.contains('http')) {
_liveQueryURL = _liveQueryURL!.replaceAll('http', 'ws');
}
_sendSessionId = autoSendSessionId ?? ParseCoreData().autoSendSessionId;

reconnectingController = LiveQueryReconnectingController(
() => reconnect(userInitialized: false), getClientEventStream, _debug);
}
static LiveQueryClient get instance => _getInstance();
static LiveQueryClient? _instance;
static LiveQueryClient _getInstance(
{bool? debug, bool? autoSendSessionId}) {
_instance ??= LiveQueryClient._internal(
debug: debug, autoSendSessionId: autoSendSessionId);
return _instance!;
static LiveQueryClient _getInstance({bool? debug, bool? autoSendSessionId}) {
String? liveQueryURL = ParseCoreData().liveQueryURL;
if (liveQueryURL == null) {
assert(false,
'liveQueryUrl is not set. For how to setup Live Queries, see https://github.com/parse-community/Parse-SDK-Flutter/tree/master/packages/flutter#live-queries.');
liveQueryURL = "";
} else {
if (liveQueryURL.contains('https')) {
liveQueryURL = liveQueryURL.replaceAll('https', 'wss');
} else if (liveQueryURL.contains('http')) {
liveQueryURL = liveQueryURL.replaceAll('http', 'ws');
}
}
LiveQueryClient instance = _instance ??
LiveQueryClient._internal(liveQueryURL,
debug: debug, autoSendSessionId: autoSendSessionId);
_instance ??= instance;
return instance;
}

Stream<LiveQueryClientEvent> get getClientEventStream {
Expand All @@ -163,7 +169,7 @@ class LiveQueryClient {
late bool _debug;
late bool _sendSessionId;
WebSocketChannel? _channel;
String? _liveQueryURL;
final String _liveQueryURL;
bool _connecting = false;
late StreamController<LiveQueryClientEvent> _clientEventStreamController;
late Stream<LiveQueryClientEvent> _clientEventStream;
Expand All @@ -177,26 +183,29 @@ class LiveQueryClient {
}

int readyState() {
if (_webSocket != null) {
return _webSocket!.readyState;
parse_web_socket.WebSocket? webSocket = _webSocket;
if (webSocket != null) {
return webSocket.readyState;
}
return parse_web_socket.WebSocket.CONNECTING;
}

Future<dynamic> disconnect({bool userInitialized = false}) async {
if (_webSocket != null &&
_webSocket!.readyState == parse_web_socket.WebSocket.OPEN) {
parse_web_socket.WebSocket? webSocket = _webSocket;
if (webSocket != null &&
webSocket.readyState == parse_web_socket.WebSocket.OPEN) {
if (_debug) {
print('$_printConstLiveQuery: Socket closed');
}
await _webSocket!.close();
await webSocket.close();
_webSocket = null;
}
if (_channel != null && _channel!.sink != null) {
WebSocketChannel? channel = _channel;
if (channel != null) {
if (_debug) {
print('$_printConstLiveQuery: close');
}
await _channel!.sink.close();
await channel.sink.close();
_channel = null;
}
_requestSubscription.values.toList().forEach((Subscription subscription) {
Expand Down Expand Up @@ -231,11 +240,12 @@ class LiveQueryClient {
'op': 'unsubscribe',
'requestId': subscription.requestId,
};
if (_channel != null && _channel!.sink != null) {
WebSocketChannel? channel = _channel;
if (channel != null) {
if (_debug) {
print('$_printConstLiveQuery: UnsubscribeMessage: $unsubscribeMessage');
}
_channel!.sink.add(jsonEncode(unsubscribeMessage));
channel.sink.add(jsonEncode(unsubscribeMessage));
subscription._enabled = false;
_requestSubscription.remove(subscription.requestId);
}
Expand All @@ -256,10 +266,11 @@ class LiveQueryClient {
_connecting = true;

try {
_webSocket = await parse_web_socket.WebSocket.connect(_liveQueryURL!);
parse_web_socket.WebSocket webSocket =
await parse_web_socket.WebSocket.connect(_liveQueryURL);
_webSocket = webSocket;
_connecting = false;
if (_webSocket != null &&
_webSocket!.readyState == parse_web_socket.WebSocket.OPEN) {
if (webSocket.readyState == parse_web_socket.WebSocket.OPEN) {
if (_debug) {
print('$_printConstLiveQuery: Socket opened');
}
Expand All @@ -269,8 +280,9 @@ class LiveQueryClient {
}
return Future<void>.value(null);
}
_channel = _webSocket!.createWebSocketChannel();
_channel!.stream.listen((dynamic message) {
WebSocketChannel channel = webSocket.createWebSocketChannel();
_channel = channel;
channel.stream.listen((dynamic message) {
_handleMessage(message);
}, onDone: () {
_clientEventStreamController.sink
Expand Down Expand Up @@ -302,7 +314,8 @@ class LiveQueryClient {
}

void _connectLiveQuery() {
if (_channel == null || _channel!.sink == null) {
WebSocketChannel? channel = _channel;
if (channel == null) {
return;
}
//The connect message is sent from a client to the LiveQuery server.
Expand All @@ -312,19 +325,21 @@ class LiveQueryClient {
'applicationId': ParseCoreData().applicationId
};

if (_sendSessionId && ParseCoreData().sessionId != null) {
connectMessage['sessionToken'] = ParseCoreData().sessionId!;
if (_sendSessionId) {
String? sessionId = ParseCoreData().sessionId;
if (sessionId != null) {
connectMessage['sessionToken'] = sessionId;
}
}

if (ParseCoreData().clientKey != null)
connectMessage['clientKey'] = ParseCoreData().clientKey!;
if (ParseCoreData().masterKey != null)
connectMessage['masterKey'] = ParseCoreData().masterKey!;
String? clientKey = ParseCoreData().clientKey;
String? masterKey = ParseCoreData().masterKey;
if (clientKey != null) connectMessage['clientKey'] = clientKey;
if (masterKey != null) connectMessage['masterKey'] = masterKey;

if (_debug) {
print('$_printConstLiveQuery: ConnectMessage: $connectMessage');
}
_channel!.sink.add(jsonEncode(connectMessage));
channel.sink.add(jsonEncode(connectMessage));
}

void _subscribeLiveQuery(Subscription subscription) {
Expand All @@ -347,7 +362,7 @@ class LiveQueryClient {
'op': 'subscribe',
'requestId': subscription.requestId,
'query': <String, dynamic>{
'className': query.object!.parseClassName,
'className': query.object.parseClassName,
'where': _whereMap,
if (keysToReturn != null && keysToReturn.isNotEmpty)
'fields': keysToReturn
Expand All @@ -361,7 +376,7 @@ class LiveQueryClient {
print('$_printConstLiveQuery: SubscribeMessage: $subscribeMessage');
}

_channel!.sink.add(jsonEncode(subscribeMessage));
_channel?.sink.add(jsonEncode(subscribeMessage));
}

void _handleMessage(String message) {
Expand All @@ -388,22 +403,25 @@ class LiveQueryClient {
return;
}
if (subscription.eventCallbacks.containsKey(actionData['op'])) {
if (actionData.containsKey('object')) {
final Map<String, dynamic> map = actionData['object'];
final String? className = map['className'];
if (className == keyClassUser) {
subscription.eventCallbacks[actionData['op']]!(
(subscription.copyObject ??
Function? eventCallback = subscription.eventCallbacks[actionData['op']];
if (eventCallback != null) {
if (actionData.containsKey('object')) {
final Map<String, dynamic> map = actionData['object'];
final String? className = map['className'];
if (className != null) {
if (className == keyClassUser) {
eventCallback((subscription.copyObject ??
ParseCoreData.instance.createParseUser(null, null, null))
.fromJson(map));
} else {
subscription.eventCallbacks[actionData['op']]!(
(subscription.copyObject ??
ParseCoreData.instance.createObject(className!))
} else {
eventCallback((subscription.copyObject ??
ParseCoreData.instance.createObject(className))
.fromJson(map));
}
}
} else {
eventCallback(actionData);
}
} else {
subscription.eventCallbacks[actionData['op']]!(actionData);
}
}
}
Expand Down
36 changes: 20 additions & 16 deletions packages/dart/lib/src/network/parse_query.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ class QueryBuilder<T extends ParseObject> {
/// Class to create complex queries
QueryBuilder(this.object) : super();

QueryBuilder.name(String classname)
: this(ParseCoreData.instance.createObject(classname) as T?);
factory QueryBuilder.name(String classname) {
return QueryBuilder(ParseCoreData.instance.createObject(classname) as T);
}

QueryBuilder.or(this.object, List<QueryBuilder<T>> list) {
String query = '"\$or":[';
Expand All @@ -20,20 +21,21 @@ class QueryBuilder<T extends ParseObject> {
queries.add(MapEntry<String, dynamic>(_NO_OPERATOR_NEEDED, query));
}

QueryBuilder.copy(QueryBuilder<T> query) {
object = query.object;
queries = query.queries
factory QueryBuilder.copy(QueryBuilder<T> query) {
QueryBuilder<T> copy = QueryBuilder(query.object);
copy.queries = query.queries
.map((MapEntry<String, dynamic> entry) =>
MapEntry<String, dynamic>(entry.key, entry.value.toString()))
.toList();
query.limiters.forEach((String key, dynamic value) =>
limiters.putIfAbsent(key, () => value.toString()));
copy.limiters.putIfAbsent(key, () => value.toString()));
return copy;
}

static const String _NO_OPERATOR_NEEDED = 'NO_OP';
static const String _SINGLE_QUERY = 'SINGLE_QUERY';

T? object;
T object;
List<MapEntry<String, dynamic>> queries = <MapEntry<String, dynamic>>[];
final Map<String, dynamic> limiters = Map<String, dynamic>();

Expand Down Expand Up @@ -284,18 +286,20 @@ class QueryBuilder<T extends ParseObject> {
}

// Add a constraint to the query that requires a particular key's value match another QueryBuilder
void whereMatchesQuery<E extends ParseObject>(String column, QueryBuilder<E> query) {
void whereMatchesQuery<E extends ParseObject>(
String column, QueryBuilder<E> query) {
final String inQuery =
query._buildQueryRelational(query.object!.parseClassName);
query._buildQueryRelational(query.object.parseClassName);

queries.add(MapEntry<String, dynamic>(
_SINGLE_QUERY, '\"$column\":{\"\$inQuery\":$inQuery}'));
}

//Add a constraint to the query that requires a particular key's value does not match another QueryBuilder
void whereDoesNotMatchQuery<E extends ParseObject>(String column, QueryBuilder<E> query) {
void whereDoesNotMatchQuery<E extends ParseObject>(
String column, QueryBuilder<E> query) {
final String inQuery =
query._buildQueryRelational(query.object!.parseClassName);
query._buildQueryRelational(query.object.parseClassName);

queries.add(MapEntry<String, dynamic>(
_SINGLE_QUERY, '\"$column\":{\"\$notInQuery\":$inQuery}'));
Expand All @@ -315,7 +319,7 @@ class QueryBuilder<T extends ParseObject> {
}

final String inQuery =
query._buildQueryRelationalKey(query.object!.parseClassName, keyInQuery);
query._buildQueryRelationalKey(query.object.parseClassName, keyInQuery);

queries.add(MapEntry<String, dynamic>(
_SINGLE_QUERY, '\"$column\":{\"\$select\":$inQuery}'));
Expand All @@ -335,7 +339,7 @@ class QueryBuilder<T extends ParseObject> {
}

final String inQuery =
query._buildQueryRelationalKey(query.object!.parseClassName, keyInQuery);
query._buildQueryRelationalKey(query.object.parseClassName, keyInQuery);

queries.add(MapEntry<String, dynamic>(
_SINGLE_QUERY, '\"$column\":{\"\$dontSelect\":$inQuery}'));
Expand All @@ -346,7 +350,7 @@ class QueryBuilder<T extends ParseObject> {
/// Make sure to call this after defining your queries
Future<ParseResponse> query<T extends ParseObject>(
{ProgressCallback? progressCallback}) async {
return object!.query<T>(
return object.query<T>(
buildQuery(),
progressCallback: progressCallback,
);
Expand All @@ -355,12 +359,12 @@ class QueryBuilder<T extends ParseObject> {
Future<ParseResponse> distinct<T extends ParseObject>(
String className) async {
final String queryString = 'distinct=$className';
return object!.distinct<T>(queryString);
return object.distinct<T>(queryString);
}

///Counts the number of objects that match this query
Future<ParseResponse> count() async {
return object!.query(_buildQueryCount());
return object.query(_buildQueryCount());
}

/// Builds the query for Parse
Expand Down
Loading