platform/packages/mongo/lib/mongo_service.dart

236 lines
7.1 KiB
Dart
Raw Permalink Normal View History

2023-12-24 16:10:10 +00:00
part of 'services.dart';
2016-05-09 22:51:07 +00:00
2016-06-22 18:34:28 +00:00
/// Manipulates data from MongoDB as Maps.
2018-10-18 22:58:03 +00:00
class MongoService extends Service<String, Map<String, dynamic>> {
2016-05-09 22:51:07 +00:00
DbCollection collection;
2017-02-20 16:00:42 +00:00
/// If set to `true`, clients can remove all items by passing a `null` `id` to `remove`.
///
/// `false` by default.
final bool allowRemoveAll;
2017-02-23 01:03:30 +00:00
/// If set to `true`, parameters in `req.query` are applied to the database query.
final bool allowQuery;
2021-02-16 02:10:09 +00:00
MongoService(this.collection,
2022-02-27 01:19:15 +00:00
{this.allowRemoveAll = false, this.allowQuery = true})
2017-02-20 16:00:42 +00:00
: super();
2016-06-22 18:34:28 +00:00
2021-06-18 11:10:38 +00:00
SelectorBuilder? _makeQuery([Map<String, dynamic>? params_]) {
2021-02-16 02:10:09 +00:00
var params = Map.from(params_ ?? {});
2017-02-23 01:03:30 +00:00
params = params..remove('provider');
2021-02-16 02:10:09 +00:00
var result = where.exists('_id');
2017-02-23 01:03:30 +00:00
// You can pass a SelectorBuilder as 'query';
if (params['query'] is SelectorBuilder) {
2021-06-18 11:10:38 +00:00
return params['query'] as SelectorBuilder?;
2017-02-23 01:03:30 +00:00
}
for (var key in params.keys) {
if (key == r'$sort' ||
key == r'$query' &&
(allowQuery == true || !params.containsKey('provider'))) {
if (params[key] is Map) {
// If they send a map, then we'll sort by every key in the map
2021-02-16 02:10:09 +00:00
for (var fieldName in params[key].keys.where((x) => x is String)) {
2017-02-23 01:03:30 +00:00
var sorter = params[key][fieldName];
2021-02-16 02:10:09 +00:00
if (sorter is num && fieldName is String) {
2017-02-23 01:03:30 +00:00
result = result.sortBy(fieldName, descending: sorter == -1);
2021-02-16 02:10:09 +00:00
} else if (sorter is String && fieldName is String) {
result = result.sortBy(fieldName, descending: sorter == '-1');
2017-02-23 01:03:30 +00:00
} else if (sorter is SelectorBuilder) {
result = result.and(sorter);
}
}
} else if (params[key] is String && key == r'$sort') {
// If they send just a string, then we'll sort
// by that, ascending
2018-10-18 22:58:03 +00:00
result = result.sortBy(params[key] as String);
2017-02-23 01:03:30 +00:00
}
} else if (key == 'query' &&
(allowQuery == true || !params.containsKey('provider'))) {
2021-06-18 11:10:38 +00:00
var query = params[key] as Map?;
2019-09-02 17:05:20 +00:00
query?.forEach((key, v) {
2018-10-18 22:58:03 +00:00
var value = v is Map<String, dynamic> ? _filterNoQuery(v) : v;
2017-02-23 01:03:30 +00:00
2022-02-27 01:19:15 +00:00
if (!_noQuery.contains(key) &&
2017-02-23 01:03:30 +00:00
value is! RequestContext &&
value is! ResponseContext) {
2018-10-18 22:58:03 +00:00
result = result.and(where.eq(key as String, value));
2017-02-23 01:03:30 +00:00
}
});
}
}
return result;
}
2018-10-18 22:58:03 +00:00
Map<String, dynamic> _jsonify(Map<String, dynamic> doc,
2021-06-18 11:10:38 +00:00
[Map<String, dynamic>? params]) {
2018-10-18 22:58:03 +00:00
var result = <String, dynamic>{};
2017-02-13 01:38:24 +00:00
2016-05-09 22:51:07 +00:00
for (var key in doc.keys) {
2017-02-13 01:38:24 +00:00
var value = doc[key];
if (value is ObjectId) {
2024-06-02 02:37:49 +00:00
result[key] = value.oid;
2017-02-13 01:38:24 +00:00
} else if (value is! RequestContext && value is! ResponseContext) {
result[key] = value;
}
2016-05-09 22:51:07 +00:00
}
2016-06-22 18:34:28 +00:00
return _transformId(result);
2016-05-09 22:51:07 +00:00
}
@override
2018-10-18 22:58:03 +00:00
Future<List<Map<String, dynamic>>> index(
2021-06-18 11:10:38 +00:00
[Map<String, dynamic>? params]) async {
2021-06-18 10:17:13 +00:00
return await (collection.find(_makeQuery(params)))
2016-06-22 18:34:28 +00:00
.map((x) => _jsonify(x, params))
2016-05-09 22:51:07 +00:00
.toList();
}
2024-06-23 03:15:41 +00:00
// Deprecated:
//static const String _nonceKey = '__angel__mongo__nonce__key__';
2017-07-09 18:07:02 +00:00
2016-05-09 22:51:07 +00:00
@override
2018-10-18 22:58:03 +00:00
Future<Map<String, dynamic>> create(Map<String, dynamic> data,
2021-06-18 11:10:38 +00:00
[Map<String, dynamic>? params]) async {
2018-10-18 22:58:03 +00:00
var item = _removeSensitive(data);
2016-05-09 22:51:07 +00:00
try {
2024-06-23 03:15:41 +00:00
if (params == null || params.isEmpty) {
var result = await collection.insertOne(data);
return _jsonify(result.document ?? {});
} else {
// Deprecated:
// var nonce = (await collection.db.getNonce())['nonce'] as String?;
var result = await collection.findAndModify(
query: _makeQuery(params),
update: item,
returnNew: true,
upsert: true);
return _jsonify(result ?? {});
}
2016-06-22 18:34:28 +00:00
} catch (e, st) {
throw ProtevusHttpException(stackTrace: st);
2016-05-09 22:51:07 +00:00
}
2016-06-22 18:34:28 +00:00
}
2016-05-09 22:51:07 +00:00
2018-12-10 19:19:20 +00:00
@override
Future<Map<String, dynamic>> findOne(
2021-06-18 11:10:38 +00:00
[Map<String, dynamic>? params,
2018-12-10 19:19:20 +00:00
String errorMessage =
'No record was found matching the given query.']) async {
var found = await collection.findOne(_makeQuery(params));
if (found == null) {
throw ProtevusHttpException.notFound(message: errorMessage);
2018-12-10 19:19:20 +00:00
}
return _jsonify(found, params);
}
2016-06-22 18:34:28 +00:00
@override
2018-10-18 22:58:03 +00:00
Future<Map<String, dynamic>> read(String id,
2021-06-18 11:10:38 +00:00
[Map<String, dynamic>? params]) async {
2023-12-24 16:10:10 +00:00
var localId = _makeId(id);
2021-06-18 11:10:38 +00:00
var found =
2023-12-24 16:10:10 +00:00
await collection.findOne(where.id(localId).and(_makeQuery(params)!));
2016-05-09 22:51:07 +00:00
if (found == null) {
throw ProtevusHttpException.notFound(
2024-06-02 02:37:49 +00:00
message: 'No record found for ID ${localId.oid}');
2016-05-09 22:51:07 +00:00
}
2016-06-22 18:34:28 +00:00
return _jsonify(found, params);
2016-05-09 22:51:07 +00:00
}
2016-06-22 18:34:28 +00:00
2018-12-10 19:19:20 +00:00
@override
Future<List<Map<String, dynamic>>> readMany(List<String> ids,
2021-06-18 11:10:38 +00:00
[Map<String, dynamic>? params]) async {
2018-12-10 19:19:20 +00:00
var q = _makeQuery(params);
2021-06-18 11:10:38 +00:00
q = ids.fold(q, (q, id) => q!.or(where.id(_makeId(id))));
2021-06-18 10:17:13 +00:00
return await (collection.find(q)).map((x) => _jsonify(x, params)).toList();
2018-12-10 19:19:20 +00:00
}
2016-06-22 18:34:28 +00:00
@override
2018-10-18 22:58:03 +00:00
Future<Map<String, dynamic>> modify(String id, data,
2021-06-18 11:10:38 +00:00
[Map<String, dynamic>? params]) async {
2024-06-23 03:15:41 +00:00
Map<String, dynamic> currentDoc;
2017-04-10 02:28:29 +00:00
try {
2024-06-23 03:15:41 +00:00
currentDoc = await read(id, params);
} on ProtevusHttpException catch (e) {
2021-06-18 10:17:13 +00:00
if (e.statusCode == 404) {
2017-04-10 02:28:29 +00:00
return await create(data, params);
2021-06-18 10:17:13 +00:00
} else {
2017-04-10 02:28:29 +00:00
rethrow;
2021-06-18 10:17:13 +00:00
}
2017-04-10 02:28:29 +00:00
}
2024-06-23 03:15:41 +00:00
var updatedDoc = mergeMap([currentDoc, _removeSensitive(data)]);
updatedDoc['updatedAt'] = DateTime.now().toIso8601String();
2016-06-22 18:34:28 +00:00
try {
2024-06-23 03:15:41 +00:00
var modified = await collection.findAndModify(
query: where.id(_makeId(id)), update: updatedDoc, returnNew: true);
var result = _jsonify(modified ?? {}, params);
2024-06-02 02:37:49 +00:00
result['id'] = _makeId(id).oid;
2016-06-22 18:34:28 +00:00
return result;
} catch (e, st) {
2017-07-09 18:07:02 +00:00
//printDebug(e, st, 'MODIFY');
throw ProtevusHttpException(stackTrace: st);
2016-06-22 18:34:28 +00:00
}
}
@override
2018-10-18 22:58:03 +00:00
Future<Map<String, dynamic>> update(String id, Map<String, dynamic> data,
2021-06-18 11:10:38 +00:00
[Map<String, dynamic>? params]) async {
2024-06-23 03:15:41 +00:00
var updatedDoc = _removeSensitive(data);
2017-02-19 12:32:21 +00:00
2024-06-23 03:15:41 +00:00
updatedDoc['updatedAt'] = DateTime.now().toIso8601String();
2016-06-22 18:34:28 +00:00
try {
2024-06-23 03:15:41 +00:00
var updated = await collection.findAndModify(
2017-07-09 18:07:02 +00:00
query: where.id(_makeId(id)),
2024-06-23 03:15:41 +00:00
update: updatedDoc,
2017-07-09 18:07:02 +00:00
returnNew: true,
2024-06-23 03:15:41 +00:00
upsert: true);
var result = _jsonify(updated ?? {}, params);
2024-06-02 02:37:49 +00:00
result['id'] = _makeId(id).oid;
2016-06-22 18:34:28 +00:00
return result;
} catch (e, st) {
2017-07-09 18:07:02 +00:00
//printDebug(e, st, 'UPDATE');
throw ProtevusHttpException(stackTrace: st);
2016-06-22 18:34:28 +00:00
}
}
2016-06-23 04:58:21 +00:00
@override
2018-10-18 22:58:03 +00:00
Future<Map<String, dynamic>> remove(String id,
2021-06-18 11:10:38 +00:00
[Map<String, dynamic>? params]) async {
if (id == 'null') {
2019-04-20 18:59:22 +00:00
// Remove everything...
if (!(allowRemoveAll == true ||
params?.containsKey('provider') != true)) {
throw ProtevusHttpException.forbidden(
2019-04-20 18:59:22 +00:00
message: 'Clients are not allowed to delete all items.');
} else {
await collection.remove(null);
return {};
}
2017-02-20 16:00:42 +00:00
}
2017-07-09 18:07:02 +00:00
// var result = await read(id, params);
2016-06-22 18:34:28 +00:00
2016-06-23 04:58:21 +00:00
try {
2024-06-23 03:15:41 +00:00
var result = await collection.findAndModify(
query: where.id(_makeId(id)), remove: true);
return _jsonify(result ?? {});
2016-06-23 04:58:21 +00:00
} catch (e, st) {
2017-07-09 18:07:02 +00:00
//printDebug(e, st, 'REMOVE');
throw ProtevusHttpException(stackTrace: st);
2016-06-23 04:58:21 +00:00
}
}
2016-05-09 22:51:07 +00:00
}