Skip to content

Commit e2d0e4e

Browse files
authored
feat: Add include option to getObject and fetch (#798)
1 parent 2befe18 commit e2d0e4e

15 files changed

+498
-206
lines changed

packages/dart/CHANGELOG.md

+6
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
## [3.1.7](https://github.com/parse-community/Parse-SDK-Flutter/compare/dart-3.1.6...dart-3.1.7) (2022-12-22)
2+
3+
### Features
4+
5+
* Add `include` option to `getObject` and `fetch` ([#798](https://github.com/parse-community/Parse-SDK-Flutter/issues/798))
6+
17
## [3.1.6](https://github.com/parse-community/Parse-SDK-Flutter/compare/dart-3.1.5...dart-3.1.6) (2022-12-21)
28

39
### Bug Fixes

packages/dart/lib/parse_server_sdk.dart

-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import 'dart:convert';
55
import 'dart:io';
66
import 'dart:math';
77
import 'dart:typed_data';
8-
98
import 'package:dio/dio.dart';
109
import 'package:meta/meta.dart';
1110
import 'package:mime_type/mime_type.dart';

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.1.6';
4+
const String keySdkVersion = '3.1.7';
55
const String keyLibraryName = 'Flutter Parse SDK';
66

77
// End Points

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

-14
Original file line numberDiff line numberDiff line change
@@ -435,20 +435,6 @@ class QueryBuilder<T extends ParseObject> {
435435
return queryBuilder;
436436
}
437437

438-
String concatenateArray(List<String> queries) {
439-
String queryBuilder = '';
440-
441-
for (final String item in queries) {
442-
if (item == queries.first) {
443-
queryBuilder += item;
444-
} else {
445-
queryBuilder += ',$item';
446-
}
447-
}
448-
449-
return queryBuilder;
450-
}
451-
452438
/// Creates a query param using the column, the value and the queryOperator
453439
/// that the column and value are being queried against
454440
MapEntry<String, dynamic> _buildQueryWithColumnValueAndOperator(

packages/dart/lib/src/objects/parse_object.dart

+12-4
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,15 @@ class ParseObject extends ParseBase implements ParseCloneable {
3333
late ParseClient _client;
3434

3535
/// Gets an object from the server using it's [String] objectId
36-
Future<ParseResponse> getObject(String objectId) async {
36+
///
37+
/// `List<String>` include refers to other ParseObjects stored as a Pointer
38+
Future<ParseResponse> getObject(String objectId,
39+
{List<String>? include}) async {
3740
try {
38-
final String uri = '$_path/$objectId';
41+
String uri = '$_path/$objectId';
42+
if (include != null) {
43+
uri = '$uri?include=${concatenateArray(include)}';
44+
}
3945
final Uri url = getSanitisedUri(_client, uri);
4046

4147
final ParseNetworkResponse result = await _client.get(url.toString());
@@ -451,12 +457,14 @@ class ParseObject extends ParseBase implements ParseCloneable {
451457

452458
///Fetches this object with the data from the server. Call this whenever you want the state of the
453459
///object to reflect exactly what is on the server.
454-
Future<ParseObject> fetch() async {
460+
///
461+
/// `List<String>` include refers to other ParseObjects stored as a Pointer
462+
Future<ParseObject> fetch({List<String>? include}) async {
455463
if (objectId == null || objectId!.isEmpty) {
456464
throw 'can not fetch without a objectId';
457465
}
458466

459-
final ParseResponse response = await getObject(objectId!);
467+
final ParseResponse response = await getObject(objectId!, include: include);
460468

461469
if (response.success && response.results != null) {
462470
return response.results!.first;

packages/dart/lib/src/utils/parse_utils.dart

+15
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,21 @@ bool isDebugEnabled({bool? objectLevelDebug}) {
88
return objectLevelDebug ?? ParseCoreData().debug;
99
}
1010

11+
/// Convert list of strings to a string with commas
12+
String concatenateArray(List<String> list) {
13+
String output = '';
14+
15+
for (final String item in list) {
16+
if (item == list.first) {
17+
output += item;
18+
} else {
19+
output += ',$item';
20+
}
21+
}
22+
23+
return output;
24+
}
25+
1126
/// Converts the object to the correct value for JSON,
1227
///
1328
/// Strings are wrapped with "" but integers and others are not

packages/dart/pubspec.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: parse_server_sdk
22
description: Dart plugin for Parse Server, (https://parseplatform.org), (https://back4app.com)
3-
version: 3.1.6
3+
version: 3.1.7
44
homepage: https://github.com/parse-community/Parse-SDK-Flutter
55

66
environment:

packages/dart/test/parse_client_configuration_test.dart

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import 'package:test/test.dart';
33

44
void main() {
55
test('testBuilder', () async {
6+
// arrange
67
await Parse().initialize(
78
'appId',
89
'serverUrl',
@@ -17,6 +18,7 @@ void main() {
1718
debug: true,
1819
);
1920

21+
// assert
2022
expect(ParseCoreData().applicationId, 'appId');
2123
expect(ParseCoreData().serverUrl, 'serverUrl');
2224
expect(ParseCoreData().clientKey, 'clientKey');
+37-10
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import 'dart:convert';
2+
13
import 'package:parse_server_sdk/parse_server_sdk.dart';
24
import 'package:test/test.dart';
35

@@ -8,7 +10,7 @@ void main() {
810
// arrange
911
await Parse().initialize(
1012
'appId',
11-
'https://test.parse.com',
13+
'https://example.com',
1214
debug: true,
1315
// to prevent automatic detection
1416
fileDirectory: 'someDirectory',
@@ -20,10 +22,10 @@ void main() {
2022
appVersion: 'someAppVersion',
2123
);
2224

25+
// act
2326
ParseObject parseObject2 = ParseObject("objectId2");
2427
parseObject2.objectId = "objectId2";
2528

26-
// List and Map
2729
parseObject2
2830
.setAdd("dataParseObjectList", ["ListText1", "ListText2", "ListText3"]);
2931
parseObject2.setAdd("dataParseObjectMap", {
@@ -32,19 +34,44 @@ void main() {
3234
'KeyTestMap3': 'ValueTestMap3',
3335
});
3436

35-
// parseObject2 inside parseObject1
3637
ParseObject parseObject1 = ParseObject("parseObject1");
3738
parseObject1.objectId = "objectId1";
3839
parseObject1.setAdd("dataParseObject2", parseObject2);
3940

40-
// desired output
41-
String expectedResult =
42-
"{className: parseObject1, objectId: objectId1, dataParseObject2: {__op: Add, objects: [{className: objectId2, objectId: objectId2, dataParseObjectList: {__op: Add, objects: [[ListText1, ListText2, ListText3]]}, dataParseObjectMap: {__op: Add, objects: [{KeyTestMap1: ValueTestMap1, KeyTestMap2: ValueTestMap2, KeyTestMap3: ValueTestMap3}]}}]}}";
43-
44-
// act
4541
dynamic actualResult = parseEncode(parseObject1, full: true);
4642

47-
//assert
48-
expect(actualResult.toString(), expectedResult);
43+
var objectDesiredOutput = {
44+
"className": "parseObject1",
45+
"objectId": "objectId1",
46+
"dataParseObject2": {
47+
"__op": "Add",
48+
"objects": [
49+
{
50+
"className": "objectId2",
51+
"objectId": "objectId2",
52+
"dataParseObjectList": {
53+
"__op": "Add",
54+
"objects": [
55+
["ListText1", "ListText2", "ListText3"]
56+
],
57+
},
58+
"dataParseObjectMap": {
59+
"__op": "Add",
60+
"objects": [
61+
{
62+
"KeyTestMap1": "ValueTestMap1",
63+
"KeyTestMap2": "ValueTestMap2",
64+
"KeyTestMap3": "ValueTestMap3"
65+
}
66+
]
67+
}
68+
}
69+
],
70+
},
71+
};
72+
var objectJsonDesiredOutput = jsonEncode(objectDesiredOutput);
73+
74+
// assert
75+
expect(jsonEncode(actualResult), objectJsonDesiredOutput);
4976
});
5077
}
+185
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
import 'dart:convert';
2+
3+
import 'package:mockito/annotations.dart';
4+
import 'package:mockito/mockito.dart';
5+
import 'package:parse_server_sdk/parse_server_sdk.dart';
6+
import 'package:test/test.dart';
7+
import 'parse_query_test.mocks.dart';
8+
9+
@GenerateMocks([ParseClient])
10+
void main() {
11+
group('parseObject', () {
12+
late MockParseClient client;
13+
setUp(() async {
14+
client = MockParseClient();
15+
16+
await Parse().initialize(
17+
'appId',
18+
'https://example.com',
19+
debug: true,
20+
// to prevent automatic detection
21+
fileDirectory: 'someDirectory',
22+
// to prevent automatic detection
23+
appName: 'appName',
24+
// to prevent automatic detection
25+
appPackageName: 'somePackageName',
26+
// to prevent automatic detection
27+
appVersion: 'someAppVersion',
28+
);
29+
});
30+
31+
test('should return expectedIncludeResult json when use fetch', () async {
32+
// arrange
33+
ParseObject myUserObject = ParseObject("MyUser", client: client);
34+
myUserObject.objectId = "Mn1iJTkWTE";
35+
36+
var desiredOutput = {
37+
"results": [
38+
{
39+
"objectId": "Mn1iJTkWTE",
40+
"phone": "+12025550463",
41+
"createdAt": "2022-09-04T13:35:20.883Z",
42+
"updatedAt": "2022-11-14T10:55:56.202Z",
43+
"img": {
44+
"objectId": "8nGrLj3Mvk",
45+
"size": "67663",
46+
"mime": "image/jpg",
47+
"file": {
48+
"__type": "File",
49+
"name": "dc7320ee9146ee19aed8997722fd4e3c.bin",
50+
"url":
51+
"http://ip:port/api/files/myapp/dc7320ee9146ee19aed8997722fd4e3c.bin",
52+
},
53+
"createdAt": "2022-11-14T10:55:56.025Z",
54+
"updatedAt": "2022-11-14T10:55:56.025Z",
55+
"__type": "Object",
56+
"className": "MyFile",
57+
},
58+
}
59+
]
60+
};
61+
62+
when(client.get(
63+
any,
64+
options: anyNamed("options"),
65+
onReceiveProgress: anyNamed("onReceiveProgress"),
66+
)).thenAnswer((_) async => ParseNetworkResponse(
67+
statusCode: 200, data: jsonEncode(desiredOutput)));
68+
69+
// act
70+
ParseObject parseObject = await myUserObject.fetch(include: ["img"]);
71+
72+
var objectDesiredOutput = {
73+
"className": "MyFile",
74+
"objectId": "8nGrLj3Mvk",
75+
"createdAt": "2022-11-14T10:55:56.025Z",
76+
"updatedAt": "2022-11-14T10:55:56.025Z",
77+
"size": "67663",
78+
"mime": "image/jpg",
79+
"file": {
80+
"__type": "File",
81+
"name": "dc7320ee9146ee19aed8997722fd4e3c.bin",
82+
"url":
83+
"http://ip:port/api/files/myapp/dc7320ee9146ee19aed8997722fd4e3c.bin",
84+
},
85+
};
86+
var objectJsonDesiredOutput = jsonEncode(objectDesiredOutput);
87+
88+
final Uri result = Uri.parse(verify(client.get(
89+
captureAny,
90+
options: anyNamed("options"),
91+
onReceiveProgress: anyNamed("onReceiveProgress"),
92+
)).captured.single);
93+
94+
// assert
95+
expect(
96+
jsonEncode(
97+
parseEncode(parseObject.get<ParseObject>('img'), full: true)),
98+
objectJsonDesiredOutput);
99+
expect(parseObject['img'].objectId, "8nGrLj3Mvk");
100+
101+
expect(Uri.decodeComponent(result.path),
102+
'/classes/MyUser/Mn1iJTkWTE?include=img');
103+
});
104+
105+
test('should return expectedIncludeResult json when use getObject',
106+
() async {
107+
// arrange
108+
ParseObject myUserObject = ParseObject("MyUser", client: client);
109+
myUserObject.objectId = "Mn1iJTkWTE";
110+
111+
var desiredOutput = {
112+
"results": [
113+
{
114+
"objectId": "Mn1iJTkWTE",
115+
"phone": "+12025550463",
116+
"createdAt": "2022-09-04T13:35:20.883Z",
117+
"updatedAt": "2022-11-14T10:55:56.202Z",
118+
"img": {
119+
"objectId": "8nGrLj3Mvk",
120+
"size": "67663",
121+
"mime": "image/jpg",
122+
"file": {
123+
"__type": "File",
124+
"name": "dc7320ee9146ee19aed8997722fd4e3c.bin",
125+
"url":
126+
"http://ip:port/api/files/myapp/dc7320ee9146ee19aed8997722fd4e3c.bin",
127+
},
128+
"createdAt": "2022-11-14T10:55:56.025Z",
129+
"updatedAt": "2022-11-14T10:55:56.025Z",
130+
"__type": "Object",
131+
"className": "MyFile",
132+
},
133+
}
134+
]
135+
};
136+
137+
when(client.get(
138+
any,
139+
options: anyNamed("options"),
140+
onReceiveProgress: anyNamed("onReceiveProgress"),
141+
)).thenAnswer((_) async => ParseNetworkResponse(
142+
statusCode: 200, data: jsonEncode(desiredOutput)));
143+
144+
// act
145+
ParseResponse response =
146+
await myUserObject.getObject("Mn1iJTkWTE", include: ["img"]);
147+
148+
ParseObject parseObject = response.results?.first;
149+
150+
var objectDesiredOutput = {
151+
"className": "MyFile",
152+
"objectId": "8nGrLj3Mvk",
153+
"createdAt": "2022-11-14T10:55:56.025Z",
154+
"updatedAt": "2022-11-14T10:55:56.025Z",
155+
"size": "67663",
156+
"mime": "image/jpg",
157+
"file": {
158+
"__type": "File",
159+
"name": "dc7320ee9146ee19aed8997722fd4e3c.bin",
160+
"url":
161+
"http://ip:port/api/files/myapp/dc7320ee9146ee19aed8997722fd4e3c.bin",
162+
},
163+
};
164+
var objectJsonDesiredOutput = jsonEncode(objectDesiredOutput);
165+
166+
final Uri result = Uri.parse(verify(client.get(
167+
captureAny,
168+
options: anyNamed("options"),
169+
onReceiveProgress: anyNamed("onReceiveProgress"),
170+
)).captured.single);
171+
172+
// assert
173+
expect(response.results?.first, isA<ParseObject>());
174+
175+
expect(
176+
jsonEncode(
177+
parseEncode(parseObject.get<ParseObject>('img'), full: true)),
178+
objectJsonDesiredOutput);
179+
expect(parseObject['img'].objectId, "8nGrLj3Mvk");
180+
181+
expect(Uri.decodeComponent(result.path),
182+
'/classes/MyUser/Mn1iJTkWTE?include=img');
183+
});
184+
});
185+
}

0 commit comments

Comments
 (0)