Skip to content

Commit 508318f

Browse files
committed
Merge remote-tracking branch 'upstream/master' into pc_server
2 parents e154603 + a96d3a5 commit 508318f

File tree

11 files changed

+522
-36
lines changed

11 files changed

+522
-36
lines changed

packages/dart/CHANGELOG.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,24 @@
1+
# [dart-v9.4.4](https://github.com/parse-community/Parse-SDK-Flutter/compare/dart-9.4.3...dart-9.4.4) (2025-12-04)
2+
3+
4+
### Bug Fixes
5+
6+
* `ParseBase.toJson()` failure when date fields are stored as Maps ([#1094](https://github.com/parse-community/Parse-SDK-Flutter/issues/1094)) ([04a8d5b](https://github.com/parse-community/Parse-SDK-Flutter/commit/04a8d5b6d3d811636ea1b54247e73871b25266d1))
7+
8+
# [dart-v9.4.3](https://github.com/parse-community/Parse-SDK-Flutter/compare/dart-9.4.2...dart-9.4.3) (2025-12-04)
9+
10+
11+
### Bug Fixes
12+
13+
* Flutter Web build failure with ambiguous import from `sembast` and `idb_shim` ([#1093](https://github.com/parse-community/Parse-SDK-Flutter/issues/1093)) ([71aa5f2](https://github.com/parse-community/Parse-SDK-Flutter/commit/71aa5f20ab160a6113ac8167d66c540de11ff88f))
14+
15+
# [dart-v9.4.2](https://github.com/parse-community/Parse-SDK-Flutter/compare/dart-9.4.1...dart-9.4.2) (2025-12-02)
16+
17+
18+
### Bug Fixes
19+
20+
* Ethernet not recognized as connectivity state ([#1090](https://github.com/parse-community/Parse-SDK-Flutter/issues/1090)) ([f76fde4](https://github.com/parse-community/Parse-SDK-Flutter/commit/f76fde4f348af5d3992663795d279d57658f4e87))
21+
122
# [dart-v9.4.1](https://github.com/parse-community/Parse-SDK-Flutter/compare/dart-9.4.0...dart-9.4.1) (2025-11-30)
223

324

packages/dart/lib/src/network/parse_connectivity.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ enum ParseConnectivityResult {
55
/// WiFi: Device connected via Wi-Fi
66
wifi,
77

8+
/// Ethernet: Device connected via Ethernet
9+
ethernet,
10+
811
/// Mobile: Device connected to cellular network
912
mobile,
1013

packages/dart/lib/src/objects/parse_base.dart

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -52,22 +52,24 @@ abstract class ParseBase {
5252

5353
/// Returns [DateTime] createdAt
5454
DateTime? get createdAt {
55-
if (get<dynamic>(keyVarCreatedAt) is String) {
56-
final String? dateAsString = get<String>(keyVarCreatedAt);
57-
return dateAsString != null ? _parseDateFormat.parse(dateAsString) : null;
58-
} else {
59-
return get<DateTime>(keyVarCreatedAt);
55+
final dynamic value = get<dynamic>(keyVarCreatedAt);
56+
if (value is DateTime) return value;
57+
if (value is String) return _parseDateFormat.parse(value);
58+
if (value is Map<String, dynamic> && value['iso'] is String) {
59+
return _parseDateFormat.parse(value['iso'] as String);
6060
}
61+
return null;
6162
}
6263

6364
/// Returns [DateTime] updatedAt
6465
DateTime? get updatedAt {
65-
if (get<dynamic>(keyVarUpdatedAt) is String) {
66-
final String? dateAsString = get<String>(keyVarUpdatedAt);
67-
return dateAsString != null ? _parseDateFormat.parse(dateAsString) : null;
68-
} else {
69-
return get<DateTime>(keyVarUpdatedAt);
66+
final dynamic value = get<dynamic>(keyVarUpdatedAt);
67+
if (value is DateTime) return value;
68+
if (value is String) return _parseDateFormat.parse(value);
69+
if (value is Map<String, dynamic> && value['iso'] is String) {
70+
return _parseDateFormat.parse(value['iso'] as String);
7071
}
72+
return null;
7173
}
7274

7375
/// Converts object to [String] in JSON format
@@ -140,12 +142,20 @@ abstract class ParseBase {
140142
} else if (key == keyVarCreatedAt) {
141143
if (value is String) {
142144
_getObjectData()[keyVarCreatedAt] = _parseDateFormat.parse(value);
145+
} else if (value is Map<String, dynamic> && value['iso'] is String) {
146+
_getObjectData()[keyVarCreatedAt] = _parseDateFormat.parse(
147+
value['iso'] as String,
148+
);
143149
} else {
144150
_getObjectData()[keyVarCreatedAt] = value;
145151
}
146152
} else if (key == keyVarUpdatedAt) {
147153
if (value is String) {
148154
_getObjectData()[keyVarUpdatedAt] = _parseDateFormat.parse(value);
155+
} else if (value is Map<String, dynamic> && value['iso'] is String) {
156+
_getObjectData()[keyVarUpdatedAt] = _parseDateFormat.parse(
157+
value['iso'] as String,
158+
);
149159
} else {
150160
_getObjectData()[keyVarUpdatedAt] = value;
151161
}

packages/dart/pubspec.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: parse_server_sdk
22
description: The Dart SDK to connect to Parse Server. Build your apps faster with Parse Platform, the complete application stack.
3-
version: 9.4.1
3+
version: 9.4.4
44
homepage: https://parseplatform.org
55
repository: https://github.com/parse-community/Parse-SDK-Flutter
66
issue_tracker: https://github.com/parse-community/Parse-SDK-Flutter/issues
@@ -28,8 +28,8 @@ dependencies:
2828
web_socket_channel: ">=2.4.3 <4.0.0"
2929

3030
#Database
31-
sembast: ^3.6.0
32-
sembast_web: ^2.4.2
31+
sembast: ^3.7.4
32+
sembast_web: ^2.4.0
3333

3434
# Utils
3535
uuid: ^4.5.1

packages/dart/test/src/network/parse_live_query_test.dart

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,30 @@ import 'package:test/test.dart';
66

77
import '../../test_utils.dart';
88

9+
/// Mock connectivity provider for testing different connectivity states
10+
class MockConnectivityProvider implements ParseConnectivityProvider {
11+
final StreamController<ParseConnectivityResult> _controller =
12+
StreamController<ParseConnectivityResult>.broadcast();
13+
ParseConnectivityResult _currentState = ParseConnectivityResult.wifi;
14+
15+
@override
16+
Future<ParseConnectivityResult> checkConnectivity() async {
17+
return _currentState;
18+
}
19+
20+
@override
21+
Stream<ParseConnectivityResult> get connectivityStream => _controller.stream;
22+
23+
void setConnectivity(ParseConnectivityResult state) {
24+
_currentState = state;
25+
_controller.add(state);
26+
}
27+
28+
void dispose() {
29+
_controller.close();
30+
}
31+
}
32+
933
void main() {
1034
setUpAll(() async {
1135
// Create a fake server
@@ -62,4 +86,124 @@ void main() {
6286
// 10 millisecond hold for stream
6387
await Future.delayed(Duration(milliseconds: 10));
6488
});
89+
90+
group('Connectivity handling', () {
91+
late MockConnectivityProvider mockConnectivity;
92+
93+
// Initialize once with mock provider, then test state changes
94+
setUpAll(() async {
95+
mockConnectivity = MockConnectivityProvider();
96+
// Initialize Parse once with the mock provider
97+
await Parse().initialize(
98+
'appId',
99+
serverUrl,
100+
debug: false,
101+
fileDirectory: 'someDirectory',
102+
appName: 'appName',
103+
appPackageName: 'somePackageName',
104+
appVersion: 'someAppVersion',
105+
connectivityProvider: mockConnectivity,
106+
);
107+
});
108+
109+
tearDownAll(() {
110+
mockConnectivity.dispose();
111+
});
112+
113+
// Test data for parameterized connectivity state tests
114+
final connectivityTestCases = <Map<String, dynamic>>[
115+
{
116+
'state': ParseConnectivityResult.wifi,
117+
'isOnline': true,
118+
'description': 'wifi should be treated as online',
119+
},
120+
{
121+
'state': ParseConnectivityResult.ethernet,
122+
'isOnline': true,
123+
'description': 'ethernet should be treated as online',
124+
},
125+
{
126+
'state': ParseConnectivityResult.mobile,
127+
'isOnline': true,
128+
'description': 'mobile should be treated as online',
129+
},
130+
{
131+
'state': ParseConnectivityResult.none,
132+
'isOnline': false,
133+
'description': 'none should be treated as offline',
134+
},
135+
];
136+
137+
for (final testCase in connectivityTestCases) {
138+
test(testCase['description'], () async {
139+
// arrange
140+
final state = testCase['state'] as ParseConnectivityResult;
141+
final isOnline = testCase['isOnline'] as bool;
142+
143+
// act
144+
mockConnectivity.setConnectivity(state);
145+
final result = await mockConnectivity.checkConnectivity();
146+
147+
// assert - verify the state is correctly identified
148+
expect(result, state);
149+
expect(result != ParseConnectivityResult.none, isOnline);
150+
});
151+
}
152+
153+
test('should emit connectivity state transitions through stream', () async {
154+
// arrange
155+
final emittedStates = <ParseConnectivityResult>[];
156+
final subscription = mockConnectivity.connectivityStream.listen((state) {
157+
emittedStates.add(state);
158+
});
159+
160+
// act - transition through different connectivity states
161+
mockConnectivity.setConnectivity(ParseConnectivityResult.wifi);
162+
await Future.delayed(Duration(milliseconds: 10));
163+
mockConnectivity.setConnectivity(ParseConnectivityResult.ethernet);
164+
await Future.delayed(Duration(milliseconds: 10));
165+
mockConnectivity.setConnectivity(ParseConnectivityResult.mobile);
166+
await Future.delayed(Duration(milliseconds: 10));
167+
mockConnectivity.setConnectivity(ParseConnectivityResult.none);
168+
await Future.delayed(Duration(milliseconds: 10));
169+
170+
// assert - all state changes should be emitted
171+
expect(emittedStates.length, 4);
172+
expect(emittedStates[0], ParseConnectivityResult.wifi);
173+
expect(emittedStates[1], ParseConnectivityResult.ethernet);
174+
expect(emittedStates[2], ParseConnectivityResult.mobile);
175+
expect(emittedStates[3], ParseConnectivityResult.none);
176+
177+
// verify online states (wifi, ethernet, mobile) are not "none"
178+
expect(emittedStates[0], isNot(ParseConnectivityResult.none));
179+
expect(emittedStates[1], isNot(ParseConnectivityResult.none));
180+
expect(emittedStates[2], isNot(ParseConnectivityResult.none));
181+
182+
await subscription.cancel();
183+
});
184+
185+
test('should transition from offline to online correctly', () async {
186+
// arrange
187+
final stateChanges = <ParseConnectivityResult>[];
188+
final subscription = mockConnectivity.connectivityStream.listen((state) {
189+
stateChanges.add(state);
190+
});
191+
192+
// act - start offline, then go online via ethernet
193+
mockConnectivity.setConnectivity(ParseConnectivityResult.none);
194+
await Future.delayed(Duration(milliseconds: 10));
195+
mockConnectivity.setConnectivity(ParseConnectivityResult.ethernet);
196+
await Future.delayed(Duration(milliseconds: 10));
197+
198+
// assert
199+
expect(stateChanges.length, 2);
200+
expect(stateChanges[0], ParseConnectivityResult.none);
201+
expect(stateChanges[1], ParseConnectivityResult.ethernet);
202+
// Verify the transition is from offline to online
203+
expect(stateChanges[0] == ParseConnectivityResult.none, true);
204+
expect(stateChanges[1] != ParseConnectivityResult.none, true);
205+
206+
await subscription.cancel();
207+
});
208+
});
65209
}

packages/dart/test/src/objects/parse_base_test.dart

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,5 +170,86 @@ void main() {
170170
);
171171
},
172172
);
173+
174+
group('date parsing', () {
175+
test('createdAt and updatedAt should handle Map date format', () {
176+
// Create a parse object and simulate server response with date as Map
177+
final parseObject = ParseObject('TestClass');
178+
179+
// This is what the server sometimes returns - date as Map
180+
parseObject.fromJson({
181+
'objectId': 'testObjectId',
182+
'createdAt': {'__type': 'Date', 'iso': '2023-01-01T00:00:00.000Z'},
183+
'updatedAt': {'__type': 'Date', 'iso': '2023-01-02T00:00:00.000Z'},
184+
});
185+
186+
// These should not throw and return DateTime objects
187+
expect(parseObject.createdAt, isA<DateTime>());
188+
expect(parseObject.updatedAt, isA<DateTime>());
189+
190+
expect(parseObject.createdAt?.year, equals(2023));
191+
expect(parseObject.createdAt?.month, equals(1));
192+
expect(parseObject.createdAt?.day, equals(1));
193+
194+
expect(parseObject.updatedAt?.year, equals(2023));
195+
expect(parseObject.updatedAt?.month, equals(1));
196+
expect(parseObject.updatedAt?.day, equals(2));
197+
});
198+
199+
test('createdAt and updatedAt should handle String date format', () {
200+
final parseObject = ParseObject('TestClass');
201+
202+
parseObject.fromJson({
203+
'objectId': 'testObjectId',
204+
'createdAt': '2023-01-01T00:00:00.000Z',
205+
'updatedAt': '2023-01-02T00:00:00.000Z',
206+
});
207+
208+
expect(parseObject.createdAt, isA<DateTime>());
209+
expect(parseObject.updatedAt, isA<DateTime>());
210+
211+
expect(parseObject.createdAt?.year, equals(2023));
212+
expect(parseObject.updatedAt?.year, equals(2023));
213+
});
214+
215+
test('createdAt and updatedAt should handle DateTime objects', () {
216+
final createdAt = DateTime(2023, 1, 1);
217+
final updatedAt = DateTime(2023, 1, 2);
218+
219+
final parseObject = ParseObject('TestClass');
220+
parseObject.fromJson({
221+
'objectId': 'testObjectId',
222+
'createdAt': createdAt,
223+
'updatedAt': updatedAt,
224+
});
225+
226+
expect(parseObject.createdAt, equals(createdAt));
227+
expect(parseObject.updatedAt, equals(updatedAt));
228+
});
229+
230+
test('toJson should work when date fields are stored as Maps', () {
231+
final parseObject = ParseObject('TestClass');
232+
233+
// Simulate server response with date as Map
234+
parseObject.fromJson({
235+
'objectId': 'testObjectId',
236+
'createdAt': {'__type': 'Date', 'iso': '2023-01-01T00:00:00.000Z'},
237+
'updatedAt': {'__type': 'Date', 'iso': '2023-01-02T00:00:00.000Z'},
238+
});
239+
240+
// toJson should work without throwing
241+
expect(() => parseObject.toJson(full: true), returnsNormally);
242+
expect(() => parseObject.toString(), returnsNormally);
243+
});
244+
245+
test('createdAt and updatedAt should return null for null values', () {
246+
final parseObject = ParseObject('TestClass');
247+
248+
parseObject.fromJson({'objectId': 'testObjectId'});
249+
250+
expect(parseObject.createdAt, isNull);
251+
expect(parseObject.updatedAt, isNull);
252+
});
253+
});
173254
});
174255
}

packages/flutter/CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
# [flutter-v10.6.2](https://github.com/parse-community/Parse-SDK-Flutter/compare/flutter-10.6.1...flutter-10.6.2) (2025-12-04)
2+
3+
4+
### Bug Fixes
5+
6+
* Flutter Web build failure with ambiguous import from `sembast` and `idb_shim` ([#1093](https://github.com/parse-community/Parse-SDK-Flutter/issues/1093)) ([71aa5f2](https://github.com/parse-community/Parse-SDK-Flutter/commit/71aa5f20ab160a6113ac8167d66c540de11ff88f))
7+
8+
# [flutter-v10.6.1](https://github.com/parse-community/Parse-SDK-Flutter/compare/flutter-10.6.0...flutter-10.6.1) (2025-12-03)
9+
10+
11+
### Bug Fixes
12+
13+
* LiveQuery not working due to ethernet not recognized as connectivity state ([#1091](https://github.com/parse-community/Parse-SDK-Flutter/issues/1091)) ([60520d6](https://github.com/parse-community/Parse-SDK-Flutter/commit/60520d678b470dd04f125e1d207e1558b339acf7))
14+
115
# [flutter-v10.6.0](https://github.com/parse-community/Parse-SDK-Flutter/compare/flutter-10.5.0...flutter-10.6.0) (2025-11-30)
216

317

0 commit comments

Comments
 (0)