Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • car-app/appwrite-functions
1 result
Show changes
Showing
with 342 additions and 125 deletions
import 'dart:async';
import 'package:dart_appwrite/dart_appwrite.dart';
import 'package:dart_appwrite/models.dart' as appwrite_models;
import 'package:lib/src/request_response/db_request.dart';
import 'package:lib/src/request_response/db_response.dart';
import 'package:lib/src/exceptions/id_null.dart';
import 'package:lib/src/interface/client_function.dart';
import 'package:lib/src/interface/db_model.dart';
abstract class BaseDatabase<T extends DbModel> extends ClientFunction {
static const String itemDataField = "data";
final T Function(Map<String, dynamic>) itemFromDbMap;
late final Databases databases;
String get databaseId;
String get collectionId;
BaseDatabase({required super.host, required super.projectId, required super.apiKey, required this.itemFromDbMap}) {
databases = Databases(client);
}
Future<DbResponse<T>> create(DbRequest<T> request) async {
var id = request.item.id ?? ID.unique();
var document = await databases.createDocument(databaseId: databaseId, collectionId: collectionId, documentId: id, data: request.item.toDbData(), permissions: request.item.permissions?.dbPermissions);
return _responseFromDocument(document);
}
Future<DbResponse<T>> get(DbRequest<String> request) async {
var document = await databases.getDocument(databaseId: databaseId, collectionId: collectionId, documentId: request.item);
return _responseFromDocument(document);
}
Stream<DbResponse<T>> getFromQueries(DbRequest<Iterable<String>> request) async* {
var queryResult = await databases.listDocuments(databaseId: databaseId, collectionId: collectionId, queries: request.item.toList());
for (var document in queryResult.documents) {
yield _responseFromDocument(document);
}
}
Stream<DbResponse<T>> getAll(DbRequest request) async* {
var queryResults = getFromQueries(DbRequest(item: [], requestingUser: request.requestingUser));
await for (var t in queryResults) {
yield t;
}
}
Future<DbResponse<T>> update(DbRequest<T> request) async {
var id = request.item.id;
if (id == null) {
throw IdNullException(request.item);
}
var document = await databases.updateDocument(databaseId: databaseId, collectionId: collectionId, documentId: id, data: request.item.toDbData(), permissions: request.item.permissions?.dbPermissions);
return _responseFromDocument(document);
}
Future<DbResponse<T>> delete(DbRequest<String> request) async {
var itemToDelete = await get(request);
await databases.deleteDocument(databaseId: databaseId, collectionId: collectionId, documentId: request.item);
return itemToDelete;
}
DbResponse<T> _responseFromDocument(appwrite_models.Document document) {
return DbResponse(item: itemFromDbMap(document.toMap()[itemDataField]));
}
}
import 'dart:async';
import 'package:dart_appwrite/dart_appwrite.dart';
import 'package:dart_appwrite/models.dart' as appwriteModels;
import 'package:lib/lib.dart';
import 'package:lib/src/db_model.dart';
import 'package:lib/src/interface/model.dart';
import 'package:lib/src/interface/client_function.dart';
abstract class DatabaseFunction<T extends Model> extends ClientFunction {
static const String itemDataField = "data";
final T Function(Map<String, dynamic>) itemFromMap;
late final Databases databases;
String get databaseId;
String get collectionId;
DatabaseFunction({required super.host, required super.projectId, required super.apiKey, required this.itemFromMap}) {
databases = Databases(client);
}
Future<DbModel> create(T item, {String? id}) async {
id ??= ID.unique();
var document = await databases.createDocument(databaseId: databaseId, collectionId: collectionId, documentId: id, data: item.toMap());
return _dbModelFromDocument(document);
}
Future<DbModel> get(String id) async {
var document = await databases.getDocument(databaseId: databaseId, collectionId: collectionId, documentId: id);
return _dbModelFromDocument(document);
}
Future<List<DbModel>> getFromQueries(List<String> queries) async {
var queryResult = await databases.listDocuments(databaseId: databaseId, collectionId: collectionId, queries: queries);
List<DbModel> models = [];
for (var document in queryResult.documents) {
models.add(_dbModelFromDocument(document));
}
return models;
}
Future<DbModel> update(String id, T item) async {
var document = await databases.updateDocument(databaseId: databaseId, collectionId: collectionId, documentId: id, data: item.toMap());
return _dbModelFromDocument(document);
}
Future<DbModel> delete (String id) async {
var itemToDelete = await this.get(id);
await databases.deleteDocument(databaseId: databaseId, collectionId: collectionId, documentId: id);
return itemToDelete;
}
DbModel _dbModelFromDocument(appwriteModels.Document document) {
return DbModel(
id: document.$id,
item: itemFromMap(document.toMap()[itemDataField]),
);
}
// update(T1 id, T item);
// delete(T1 id);
}
import 'dart:convert';
import 'package:lib/src/base_database.dart';
import 'package:lib/src/interface/api_endpoint.dart';
import 'package:lib/src/interface/db_model.dart';
import 'package:lib/src/request_response/db_request.dart';
import 'package:lib/src/user_details.dart';
import 'package:lib/src/utils/map_helper.dart';
import 'package:lib/src/utils/variables.dart';
class DbApiEndpoint<T extends DbModel> extends ApiEndpoint {
final BaseDatabase<T> database;
final T Function(Map<String, dynamic>) itemGenerator;
final UserDetails requestingUser;
DbApiEndpoint({required this.database, required this.itemGenerator, required this.requestingUser});
@override
Future<Map<String, dynamic>> handleDelete(context) async {
var id = _idRequestFromContext(context);
var response = await database.delete(id);
return context.res.json(response.item.toResponseData());
}
@override
Future<Map<String, dynamic>> handleGet(context) async {
var id = _idRequestFromContext(context);
var response = await database.get(id);
return context.res.json(response.item.toResponseData());
}
@override
Future<Map<String, dynamic>> handlePost(context) async {
var item = _requestFromContext(context);
var response = await database.create(item);
return context.res.json(response.item.toResponseData());
}
@override
Future<Map<String, dynamic>> handlePut(context) async {
var item = _requestFromContext(context);
var response = await database.update(item);
return context.res.json(response.item.toResponseData());
}
DbRequest<T> _requestFromContext(final context) {
return DbRequest(item: itemGenerator(jsonDecode(context.req.body)), requestingUser: requestingUser);
}
DbRequest<String> _idRequestFromContext(final context) {
var map = jsonDecode(context.req.body);
var id = getRequiredMapItem<String>(RequestResponseVariables.idFieldName, map);
return DbRequest(item: id, requestingUser: requestingUser);
}
}
\ No newline at end of file
import 'package:lib/src/interface/model.dart';
class DbModel<T extends Model> {
final String id;
final T item;
DbModel({required this.id, required this.item});
Map<String, dynamic> toMap() {
return {
'id': id,
'item': item.toMap()
};
}
}
import 'dart:io';
class DbActionForbiddenException implements IOException {
final String action;
final String db;
const DbActionForbiddenException(this.action, this.db);
@override
String toString() {
return "Database $db does not allow action $action";
}
}
import 'dart:io';
class IdNullException<T> implements IOException {
final T item;
IdNullException(this.item);
@override
String toString() {
return "Id of item $item is null, but a non-null value was expected";
}
}
import 'dart:io';
class RequiredArgumentMissing extends IOException {
final String argumentName;
RequiredArgumentMissing(this.argumentName);
@override
String toString() {
return "Required Argument '$argumentName' is missing.";
}
}
\ No newline at end of file
class UnauthorizedException implements Exception {
static const String _message = "You are not authorized to perform this action";
@override
String toString() {
return _message;
}
}
abstract class ApiEndpoint {
Future<Map<String, dynamic>> handleGet(final context);
Future<Map<String, dynamic>> handlePost(final context);
Future<Map<String, dynamic>> handlePut(final context);
Future<Map<String, dynamic>> handleDelete(final context);
}
import 'package:lib/src/utils/permissions.dart';
abstract class DbModel {
String? get id;
Permissions? get permissions;
Map<String, dynamic> toDbData();
Map<String, dynamic> toResponseData();
}
abstract class Model {
Map<String, dynamic> toMap();
static T getRequiredMapItem<T>(String key, Map<String, dynamic> map) {
if (!map.containsKey('latitude')) {
throw ArgumentError.value(map, 'map',
'The supplied map doesn\'t contain the mandatory key `$key`.');
}
return map[key];
}
static T? getOptionalMapItem<T>(String key, Map<String, dynamic> map) {
if (map.containsKey(key)) {
return map[key];
}
return null;
}
}
import 'package:lib/src/user_details.dart';
class DbRequest<T> {
final T _item;
final UserDetails _requestingUser;
T get item => _item;
UserDetails get requestingUser => _requestingUser;
DbRequest({required T item, required UserDetails requestingUser}) :
_item = item,
_requestingUser = requestingUser;
}
\ No newline at end of file
class DbResponse<T> {
final T _item;
T get item => _item;
DbResponse({required T item}) :
_item = item;
}
\ No newline at end of file
class UserDetails {
final String _userId;
final List<String> _teamIds;
String get userId => _userId;
List<String> get teamIds => _teamIds;
UserDetails({required String userId, required List<String> teamIds}) :
_userId = userId,
_teamIds = teamIds;
}
import 'dart:convert';
import 'dart:io';
import 'package:lib/lib.dart';
class DbApiHandler<T extends Model> {
static const String requestMethodPost = "POST";
final DatabaseFunction dbFunction;
final T Function(Map<String, dynamic>) itemFromMap;
DbApiHandler({required this.dbFunction, required this.itemFromMap});
Future<void> handleRequest(final context) async {
var item = itemFromMap(jsonDecode(context.req.body));
if (context.req.method == requestMethodPost) {
var createdModel = await dbFunction.create(item);
return context.res.json(createdModel.toMap());
}
var message = "Method ${context.req.method} not implemented";
context.error(message);
throw HttpException(message, uri: Uri.parse(context.req.url));
}
}
import 'package:lib/src/exceptions/required_argument_missing.dart';
T getRequiredMapItem<T>(String key, Map<String, dynamic> map) {
if (!map.containsKey(key)) {
throw RequiredArgumentMissing(key);
}
return map[key];
}
T? getOptionalMapItem<T>(String key, Map<String, dynamic> map) {
if (map.containsKey(key)) {
return map[key];
}
return null;
}
import 'package:dart_appwrite/dart_appwrite.dart';
class Permission {
static final RegExp _permissionStringRegEx = RegExp(r'((?<action>\w+))\("((?<role>.+))"\)');
final String role;
final String action;
String get dbPermissionString => "$action(\"$role\")";
Permission({required this.role, required this.action});
bool isUserPermitted(String userId, List<String>? userTeamIds) {
// Not a complete check but is enough for this the current use case
return role == Role.any() || role == Role.user(userId) || (userTeamIds != null && userTeamIds.any((teamId) => role == Role.team(teamId)));
}
factory Permission.parse(String permissionString) {
var regexpMatch = _permissionStringRegEx.firstMatch(permissionString)!;
var action = regexpMatch.namedGroup("action")!;
var role = regexpMatch.namedGroup("role")!;
return Permission(
action: action,
role: role,
);
}
}
\ No newline at end of file
import 'package:lib/src/utils/map_helper.dart';
import 'package:lib/src/utils/permission.dart';
import 'package:lib/src/utils/variables.dart';
class Permissions {
final List<Permission> _permissions;
List<String> get dbPermissions => _permissions.map((e) => e.dbPermissionString).toList();
Permissions({List<Permission>? permissions}) :
_permissions = permissions ?? [];
Permissions add(Permission permission) {
_permissions.add(permission);
return this;
}
Permissions addFromPermissionString(String permissionString) {
_permissions.add(Permission.parse(permissionString));
return this;
}
bool isUserPermitted(String userId, String action, List<String>? userTeamIds) {
return _permissions.any((permission) => action == permission.action && permission.isUserPermitted(userId, userTeamIds));
}
factory Permissions.fromDbData(Map<String, dynamic> map) {
var dbPermissions = getRequiredMapItem<Iterable>(DatabaseVariables.permissionsColumnName, map);
return Permissions(
permissions: dbPermissions.map((e) => Permission.parse(e)).toList(),
);
}
}
import 'dart:io';
import 'package:lib/lib.dart';
class RequestHandler<T extends DbModel> {
static const String requestMethodGet = "GET";
static const String requestMethodPost = "POST";
static const String requestMethodPut = "PUT";
static const String requestMethodDelete = "DELETE";
final ApiEndpoint apiEndpoint;
RequestHandler({required this.apiEndpoint});
Future<Map<String, dynamic>> handleRequest(final context) async {
var requestMethod = context.req.method;
switch (requestMethod) {
case requestMethodGet:
return apiEndpoint.handleGet(context);
case requestMethodPost:
return apiEndpoint.handlePost(context);
case requestMethodPut:
return apiEndpoint.handlePut(context);
case requestMethodDelete:
return apiEndpoint.handleDelete(context);
default:
var message = "Method ${context.req.method} not implemented";
context.error(message);
throw HttpException(message, uri: Uri.parse(context.req.url));
}
}
}
import 'dart:io';
String get projectId => Platform.environment['APPWRITE_FUNCTION_PROJECT_ID'] ?? "";
class AppwriteVariables {
static String get projectId => Platform.environment['APPWRITE_FUNCTION_PROJECT_ID'] ?? "";
static String userId(final context) {
return context.req.headers['x-appwrite-user-id'];
}
static String trigger(final context) {
return context.req.headers['x-appwrite-trigger'];
}
}
dynamic httpContext; // DON'T USE IN PRODUCTION CODE; only for debugging
class DatabaseVariables {
static String get idColumnName => "\$id";
static String get permissionsColumnName => "\$permissions";
}
class RequestResponseVariables {
static String get idFieldName => "id";
}