Skip to content

Commit

Permalink
feat: Add include option to getObject and fetch (#798)
Browse files Browse the repository at this point in the history
  • Loading branch information
mbfakourii authored Dec 22, 2022
1 parent 2befe18 commit e2d0e4e
Show file tree
Hide file tree
Showing 15 changed files with 498 additions and 206 deletions.
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

0 comments on commit e2d0e4e

Please sign in to comment.