Skip to content

Commit a25d4a9

Browse files
Merge pull request #640 from fischerscode/livelist-nullsafety
LiveList/LiveQuery nullsafety
2 parents 254dcda + a1d95b3 commit a25d4a9

File tree

9 files changed

+249
-209
lines changed

9 files changed

+249
-209
lines changed

.travis.yml

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ script:
1313
- (cd packages/dart && pub get)
1414
- (cd packages/dart && dart run build_runner build --delete-conflicting-outputs)
1515
- (cd packages/dart && pub run test)
16+
- (cd packages/flutter && flutter pub remove parse_server_sdk)
17+
- (cd packages/flutter && flutter pub add parse_server_sdk --path ../dart)
1618
- (cd packages/flutter && flutter pub get)
1719
- (cd packages/flutter && flutter test --no-pub test/)
1820

packages/dart/lib/src/base/parse_constants.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
part of flutter_parse_sdk;
22

33
// Library
4-
const String keySdkVersion = '3.0.0';
4+
const String keySdkVersion = '3.1.0';
55
const String keyLibraryName = 'Flutter Parse SDK';
66

77
// End Points

packages/dart/lib/src/network/parse_live_query.dart

+74-56
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,9 @@ class LiveQueryReconnectingController {
6161
break;
6262
case LiveQueryClientEvent.USER_DISCONNECTED:
6363
_userDisconnected = true;
64-
if (_currentTimer != null) {
65-
_currentTimer!.cancel();
64+
Timer? currentTimer = _currentTimer;
65+
if (currentTimer != null) {
66+
currentTimer.cancel();
6667
_currentTimer = null;
6768
}
6869
break;
@@ -126,33 +127,38 @@ class LiveQueryReconnectingController {
126127
class LiveQueryClient {
127128
factory LiveQueryClient() => _getInstance();
128129

129-
LiveQueryClient._internal({bool? debug, bool? autoSendSessionId}) {
130+
LiveQueryClient._internal(this._liveQueryURL,
131+
{bool? debug, bool? autoSendSessionId}) {
130132
_clientEventStreamController = StreamController<LiveQueryClientEvent>();
131133
_clientEventStream =
132134
_clientEventStreamController.stream.asBroadcastStream();
133135

134136
_debug = isDebugEnabled(objectLevelDebug: debug);
135-
_sendSessionId =
136-
autoSendSessionId ?? ParseCoreData().autoSendSessionId;
137-
_liveQueryURL = ParseCoreData().liveQueryURL;
138-
assert(_liveQueryURL != null,
139-
'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.');
140-
if (_liveQueryURL!.contains('https')) {
141-
_liveQueryURL = _liveQueryURL!.replaceAll('https', 'wss');
142-
} else if (_liveQueryURL!.contains('http')) {
143-
_liveQueryURL = _liveQueryURL!.replaceAll('http', 'ws');
144-
}
137+
_sendSessionId = autoSendSessionId ?? ParseCoreData().autoSendSessionId;
145138

146139
reconnectingController = LiveQueryReconnectingController(
147140
() => reconnect(userInitialized: false), getClientEventStream, _debug);
148141
}
149142
static LiveQueryClient get instance => _getInstance();
150143
static LiveQueryClient? _instance;
151-
static LiveQueryClient _getInstance(
152-
{bool? debug, bool? autoSendSessionId}) {
153-
_instance ??= LiveQueryClient._internal(
154-
debug: debug, autoSendSessionId: autoSendSessionId);
155-
return _instance!;
144+
static LiveQueryClient _getInstance({bool? debug, bool? autoSendSessionId}) {
145+
String? liveQueryURL = ParseCoreData().liveQueryURL;
146+
if (liveQueryURL == null) {
147+
assert(false,
148+
'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.');
149+
liveQueryURL = "";
150+
} else {
151+
if (liveQueryURL.contains('https')) {
152+
liveQueryURL = liveQueryURL.replaceAll('https', 'wss');
153+
} else if (liveQueryURL.contains('http')) {
154+
liveQueryURL = liveQueryURL.replaceAll('http', 'ws');
155+
}
156+
}
157+
LiveQueryClient instance = _instance ??
158+
LiveQueryClient._internal(liveQueryURL,
159+
debug: debug, autoSendSessionId: autoSendSessionId);
160+
_instance ??= instance;
161+
return instance;
156162
}
157163

158164
Stream<LiveQueryClientEvent> get getClientEventStream {
@@ -163,7 +169,7 @@ class LiveQueryClient {
163169
late bool _debug;
164170
late bool _sendSessionId;
165171
WebSocketChannel? _channel;
166-
String? _liveQueryURL;
172+
final String _liveQueryURL;
167173
bool _connecting = false;
168174
late StreamController<LiveQueryClientEvent> _clientEventStreamController;
169175
late Stream<LiveQueryClientEvent> _clientEventStream;
@@ -177,26 +183,29 @@ class LiveQueryClient {
177183
}
178184

179185
int readyState() {
180-
if (_webSocket != null) {
181-
return _webSocket!.readyState;
186+
parse_web_socket.WebSocket? webSocket = _webSocket;
187+
if (webSocket != null) {
188+
return webSocket.readyState;
182189
}
183190
return parse_web_socket.WebSocket.CONNECTING;
184191
}
185192

186193
Future<dynamic> disconnect({bool userInitialized = false}) async {
187-
if (_webSocket != null &&
188-
_webSocket!.readyState == parse_web_socket.WebSocket.OPEN) {
194+
parse_web_socket.WebSocket? webSocket = _webSocket;
195+
if (webSocket != null &&
196+
webSocket.readyState == parse_web_socket.WebSocket.OPEN) {
189197
if (_debug) {
190198
print('$_printConstLiveQuery: Socket closed');
191199
}
192-
await _webSocket!.close();
200+
await webSocket.close();
193201
_webSocket = null;
194202
}
195-
if (_channel != null && _channel!.sink != null) {
203+
WebSocketChannel? channel = _channel;
204+
if (channel != null) {
196205
if (_debug) {
197206
print('$_printConstLiveQuery: close');
198207
}
199-
await _channel!.sink.close();
208+
await channel.sink.close();
200209
_channel = null;
201210
}
202211
_requestSubscription.values.toList().forEach((Subscription subscription) {
@@ -231,11 +240,12 @@ class LiveQueryClient {
231240
'op': 'unsubscribe',
232241
'requestId': subscription.requestId,
233242
};
234-
if (_channel != null && _channel!.sink != null) {
243+
WebSocketChannel? channel = _channel;
244+
if (channel != null) {
235245
if (_debug) {
236246
print('$_printConstLiveQuery: UnsubscribeMessage: $unsubscribeMessage');
237247
}
238-
_channel!.sink.add(jsonEncode(unsubscribeMessage));
248+
channel.sink.add(jsonEncode(unsubscribeMessage));
239249
subscription._enabled = false;
240250
_requestSubscription.remove(subscription.requestId);
241251
}
@@ -256,10 +266,11 @@ class LiveQueryClient {
256266
_connecting = true;
257267

258268
try {
259-
_webSocket = await parse_web_socket.WebSocket.connect(_liveQueryURL!);
269+
parse_web_socket.WebSocket webSocket =
270+
await parse_web_socket.WebSocket.connect(_liveQueryURL);
271+
_webSocket = webSocket;
260272
_connecting = false;
261-
if (_webSocket != null &&
262-
_webSocket!.readyState == parse_web_socket.WebSocket.OPEN) {
273+
if (webSocket.readyState == parse_web_socket.WebSocket.OPEN) {
263274
if (_debug) {
264275
print('$_printConstLiveQuery: Socket opened');
265276
}
@@ -269,8 +280,9 @@ class LiveQueryClient {
269280
}
270281
return Future<void>.value(null);
271282
}
272-
_channel = _webSocket!.createWebSocketChannel();
273-
_channel!.stream.listen((dynamic message) {
283+
WebSocketChannel channel = webSocket.createWebSocketChannel();
284+
_channel = channel;
285+
channel.stream.listen((dynamic message) {
274286
_handleMessage(message);
275287
}, onDone: () {
276288
_clientEventStreamController.sink
@@ -302,7 +314,8 @@ class LiveQueryClient {
302314
}
303315

304316
void _connectLiveQuery() {
305-
if (_channel == null || _channel!.sink == null) {
317+
WebSocketChannel? channel = _channel;
318+
if (channel == null) {
306319
return;
307320
}
308321
//The connect message is sent from a client to the LiveQuery server.
@@ -312,19 +325,21 @@ class LiveQueryClient {
312325
'applicationId': ParseCoreData().applicationId
313326
};
314327

315-
if (_sendSessionId && ParseCoreData().sessionId != null) {
316-
connectMessage['sessionToken'] = ParseCoreData().sessionId!;
328+
if (_sendSessionId) {
329+
String? sessionId = ParseCoreData().sessionId;
330+
if (sessionId != null) {
331+
connectMessage['sessionToken'] = sessionId;
332+
}
317333
}
318-
319-
if (ParseCoreData().clientKey != null)
320-
connectMessage['clientKey'] = ParseCoreData().clientKey!;
321-
if (ParseCoreData().masterKey != null)
322-
connectMessage['masterKey'] = ParseCoreData().masterKey!;
334+
String? clientKey = ParseCoreData().clientKey;
335+
String? masterKey = ParseCoreData().masterKey;
336+
if (clientKey != null) connectMessage['clientKey'] = clientKey;
337+
if (masterKey != null) connectMessage['masterKey'] = masterKey;
323338

324339
if (_debug) {
325340
print('$_printConstLiveQuery: ConnectMessage: $connectMessage');
326341
}
327-
_channel!.sink.add(jsonEncode(connectMessage));
342+
channel.sink.add(jsonEncode(connectMessage));
328343
}
329344

330345
void _subscribeLiveQuery(Subscription subscription) {
@@ -347,7 +362,7 @@ class LiveQueryClient {
347362
'op': 'subscribe',
348363
'requestId': subscription.requestId,
349364
'query': <String, dynamic>{
350-
'className': query.object!.parseClassName,
365+
'className': query.object.parseClassName,
351366
'where': _whereMap,
352367
if (keysToReturn != null && keysToReturn.isNotEmpty)
353368
'fields': keysToReturn
@@ -361,7 +376,7 @@ class LiveQueryClient {
361376
print('$_printConstLiveQuery: SubscribeMessage: $subscribeMessage');
362377
}
363378

364-
_channel!.sink.add(jsonEncode(subscribeMessage));
379+
_channel?.sink.add(jsonEncode(subscribeMessage));
365380
}
366381

367382
void _handleMessage(String message) {
@@ -388,22 +403,25 @@ class LiveQueryClient {
388403
return;
389404
}
390405
if (subscription.eventCallbacks.containsKey(actionData['op'])) {
391-
if (actionData.containsKey('object')) {
392-
final Map<String, dynamic> map = actionData['object'];
393-
final String? className = map['className'];
394-
if (className == keyClassUser) {
395-
subscription.eventCallbacks[actionData['op']]!(
396-
(subscription.copyObject ??
406+
Function? eventCallback = subscription.eventCallbacks[actionData['op']];
407+
if (eventCallback != null) {
408+
if (actionData.containsKey('object')) {
409+
final Map<String, dynamic> map = actionData['object'];
410+
final String? className = map['className'];
411+
if (className != null) {
412+
if (className == keyClassUser) {
413+
eventCallback((subscription.copyObject ??
397414
ParseCoreData.instance.createParseUser(null, null, null))
398415
.fromJson(map));
399-
} else {
400-
subscription.eventCallbacks[actionData['op']]!(
401-
(subscription.copyObject ??
402-
ParseCoreData.instance.createObject(className!))
416+
} else {
417+
eventCallback((subscription.copyObject ??
418+
ParseCoreData.instance.createObject(className))
403419
.fromJson(map));
420+
}
421+
}
422+
} else {
423+
eventCallback(actionData);
404424
}
405-
} else {
406-
subscription.eventCallbacks[actionData['op']]!(actionData);
407425
}
408426
}
409427
}

packages/dart/lib/src/network/parse_query.dart

+20-16
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@ class QueryBuilder<T extends ParseObject> {
55
/// Class to create complex queries
66
QueryBuilder(this.object) : super();
77

8-
QueryBuilder.name(String classname)
9-
: this(ParseCoreData.instance.createObject(classname) as T?);
8+
factory QueryBuilder.name(String classname) {
9+
return QueryBuilder(ParseCoreData.instance.createObject(classname) as T);
10+
}
1011

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

23-
QueryBuilder.copy(QueryBuilder<T> query) {
24-
object = query.object;
25-
queries = query.queries
24+
factory QueryBuilder.copy(QueryBuilder<T> query) {
25+
QueryBuilder<T> copy = QueryBuilder(query.object);
26+
copy.queries = query.queries
2627
.map((MapEntry<String, dynamic> entry) =>
2728
MapEntry<String, dynamic>(entry.key, entry.value.toString()))
2829
.toList();
2930
query.limiters.forEach((String key, dynamic value) =>
30-
limiters.putIfAbsent(key, () => value.toString()));
31+
copy.limiters.putIfAbsent(key, () => value.toString()));
32+
return copy;
3133
}
3234

3335
static const String _NO_OPERATOR_NEEDED = 'NO_OP';
3436
static const String _SINGLE_QUERY = 'SINGLE_QUERY';
3537

36-
T? object;
38+
T object;
3739
List<MapEntry<String, dynamic>> queries = <MapEntry<String, dynamic>>[];
3840
final Map<String, dynamic> limiters = Map<String, dynamic>();
3941

@@ -284,18 +286,20 @@ class QueryBuilder<T extends ParseObject> {
284286
}
285287

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

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

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

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

317321
final String inQuery =
318-
query._buildQueryRelationalKey(query.object!.parseClassName, keyInQuery);
322+
query._buildQueryRelationalKey(query.object.parseClassName, keyInQuery);
319323

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

337341
final String inQuery =
338-
query._buildQueryRelationalKey(query.object!.parseClassName, keyInQuery);
342+
query._buildQueryRelationalKey(query.object.parseClassName, keyInQuery);
339343

340344
queries.add(MapEntry<String, dynamic>(
341345
_SINGLE_QUERY, '\"$column\":{\"\$dontSelect\":$inQuery}'));
@@ -346,7 +350,7 @@ class QueryBuilder<T extends ParseObject> {
346350
/// Make sure to call this after defining your queries
347351
Future<ParseResponse> query<T extends ParseObject>(
348352
{ProgressCallback? progressCallback}) async {
349-
return object!.query<T>(
353+
return object.query<T>(
350354
buildQuery(),
351355
progressCallback: progressCallback,
352356
);
@@ -355,12 +359,12 @@ class QueryBuilder<T extends ParseObject> {
355359
Future<ParseResponse> distinct<T extends ParseObject>(
356360
String className) async {
357361
final String queryString = 'distinct=$className';
358-
return object!.distinct<T>(queryString);
362+
return object.distinct<T>(queryString);
359363
}
360364

361365
///Counts the number of objects that match this query
362366
Future<ParseResponse> count() async {
363-
return object!.query(_buildQueryCount());
367+
return object.query(_buildQueryCount());
364368
}
365369

366370
/// Builds the query for Parse

0 commit comments

Comments
 (0)