Skip to content

feat: Add include option to getObject and fetch #798

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 13 commits into from
Dec 22, 2022
Merged
6 changes: 6 additions & 0 deletions packages/dart/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## [3.1.7](https://github.com/parse-community/Parse-SDK-Flutter/compare/dart-3.1.6...dart-3.1.7) (2022-12-22)

### Features

* Add `include` option to `getObject` and `fetch` ([#798](https://github.com/parse-community/Parse-SDK-Flutter/issues/798))

## [3.1.6](https://github.com/parse-community/Parse-SDK-Flutter/compare/dart-3.1.5...dart-3.1.6) (2022-12-21)

### Bug Fixes
Expand Down
1 change: 0 additions & 1 deletion packages/dart/lib/parse_server_sdk.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import 'dart:convert';
import 'dart:io';
import 'dart:math';
import 'dart:typed_data';

import 'package:dio/dio.dart';
import 'package:meta/meta.dart';
import 'package:mime_type/mime_type.dart';
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.1.6';
const String keySdkVersion = '3.1.7';
const String keyLibraryName = 'Flutter Parse SDK';

// End Points
Expand Down
14 changes: 0 additions & 14 deletions packages/dart/lib/src/network/parse_query.dart
Original file line number Diff line number Diff line change
Expand Up @@ -435,20 +435,6 @@ class QueryBuilder<T extends ParseObject> {
return queryBuilder;
}

String concatenateArray(List<String> queries) {
String queryBuilder = '';

for (final String item in queries) {
if (item == queries.first) {
queryBuilder += item;
} else {
queryBuilder += ',$item';
}
}

return queryBuilder;
}

/// Creates a query param using the column, the value and the queryOperator
/// that the column and value are being queried against
MapEntry<String, dynamic> _buildQueryWithColumnValueAndOperator(
Expand Down
16 changes: 12 additions & 4 deletions packages/dart/lib/src/objects/parse_object.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,15 @@ class ParseObject extends ParseBase implements ParseCloneable {
late ParseClient _client;

/// Gets an object from the server using it's [String] objectId
Future<ParseResponse> getObject(String objectId) async {
///
/// `List<String>` include refers to other ParseObjects stored as a Pointer
Future<ParseResponse> getObject(String objectId,
{List<String>? include}) async {
try {
final String uri = '$_path/$objectId';
String uri = '$_path/$objectId';
if (include != null) {
uri = '$uri?include=${concatenateArray(include)}';
}
final Uri url = getSanitisedUri(_client, uri);

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

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

final ParseResponse response = await getObject(objectId!);
final ParseResponse response = await getObject(objectId!, include: include);

if (response.success && response.results != null) {
return response.results!.first;
Expand Down
15 changes: 15 additions & 0 deletions packages/dart/lib/src/utils/parse_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,21 @@ bool isDebugEnabled({bool? objectLevelDebug}) {
return objectLevelDebug ?? ParseCoreData().debug;
}

/// Convert list of strings to a string with commas
String concatenateArray(List<String> list) {
String output = '';

for (final String item in list) {
if (item == list.first) {
output += item;
} else {
output += ',$item';
}
}

return output;
}

/// Converts the object to the correct value for JSON,
///
/// Strings are wrapped with "" but integers and others are not
Expand Down
2 changes: 1 addition & 1 deletion packages/dart/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: parse_server_sdk
description: Dart plugin for Parse Server, (https://parseplatform.org), (https://back4app.com)
version: 3.1.6
version: 3.1.7
homepage: https://github.com/parse-community/Parse-SDK-Flutter

environment:
Expand Down
2 changes: 2 additions & 0 deletions packages/dart/test/parse_client_configuration_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:test/test.dart';

void main() {
test('testBuilder', () async {
// arrange
await Parse().initialize(
'appId',
'serverUrl',
Expand All @@ -17,6 +18,7 @@ void main() {
debug: true,
);

// assert
expect(ParseCoreData().applicationId, 'appId');
expect(ParseCoreData().serverUrl, 'serverUrl');
expect(ParseCoreData().clientKey, 'clientKey');
Expand Down
47 changes: 37 additions & 10 deletions packages/dart/test/parse_encoder_test.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:convert';

import 'package:parse_server_sdk/parse_server_sdk.dart';
import 'package:test/test.dart';

Expand All @@ -8,7 +10,7 @@ void main() {
// arrange
await Parse().initialize(
'appId',
'https://test.parse.com',
'https://example.com',
debug: true,
// to prevent automatic detection
fileDirectory: 'someDirectory',
Expand All @@ -20,10 +22,10 @@ void main() {
appVersion: 'someAppVersion',
);

// act
ParseObject parseObject2 = ParseObject("objectId2");
parseObject2.objectId = "objectId2";

// List and Map
parseObject2
.setAdd("dataParseObjectList", ["ListText1", "ListText2", "ListText3"]);
parseObject2.setAdd("dataParseObjectMap", {
Expand All @@ -32,19 +34,44 @@ void main() {
'KeyTestMap3': 'ValueTestMap3',
});

// parseObject2 inside parseObject1
ParseObject parseObject1 = ParseObject("parseObject1");
parseObject1.objectId = "objectId1";
parseObject1.setAdd("dataParseObject2", parseObject2);

// desired output
String expectedResult =
"{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}]}}]}}";

// act
dynamic actualResult = parseEncode(parseObject1, full: true);

//assert
expect(actualResult.toString(), expectedResult);
var objectDesiredOutput = {
"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"
}
]
}
}
],
},
};
var objectJsonDesiredOutput = jsonEncode(objectDesiredOutput);

// assert
expect(jsonEncode(actualResult), objectJsonDesiredOutput);
});
}
185 changes: 185 additions & 0 deletions packages/dart/test/parse_object_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
import 'dart:convert';

import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'package:parse_server_sdk/parse_server_sdk.dart';
import 'package:test/test.dart';
import 'parse_query_test.mocks.dart';

@GenerateMocks([ParseClient])
void main() {
group('parseObject', () {
late MockParseClient client;
setUp(() async {
client = MockParseClient();

await Parse().initialize(
'appId',
'https://example.com',
debug: true,
// to prevent automatic detection
fileDirectory: 'someDirectory',
// to prevent automatic detection
appName: 'appName',
// to prevent automatic detection
appPackageName: 'somePackageName',
// to prevent automatic detection
appVersion: 'someAppVersion',
);
});

test('should return expectedIncludeResult json when use fetch', () async {
// arrange
ParseObject myUserObject = ParseObject("MyUser", client: client);
myUserObject.objectId = "Mn1iJTkWTE";

var desiredOutput = {
"results": [
{
"objectId": "Mn1iJTkWTE",
"phone": "+12025550463",
"createdAt": "2022-09-04T13:35:20.883Z",
"updatedAt": "2022-11-14T10:55:56.202Z",
"img": {
"objectId": "8nGrLj3Mvk",
"size": "67663",
"mime": "image/jpg",
"file": {
"__type": "File",
"name": "dc7320ee9146ee19aed8997722fd4e3c.bin",
"url":
"http://ip:port/api/files/myapp/dc7320ee9146ee19aed8997722fd4e3c.bin",
},
"createdAt": "2022-11-14T10:55:56.025Z",
"updatedAt": "2022-11-14T10:55:56.025Z",
"__type": "Object",
"className": "MyFile",
},
}
]
};

when(client.get(
any,
options: anyNamed("options"),
onReceiveProgress: anyNamed("onReceiveProgress"),
)).thenAnswer((_) async => ParseNetworkResponse(
statusCode: 200, data: jsonEncode(desiredOutput)));

// act
ParseObject parseObject = await myUserObject.fetch(include: ["img"]);

var objectDesiredOutput = {
"className": "MyFile",
"objectId": "8nGrLj3Mvk",
"createdAt": "2022-11-14T10:55:56.025Z",
"updatedAt": "2022-11-14T10:55:56.025Z",
"size": "67663",
"mime": "image/jpg",
"file": {
"__type": "File",
"name": "dc7320ee9146ee19aed8997722fd4e3c.bin",
"url":
"http://ip:port/api/files/myapp/dc7320ee9146ee19aed8997722fd4e3c.bin",
},
};
var objectJsonDesiredOutput = jsonEncode(objectDesiredOutput);

final Uri result = Uri.parse(verify(client.get(
captureAny,
options: anyNamed("options"),
onReceiveProgress: anyNamed("onReceiveProgress"),
)).captured.single);

// assert
expect(
jsonEncode(
parseEncode(parseObject.get<ParseObject>('img'), full: true)),
objectJsonDesiredOutput);
expect(parseObject['img'].objectId, "8nGrLj3Mvk");

expect(Uri.decodeComponent(result.path),
'/classes/MyUser/Mn1iJTkWTE?include=img');
});

test('should return expectedIncludeResult json when use getObject',
() async {
// arrange
ParseObject myUserObject = ParseObject("MyUser", client: client);
myUserObject.objectId = "Mn1iJTkWTE";

var desiredOutput = {
"results": [
{
"objectId": "Mn1iJTkWTE",
"phone": "+12025550463",
"createdAt": "2022-09-04T13:35:20.883Z",
"updatedAt": "2022-11-14T10:55:56.202Z",
"img": {
"objectId": "8nGrLj3Mvk",
"size": "67663",
"mime": "image/jpg",
"file": {
"__type": "File",
"name": "dc7320ee9146ee19aed8997722fd4e3c.bin",
"url":
"http://ip:port/api/files/myapp/dc7320ee9146ee19aed8997722fd4e3c.bin",
},
"createdAt": "2022-11-14T10:55:56.025Z",
"updatedAt": "2022-11-14T10:55:56.025Z",
"__type": "Object",
"className": "MyFile",
},
}
]
};

when(client.get(
any,
options: anyNamed("options"),
onReceiveProgress: anyNamed("onReceiveProgress"),
)).thenAnswer((_) async => ParseNetworkResponse(
statusCode: 200, data: jsonEncode(desiredOutput)));

// act
ParseResponse response =
await myUserObject.getObject("Mn1iJTkWTE", include: ["img"]);

ParseObject parseObject = response.results?.first;

var objectDesiredOutput = {
"className": "MyFile",
"objectId": "8nGrLj3Mvk",
"createdAt": "2022-11-14T10:55:56.025Z",
"updatedAt": "2022-11-14T10:55:56.025Z",
"size": "67663",
"mime": "image/jpg",
"file": {
"__type": "File",
"name": "dc7320ee9146ee19aed8997722fd4e3c.bin",
"url":
"http://ip:port/api/files/myapp/dc7320ee9146ee19aed8997722fd4e3c.bin",
},
};
var objectJsonDesiredOutput = jsonEncode(objectDesiredOutput);

final Uri result = Uri.parse(verify(client.get(
captureAny,
options: anyNamed("options"),
onReceiveProgress: anyNamed("onReceiveProgress"),
)).captured.single);

// assert
expect(response.results?.first, isA<ParseObject>());

expect(
jsonEncode(
parseEncode(parseObject.get<ParseObject>('img'), full: true)),
objectJsonDesiredOutput);
expect(parseObject['img'].objectId, "8nGrLj3Mvk");

expect(Uri.decodeComponent(result.path),
'/classes/MyUser/Mn1iJTkWTE?include=img');
});
});
}
Loading