Skip to content

Commit

Permalink
feat: implement fetch method #1
Browse files Browse the repository at this point in the history
Retrieves multiple items from the database based on the
provided (optional) filters.
  • Loading branch information
yeikel16 committed Jan 21, 2022
1 parent 686062b commit de8b90c
Show file tree
Hide file tree
Showing 2 changed files with 231 additions and 4 deletions.
86 changes: 82 additions & 4 deletions lib/src/deta.dart
@@ -1,6 +1,8 @@
import 'package:deta/src/exceptions.dart';
import 'package:dio/dio.dart';

part 'deta_query.dart';

/// {@template deta}
/// The `Deta` library is the simple way to interact with the
/// services of the free clud on the [Deta](https://docs.deta.sh/) plataform.
Expand Down Expand Up @@ -65,7 +67,58 @@ abstract class DetaBase {

/// Retrieves multiple items from the database based on the
/// provided (optional) filters.
Future<Map<String, dynamic>> fetch(Map<String, dynamic> filters);
///
/// The [query] is list of [DetaQuery]. If omitted, you will get all the
/// items in the database (up to 1mb or max 1000 items).
/// The [limit] of the number of items you want to retreive, min value is 1.
/// The [last] key seen in a previous paginated response.
///
/// Throw [DetaException] if a query is made on the key.
/// Throw [DetaException] if [limit] is less than 1.
///
/// Example:
/// ```dart
/// final result await deta.base('my_base').fetch(
/// query: [DetaQuery('name').equalTo('Jhon')],
/// );
/// ```
///
/// If you have a complex object which contains more objects,
/// and you want to do the search by the parameters of the object
/// that is inside another one, you can access it in a hierarchical way.
///
/// Object User:
///
/// ```json
/// {
/// "key": "user-a",
/// "user": {
/// "username": "bev",
/// "profile": {
/// "age": 22,
/// "active": true,
/// "name": "Beverly"
/// },
/// "likes":["anime", "ramen"],
/// "purchases": 3
/// }
/// }
/// ```
/// You can search by `name` and `age` in `profile` object like this:
///
/// ```dart
/// final result await deta.base('my_base').fetch(
/// query: [
/// DetaQuery('user.profile.age')
/// .equalTo(22).and('user.profile.name').equalTo('Beverly'),
/// ], limit: 10,
/// );
///```
Future<Map<String, dynamic>> fetch({
List<DetaQuery> query = const [],
int limit = 1000,
String last = '',
});

/// Deletes an item from the database.
///
Expand Down Expand Up @@ -319,9 +372,34 @@ class _DetaBase extends DetaBase {
}

@override
Future<Map<String, dynamic>> fetch(Map<String, dynamic> filters) {
// TODO: implement fetch
throw UnimplementedError();
Future<Map<String, dynamic>> fetch({
List<DetaQuery> query = const [],
int limit = 1000,
String last = '',
}) async {
final querys = <Map>[];

if (query.isNotEmpty) {
querys.addAll(query.map((e) => e.query));
}

try {
final response = await dio.post<Map<String, dynamic>>(
'$baseUrl/$apiVersion/${deta.projectId}/$baseName/query',
options: _authorizationHeader(),
data: {'query': querys, 'limit': limit, 'last': last},
);

if (response.data != null) {
// final resultUpdate =
// response.data!.cast<String, Map<String, dynamic>>();

return response.data!;
}
} on DioError catch (e) {
throw _handleError(e);
}
throw const DetaException();
}

Options _authorizationHeader() {
Expand Down
149 changes: 149 additions & 0 deletions test/src/deta_test.dart
Expand Up @@ -12,6 +12,7 @@ void main() {
const tProjectId = 'project_id';
const tProjectKey = 'project_key';
const tBaseName = 'base_name';
const tUrlBase = 'https://database.deta.sh/v1/$tProjectId/$tBaseName';
const tUrl = 'https://database.deta.sh/v1/$tProjectId/$tBaseName/items';

setUpAll(() {
Expand Down Expand Up @@ -686,5 +687,153 @@ void main() {
);
});
});

group('fetch', () {
const key = '100';
const item = {'key': key, 'name': 'deta', 'email': 'hello@deta.sh'};
const response = {
'paging': {'size': 1, 'last': ''},
'items': [item]
};

final detaQuery1 = DetaQuery('name').equalTo('Jhon');
final detaQuery2 = DetaQuery('age').notEqualTo(24);

const limit = 10;
const last = 'last';

test('all items when no parameters are passed', () async {
when(
() => mockDio.post<Map<String, dynamic>>(
'$tUrlBase/query',
options: any(named: 'options'),
data: {
'query': <Map<String, dynamic>>[],
'limit': 1000,
'last': ''
},
),
).thenAnswer(
(_) async => Response(
data: response,
statusCode: 200,
requestOptions: RequestOptions(path: '$tUrlBase/query'),
),
);

final base = tDeta.base(tBaseName);
final result = await base.fetch();

expect(result, equals(response));
});

test('some item when using the `query`', () async {
when(
() => mockDio.post<Map<String, dynamic>>(
'$tUrlBase/query',
options: any(named: 'options'),
data: {
'query': <Map<String, dynamic>>[
detaQuery1.query,
detaQuery2.query
],
'limit': limit,
'last': last
},
),
).thenAnswer(
(_) async => Response(
data: response,
statusCode: 200,
requestOptions: RequestOptions(path: '$tUrlBase/query'),
),
);

final base = tDeta.base(tBaseName);
final result = await base.fetch(
query: [detaQuery1, detaQuery2],
limit: limit,
last: last,
);

expect(result, equals(response));
});

test(
'should throw `DetaException` '
'when `limit` is less than 1', () async {
when(
() => mockDio.post<Map<String, dynamic>>(
'$tUrlBase/query',
options: any(named: 'options'),
data: {'query': <Map<String, dynamic>>[], 'limit': 0, 'last': ''},
),
).thenThrow(
DioError(
requestOptions: RequestOptions(path: '$tUrlBase/query'),
response: Response<Map<String, dynamic>>(
data: <String, dynamic>{
'errors': ['Limit min value 1']
},
statusCode: 400,
requestOptions: RequestOptions(path: '$tUrlBase/query'),
),
error: DioErrorType.response,
),
);

final base = tDeta.base(tBaseName);

expect(
() => base.fetch(limit: 0),
throwsA(
isA<DetaException>()
.having((e) => e.message, 'message', 'Limit min value 1'),
),
);
});

test('should throw `DetaException` when query is made on the key ',
() async {
final wrongQuery = DetaQuery('key').equalTo('key');

when(
() => mockDio.post<Map<String, dynamic>>(
'$tUrlBase/query',
options: any(named: 'options'),
data: {
'query': <Map<String, dynamic>>[wrongQuery.query],
'limit': 1000,
'last': ''
},
),
).thenThrow(
DioError(
requestOptions: RequestOptions(path: '$tUrlBase/query'),
response: Response<Map<String, dynamic>>(
data: <String, dynamic>{
'errors': ['Bad query']
},
statusCode: 400,
requestOptions: RequestOptions(path: '$tUrlBase/query'),
),
error: DioErrorType.response,
),
);

final base = tDeta.base(tBaseName);

expect(
() => base.fetch(query: [wrongQuery]),
throwsA(
isA<DetaException>().having(
(e) => e.message,
'message',
'Bad query',
),
),
);
});
});
});
}

0 comments on commit de8b90c

Please sign in to comment.