diff --git a/CHANGELOG.md b/CHANGELOG.md index e054f50..906bfe3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,12 @@ Refer to [BFastProject](http://bfast.fahamutech.com/docs) for better documentations. Always use latest version +## [5.0.0-beta.0] - 16/08/2022 + +- minimize code to lift out all the implementation to developer +- improve socket factory to be singleton +- start migrate api to functional + ## [4.1.1] - 04/10/2021 ## [4.1.0] - 04/10/2021 diff --git a/LICENSE b/LICENSE index a46de78..c3ae69b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,4 @@ -MIT License - -Copyright (c) 2019 fluttertz +Copyright (c) 2022 SmartStock Company Limited Open Source Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/lib/adapter/auth.dart b/lib/adapter/auth.dart deleted file mode 100644 index 9165338..0000000 --- a/lib/adapter/auth.dart +++ /dev/null @@ -1,37 +0,0 @@ -abstract class AuthAdapter { - Future authenticated({AuthOptions options}); - - Future getEmail(); - - Future getUsername(); - - Future updateUser(Map user, {AuthOptions options}); - - Future getSessionToken(); - - Future getToken(); - - Future currentUser(); - - Future setCurrentUser(T user); - - Future signUp(String username, String password, Map attrs, - {AuthOptions options}); - - Future logIn(String username, String password, {AuthOptions options}); - - Future logOut({AuthOptions options}); - - Future requestPasswordReset(String email, {AuthOptions options}); - -} - -class AuthOptions { - bool useMasterKey; - String sessionToken; - - AuthOptions({bool useMasterKey, String sessionToken}) { - this.useMasterKey = useMasterKey; - this.sessionToken = sessionToken; - } -} diff --git a/lib/adapter/cache.dart b/lib/adapter/cache.dart index c361118..73dfb11 100644 --- a/lib/adapter/cache.dart +++ b/lib/adapter/cache.dart @@ -1,15 +1,13 @@ -import 'package:bfast/adapter/query.dart'; - -abstract class CacheAdapter { - Future set(String identifier, T data, {int dtl}); - - Future get(String identifier); - - Future> keys(); - - Future clearAll(); - - Future remove(String identifier, {bool force}); - - bool cacheEnabled({RequestOptions options}); -} +// +// +// abstract class CacheAdapter { +// Future set(String identifier, T data, {int dtl}); +// +// Future get(String identifier); +// +// Future> keys(); +// +// Future clearAll(); +// +// Future remove(String identifier, {bool force}); +// } diff --git a/lib/adapter/query.dart b/lib/adapter/query.dart deleted file mode 100644 index 4f69c5b..0000000 --- a/lib/adapter/query.dart +++ /dev/null @@ -1,57 +0,0 @@ - - -class CacheOptions { - bool cacheEnable; - int dtl; - Function({String identifier, dynamic data}) freshDataCallback; - - CacheOptions( - {bool cacheEnable, - int dtl, - Function({String identifier, dynamic data}) freshDataCallback}) { - this.cacheEnable = cacheEnable; - this.dtl = dtl; - this.freshDataCallback = freshDataCallback; - } -} - -class FullTextOptions { - String language; - String caseSensitive; - String diacriticSensitive; -} - -class AggregationOptions { - Map group; - Map match; - Map project; - Map sort; - int limit; - int skip; - - AggregationOptions({ - Map group, - Map match, - Map project, - Map sort, - int limit, - int skip, - }) { - this.group = group; - this.match = match; - this.project = project; - this.sort = sort; - this.limit = limit; - this.skip = skip; - } -} - -class RequestOptions extends CacheOptions { - bool useMasterKey; - List returnFields; - - RequestOptions({bool userMasterKey, List returnFields}) { - this.useMasterKey = userMasterKey; - this.returnFields = returnFields; - } -} diff --git a/lib/adapter/realtime.dart b/lib/adapter/realtime.dart new file mode 100644 index 0000000..91631fb --- /dev/null +++ b/lib/adapter/realtime.dart @@ -0,0 +1,10 @@ +import '../options.dart'; + +abstract class RealtimeAdapter { + send(App app,String event); + close(App app, String event); + receive(App app, String event); + closeAll(); + closeAllOfApp(App app); + count(App app); +} diff --git a/lib/adapter/rest.dart b/lib/adapter/rest.dart deleted file mode 100644 index 1d7441f..0000000 --- a/lib/adapter/rest.dart +++ /dev/null @@ -1,43 +0,0 @@ -import 'dart:typed_data'; - -import 'package:bfast/controller/rest.dart'; -import 'package:http/http.dart' as http; - -abstract class RestAdapter { - Future> get(String url, {RestRequestConfig config}); - - Future> delete(String url, {RestRequestConfig config}); - - Future> head(String url, {RestRequestConfig config}); - - Future> options(String url, {RestRequestConfig config}); - - Future> post(String url, {T data, RestRequestConfig config}); - - Future multiPartRequest(String url, ByteBuffer data, - {http.MultipartRequest multipartRequest,RestRequestConfig config}); - - Future> put(String url, - {T data, RestRequestConfig config}); - - Future> patch(String url, - {T data, RestRequestConfig config}); -} - -class RestRequestConfig { - String url; - String method; - String baseURL; - Function uploadProgress; - Map headers; - Map params; - - RestRequestConfig({ - this.headers, - this.baseURL, - this.method, - this.params, - this.uploadProgress, - this.url, - }); -} diff --git a/lib/adapter/storage.dart b/lib/adapter/storage.dart deleted file mode 100644 index 63eea69..0000000 --- a/lib/adapter/storage.dart +++ /dev/null @@ -1,44 +0,0 @@ -//import 'package:bfast/adapter/auth.dart'; -// -//class BFastFile { -// String fileName; -// String fileType; -// BFastFileDataMap data; -// -// BFastFile({String fileName, String fileType, BFastFileDataMap data}) { -// this.data = data; -// this.fileName = fileName; -// this.fileType = fileType; -// } -//} -// -//class BFastFileDataMap { -// String base64; -// -// BFastFileDataMap(String base64) { -// this.base64 = base64; -// } -//} -// -//class FileOptions extends AuthOptions { -// bool forceSecure; -// Function progress; -// -// FileOptions( -// [Function progress, -// bool forceSecure, -// bool useMasterKey, -// String sessionToken]) -// : super(useMasterKey, sessionToken) { -// this.forceSecure = forceSecure; -// this.progress = progress; -// } -//} -// -//class SaveFileResponse { -// String url; -// -// SaveFileResponse(String url) { -// this.url = url; -// } -//} diff --git a/lib/bfast.dart b/lib/bfast.dart deleted file mode 100644 index 65635ae..0000000 --- a/lib/bfast.dart +++ /dev/null @@ -1,70 +0,0 @@ -library bfast; - -import 'package:bfast/adapter/auth.dart'; -import 'package:bfast/adapter/cache.dart'; -import 'package:bfast/bfast_config.dart'; -import 'package:bfast/bfast_functions.dart'; -import 'package:bfast/controller/auth.dart'; -import 'package:bfast/controller/cache.dart'; -import 'package:bfast/controller/rest.dart'; -import 'package:flutter/material.dart'; - -import 'bfast_database.dart'; - -class BFast { - static init(AppCredentials options, - [String appName = BFastConfig.DEFAULT_APP]) { - options.cache = CacheConfigOptions(false); - return BFastConfig.getInstance(options, appName).getAppCredential(appName); - } - - static BFastConfig getConfig( - [String appName = BFastConfig.DEFAULT_APP, AppCredentials config]) { - return BFastConfig.getInstance(config, appName); - } - - static const utils = {"USER_DOMAIN_NAME": '_User'}; - - static BFastDatabase database([String appName = BFastConfig.DEFAULT_APP]) { - return BFastDatabase(appName: appName); - } - - static BFastFunctions functions([String appName = BFastConfig.DEFAULT_APP]) { - return BFastFunctions(appName: appName); - } - -// static StorageAdapter storage([String appName = BFastConfig.DEFAULT_APP]) { -// return StorageController(BFastHttpClientController(), appName: appName); -// } - - static AuthAdapter auth([String appName = BFastConfig.DEFAULT_APP]) { - return new AuthController( - BFastHttpClientController(), - CacheController( - appName, - BFastConfig.getInstance().getCacheDatabaseName( - BFastConfig.getInstance().DEFAULT_AUTH_CACHE_DB_NAME, appName), - BFastConfig.getInstance().getCacheCollectionName('cache', appName)), - appName); - } - - static CacheAdapter cache( - {@required String database, - @required String collection, - String appName = BFastConfig.DEFAULT_APP}) { - return CacheController( - appName, - (database != null) - ? BFastConfig.getInstance().getCacheDatabaseName(database, appName) - : BFastConfig.getInstance().getCacheDatabaseName( - BFastConfig.getInstance().DEFAULT_CACHE_DB_NAME, appName), - (collection != null) - ? BFastConfig.getInstance() - .getCacheCollectionName(collection, appName) - : BFastConfig.getInstance().getCacheCollectionName('cache', appName), - ); - } -} - -// // ignore: camel_case_types -// class bfast with BFast{} \ No newline at end of file diff --git a/lib/bfast_config.dart b/lib/bfast_config.dart deleted file mode 100644 index 4880692..0000000 --- a/lib/bfast_config.dart +++ /dev/null @@ -1,142 +0,0 @@ -import 'package:http/http.dart' as http; - -class BFastConfig { - static String serverUrl; - static String apiKey; - static http.Client client; - - static const DEFAULT_APP = 'DEFAULT'; - static const AUTH_CACHE_NAME = '_current_user_'; - static const AUTH_CACHE_DEFAULT_VALUE = '_empty_'; - String _DEFAULT_DOMAINS_CACHE_DB_NAME = '__domain'; - - get DEFAULT_DOMAINS_CACHE_DB_NAME { - return this._DEFAULT_DOMAINS_CACHE_DB_NAME; - } - - String _DEFAULT_CACHE_DB_NAME = '__cache'; - - get DEFAULT_CACHE_DB_NAME { - return this._DEFAULT_CACHE_DB_NAME; - } - - String _DEFAULT_CACHE_TTL_COLLECTION_NAME = '__cache_ttl'; - - get DEFAULT_CACHE_TTL_COLLECTION_NAME { - return this._DEFAULT_CACHE_TTL_COLLECTION_NAME; - } - - String _DEFAULT_AUTH_CACHE_DB_NAME = '__auth'; - - get DEFAULT_AUTH_CACHE_DB_NAME { - return this._DEFAULT_AUTH_CACHE_DB_NAME; - } - - Map credentials = {}; - - BFastConfig._(); - - static BFastConfig _instance; - - static BFastConfig getInstance( - [AppCredentials config, String appName = BFastConfig.DEFAULT_APP]) { - if (BFastConfig._instance == null) { - BFastConfig._instance = new BFastConfig._(); - } - if (config != null) { - BFastConfig._instance.credentials[appName] = config; - } - return BFastConfig._instance; - } - - AppCredentials getAppCredential([String appName = BFastConfig.DEFAULT_APP]) { - if (this.credentials[appName] == null) { - throw 'The app -> $appName is not initialized'; - } - return this.credentials[appName]; - } - - Map getHeaders(String appName) { - return { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': this.credentials[appName].applicationId - }; - } - - Map getMasterHeaders(String appName) { - return { - 'Content-Type': 'application/json', - 'X-Parse-Application-Id': this.credentials[appName].applicationId, - 'X-Parse-Master-Key': this.credentials[appName].appPassword, - }; - } - - String functionsURL(String path, String appName) { - if (path.startsWith('http') == true) { - return path; - } - if (this.credentials[appName].functionsURL != null && - this.credentials[appName].functionsURL.startsWith('http') == true) { - return this.credentials[appName].functionsURL + path; - } - return 'https://${this.credentials[appName].projectId}-faas.bfast.fahamutech.com$path'; - } - - String databaseURL(String appName, [String suffix = '']) { - if (this.credentials[appName].databaseURL != null && - this.credentials[appName].databaseURL.startsWith('http') == true) { - if (suffix != null) { - return this.credentials[appName].databaseURL + suffix; - } else { - return this.credentials[appName].databaseURL; - } - } - if (suffix != null) { - return 'https://${this.getAppCredential(appName).projectId}-daas.bfast.fahamutech.com/v2$suffix'; - } else { - return 'https://${this.getAppCredential(appName).projectId}-daas.bfast.fahamutech.com/v2'; - } - } - - String getCacheDatabaseName(String name, String appName) { - if (name != null && name.isNotEmpty == true) { - return 'bfast/${this.getAppCredential(appName).projectId}/$appName/$name'; - } else { - return 'bfast/${this.getAppCredential(appName).projectId}/$appName'; - } - } - - String getCacheCollectionName(String name, String appName) { - if (name != null && name != '') { - return '$name/$appName'; - } else { - return 'cache/$appName'; - } - } -} - -class AppCredentials { - String applicationId; - String projectId; - String functionsURL; - String databaseURL; - String appPassword; - CacheConfigOptions cache; - - AppCredentials( - this.applicationId, - this.projectId, { - this.functionsURL, - this.databaseURL, - this.appPassword, - this.cache, - }); -} - -class CacheConfigOptions { - bool enable; - String collection; - String ttlCollection; - - CacheConfigOptions(this.enable, {this.collection, this.ttlCollection}); -} diff --git a/lib/bfast_database.dart b/lib/bfast_database.dart deleted file mode 100644 index 8bd89dc..0000000 --- a/lib/bfast_database.dart +++ /dev/null @@ -1,53 +0,0 @@ -import 'package:bfast/controller/auth.dart'; -import 'package:bfast/controller/cache.dart'; -import 'package:bfast/controller/database.dart'; -import 'package:bfast/controller/rest.dart'; -import 'package:bfast/controller/rules.dart'; -import 'package:bfast/controller/transaction.dart'; - -import 'bfast_config.dart'; - -class BFastDatabase { - String appName; - - BFastDatabase({this.appName = BFastConfig.DEFAULT_APP}); - - DatabaseController domain(String domainName) { - CacheController authCache = CacheController( - this.appName, - BFastConfig.getInstance().getCacheDatabaseName( - BFastConfig.getInstance().DEFAULT_AUTH_CACHE_DB_NAME, - this.appName), - BFastConfig.getInstance() - .getCacheCollectionName('cache', this.appName)); - var restController = BFastHttpClientController(); - var authController = - AuthController(restController, authCache, this.appName); - var rulesController = RulesController(authController); - return DatabaseController(domainName, restController, authController, - rulesController, this.appName); - } - - DatabaseController collection(String collectionName) { - return this.domain(collectionName); - } - - DatabaseController table(String tableName) { - return this.domain(tableName); - } - - TransactionController transaction([bool isNormalBatch]) { - var authCache = CacheController( - this.appName, - BFastConfig.getInstance().getCacheDatabaseName( - BFastConfig.getInstance().DEFAULT_AUTH_CACHE_DB_NAME(), - this.appName), - BFastConfig.getInstance() - .getCacheCollectionName('cache', this.appName)); - var restController = BFastHttpClientController(); - var authController = - AuthController(restController, authCache, this.appName); - var rulesController = RulesController(authController); - return TransactionController(this.appName, restController, rulesController); - } -} diff --git a/lib/bfast_functions.dart b/lib/bfast_functions.dart deleted file mode 100644 index b6ac1e6..0000000 --- a/lib/bfast_functions.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'package:bfast/bfast_config.dart'; -import 'package:bfast/controller/function.dart'; -import 'package:bfast/controller/realtime.dart'; -import 'package:bfast/controller/rest.dart'; - -class BFastFunctions { - String _appName; - - BFastFunctions({String appName = BFastConfig.DEFAULT_APP}) { - this._appName = appName; - } - - FunctionController request(String path) { - return FunctionController(path, BFastHttpClientController(), - appName: this._appName); - } - - /// listen for a realtime event from a bfast::functions - /// @param eventName - /// @param onConnect {function} callback when connection established - /// @param onDisconnect {function} callback when connection terminated - SocketController event(String eventName, - {Function() onConnect, Function() onDisconnect}) { - return new SocketController(eventName, - appName: this._appName, - onConnect: onConnect, - onDisconnect: onDisconnect); - } -} diff --git a/lib/controller/auth.dart b/lib/controller/auth.dart deleted file mode 100644 index 623a3ef..0000000 --- a/lib/controller/auth.dart +++ /dev/null @@ -1,193 +0,0 @@ -import 'package:bfast/adapter/auth.dart'; -import 'package:bfast/adapter/cache.dart'; -import 'package:bfast/adapter/rest.dart'; -import 'package:bfast/controller/rest.dart'; - -import '../bfast_config.dart'; - -class AuthController extends AuthAdapter { - RestAdapter restAdapter; - CacheAdapter cacheAdapter; - String appName; - - AuthController(this.restAdapter, this.cacheAdapter, this.appName); - - @override - Future authenticated({AuthOptions options}) async { - return this.currentUser(); - } - - @override - Future currentUser() async { - var user = await this.cacheAdapter.get(BFastConfig.AUTH_CACHE_NAME); - if (user == BFastConfig.AUTH_CACHE_DEFAULT_VALUE || user == null) { - return null; - } - return user; - } - - @override - Future getEmail() async { - var user = await this.currentUser(); - if (user != null && user['email'] != null) { - return user['email']; - } else { - return null; - } - } - - @deprecated - @override - Future getSessionToken() async { - var user = await this.currentUser(); - if (user != null && user['sessionToken'] != null) { - return user['sessionToken']; - } else { - return null; - } - } - - @override - Future getToken() async { - var user = await this.currentUser(); - if (user != null && user['token'] != null) { - return user['token']; - } else { - return null; - } - } - - @override - Future getUsername() async { - var user = await this.currentUser(); - if (user != null && user['username'] != null) { - return user['username']; - } else { - return null; - } - } - - @override - Future logIn(String username, String password, {AuthOptions options}) async { - RestRequestConfig config = RestRequestConfig(); - config.headers = BFastConfig.getInstance().getHeaders(this.appName); - var authRule = {}; - authRule.addAll({ - 'applicationId': BFastConfig.getInstance() - .getAppCredential(this.appName) - .applicationId, - "auth": { - "signIn": {"username": username, "password": password} - } - }); - RestResponse response = await this.restAdapter.post( - BFastConfig.getInstance().databaseURL(this.appName), - data: authRule, - config: config); - var data = response.data; - if (data != null && - data['auth'] != null && - data['auth']['signIn'] != null) { - await this - .cacheAdapter - .set(BFastConfig.AUTH_CACHE_NAME, data['auth']['signIn'], dtl: 7); - return data['auth']['signIn']; - } else { - throw { - "message": data['errors'] != null && - data['errors']['auth'] != null && - data['errors']['auth']['signIn'] != null - ? data['errors']['auth']['signIn']['message'] - : 'Fails to login' - }; - } - } - - @override - Future logOut({AuthOptions options}) async { - await this - .cacheAdapter - .set(BFastConfig.AUTH_CACHE_NAME, BFastConfig.AUTH_CACHE_DEFAULT_VALUE); - return true; - } - - @override - Future requestPasswordReset(String email, {AuthOptions options}) async { - var authRule = {}; - authRule.addAll({ - 'applicationId': - BFastConfig.getInstance().getAppCredential(this.appName).applicationId - }); - authRule.addAll({ - "auth": { - "reset": {"email": email} - } - }); - RestResponse response = await this.restAdapter.post( - BFastConfig.getInstance().databaseURL(this.appName), - data: authRule); - var data = response.data; - if (data != null && data['auth'] != null && data['auth']['reset'] != null) { - return data['auth']['reset']; - } else { - throw { - "message": data['errors'] != null && - data['errors']['auth'] != null && - data['errors']['auth']['reset'] != null - ? data['errors']['auth']['reset']['message'] - : 'Fails to reset password' - }; - } - } - - @override - Future setCurrentUser(user) async { - await this.cacheAdapter.set(BFastConfig.AUTH_CACHE_NAME, - user == null ? BFastConfig.AUTH_CACHE_DEFAULT_VALUE : user, - dtl: 6); - return user; - } - - @override - Future signUp(String username, String password, Map attrs, - {AuthOptions options}) async { - var authRule = {}; - authRule.addAll({ - 'applicationId': - BFastConfig.getInstance().getAppCredential(this.appName).applicationId - }); - attrs.addAll({"username": username, "password": password}); - attrs['email'] = attrs['email'] ? attrs['email'] : ''; - authRule.addAll({ - "auth": {"signUp": attrs} - }); - RestResponse response = await this.restAdapter.post( - BFastConfig.getInstance().databaseURL(this.appName), - data: authRule); - var data = response.data; - if (data != null && - data['auth'] != null && - data['auth']['signUp'] != null) { - await this - .cacheAdapter - .set(BFastConfig.AUTH_CACHE_NAME, data.auth.signUp, dtl: 7); - return data['auth']['signUp']; - } else { - throw { - "message": data['errors'] != null && - data['errors']['auth'] != null && - data['errors']['auth']['signUp'] != null - ? data['errors']['auth']['signUp']['message'] - : 'Fails to signUp' - }; - } - } - - @override - Future updateUser(Map userModel, {AuthOptions options}) async { - throw { - "message": - "Not supported, use _User collection in your secure env with masterKey to update user details" - }; - } -} diff --git a/lib/controller/cache.dart b/lib/controller/cache.dart index d238fa4..dbd3cf2 100644 --- a/lib/controller/cache.dart +++ b/lib/controller/cache.dart @@ -1,126 +1,12 @@ -import 'package:bfast/adapter/cache.dart'; -import 'package:bfast/adapter/query.dart'; -import 'package:flutter/foundation.dart'; -import 'package:path/path.dart'; -import 'package:sembast/sembast.dart'; -import 'package:sembast/sembast_io.dart'; -import 'package:sembast_web/sembast_web.dart'; -import 'package:sqflite/sqflite.dart' as sqflite; - -import '../bfast_config.dart'; - -class CacheController extends CacheAdapter { - String _database; - String _collection; - String _appName; - - CacheController(this._appName, this._database, this._collection); - - Future _getCacheDatabase() async { - var databaseFactory = databaseFactoryIo; - StoreRef storeRef = stringMapStoreFactory.store(this._collection); - if (kIsWeb == true) { - databaseFactory = databaseFactoryWeb; - String dbPath = '${this._database}'; - Database db = await databaseFactory.openDatabase(dbPath); - return DatabaseInstance(db, storeRef); - } else { - var databasePah = await sqflite.getDatabasesPath(); - String dbPath = join(databasePah, '${this._database}.db'); - Database db = await databaseFactory.openDatabase(dbPath); - return DatabaseInstance(db, storeRef); - } - } - - // Future _getTTLStore() async { - // if (kIsWeb == true) { - // var databaseFactory = databaseFactoryWeb; - // String dbPath = '${this._database}_ttl_'; - // Database db = await databaseFactory.openDatabase(dbPath); - // StoreRef storeRef = stringMapStoreFactory.store(this._collection); - // return DatabaseInstance(db, storeRef); - // } else { - // var databaseFactory = - // databaseFactoryIo; //getDatabaseFactorySqflite(sqflite.databaseFactory); // - // var databasePah = await sqflite.getDatabasesPath(); - // String dbPath = join(databasePah, '${this._database}_ttl_.db'); - // StoreRef storeRef = stringMapStoreFactory.store(this._collection); - // Database db = await databaseFactory.openDatabase(dbPath); - // return DatabaseInstance(db, storeRef); - // } - // } - - // static int _getDayToLeave(int days) { - // DateTime date = new DateTime.now(); - // return date.millisecondsSinceEpoch + (days * 24 * 60 * 60 * 1000); - // } - - @override - bool cacheEnabled({RequestOptions options}) { - if (options != null && - options.cacheEnable != null && - options.cacheEnable is bool) { - return options.cacheEnable == true; - } else { - return BFastConfig.getInstance() - .getAppCredential(this._appName) - .cache - ?.enable == - true; - } - } - - @override - Future clearAll() async { - DatabaseInstance databaseInstance = await this._getCacheDatabase(); - await databaseInstance.store.delete(databaseInstance.db); - await databaseInstance.db.close(); - return true; - } - - @override - Future get(String identifier) async { - DatabaseInstance databaseInstance = await this._getCacheDatabase(); - var res = await databaseInstance.store - .record(identifier) - .get(databaseInstance.db); - await databaseInstance.db.close(); - return res as T; - } - - @override - Future> keys() async { - DatabaseInstance databaseInstance = await this._getCacheDatabase(); - var keys = await databaseInstance.store.findKeys(databaseInstance.db); - await databaseInstance.db.close(); - return keys as List; - } - - @override - Future remove(String identifier, {bool force}) async { - DatabaseInstance databaseInstance = await this._getCacheDatabase(); - await databaseInstance.store.record(identifier).delete(databaseInstance.db); - await databaseInstance.db.close(); - return true; - } - - @override - Future set(String identifier, T data, {int dtl}) async { - DatabaseInstance databaseInstance = await this._getCacheDatabase(); - var v1 = await databaseInstance.store - .record(identifier) - .put(databaseInstance.db, data, merge: true); - await databaseInstance.db.close(); - if (v1 != null) { - return data; - } - throw "Fail to save data"; - } -} - -class DatabaseInstance { - Database db; - StoreRef store; - - DatabaseInstance(this.db, this.store); -} +// import '../options._test.dart'; +// +// Future Function(dynamic x) clearAllCache(Function(String c) fn) => (x) => fn(x); +// +// Future Function(dynamic x) getCache(Function(String id) fn) => (x) => fn(x); +// +// Future Function(dynamic x) cacheKeys(Function(String c) fn) => +// (x) => fn(x); +// +// Future Function(dynamic x) removeCache(Function(String i) fn) => (x) => fn(x); +// +// setCache(App app) => (Function() fn)=> diff --git a/lib/controller/database.dart b/lib/controller/database.dart index a78fde0..3cd6b88 100644 --- a/lib/controller/database.dart +++ b/lib/controller/database.dart @@ -1,109 +1,26 @@ -import 'package:bfast/adapter/auth.dart'; -import 'package:bfast/adapter/cache.dart'; -import 'package:bfast/adapter/query.dart'; -import 'package:bfast/adapter/rest.dart'; -import 'package:bfast/controller/query.dart'; -import 'package:bfast/controller/realtime.dart'; -import 'package:bfast/controller/rest.dart'; -import 'package:bfast/controller/rules.dart'; - -import '../bfast_config.dart'; - -class DatabaseController { - String domainName; - CacheAdapter cacheAdapter; - AuthAdapter authAdapter; - RulesController rulesController; - RestAdapter restAdapter; - String appName; - - DatabaseController(this.domainName, this.restAdapter, this.authAdapter, - this.rulesController, this.appName); - - Future get(String objectId, [RequestOptions options]) async { - try { - return await this.query().byId(objectId).find(options: options); - } catch (e) { - throw {"message": DatabaseController.getErrorMessage(e)}; - } - } - - Future> getAll( - {int size, int skip, RequestOptions options}) async { - try { - var number = size!=null - ? size - : await this.query().count(true).find(options: options); - return this - .query() - .skip(skip != null ? skip : 0) - .size(number) - .find(options: options); - } catch (e) { - throw {"message": DatabaseController.getErrorMessage(e)}; - } - } - - QueryController query() { - return QueryController( - this.domainName, this.restAdapter, this.rulesController, this.appName); - } - - Future save(T model, [RequestOptions options]) async { - var createRule = await this.rulesController.createRule( - this.domainName, - model, - BFastConfig.getInstance().getAppCredential(this.appName), - options); - RestResponse response = await this.restAdapter.post( - BFastConfig.getInstance().databaseURL(this.appName), - data: createRule); - return DatabaseController.extractResultFromServer( - response.data, 'create', this.domainName); - } - - static extractResultFromServer(dynamic data, String rule, String domain) { - if (data != null && data['$rule$domain'] != null) { - return data['$rule$domain']; - } else { - if (data != null && - data['errors'] != null && - data['errors']['$rule'] != null && - data['errors']['$rule'][domain] != null) { - throw data['errors']['$rule']['$domain']; - } else { - throw {"message": 'Server general failure', "errors": data['errors']}; - } - } - } - - static dynamic getErrorMessage(dynamic e) { - if (e['message'] != null) { - return e['message']; - } else { - return (e != null && - e['response'] != null && - e['response']['data'] != null) - ? e['response']['data'] - : e['toString'](); - } - } +import 'dart:convert'; + +import '../model/raw_response.dart'; +import '../util.dart'; + +// ((a) -> b) -> map(b.body-> Map) +executeRule(Function() httpPost) async { + RawResponse response = await httpPost(); + var isOk = (_) => '${response.statusCode}'.startsWith('20'); + var exposeErr = compose([ + map((m) => {'errors': m}), + jsonDecode + ]); + var mapOfResult = ifDoElse(isOk, jsonDecode, exposeErr); + return mapOfResult(response.body); } -class DatabaseChangesController { - SocketController socketController; +// this will be running in the app specific code +// var ruleResult = compose([ifThrow((x)=>errorsMessage(x)!='', (x)=>x)]); - DatabaseChangesController(this.socketController); +_errorIsMap(e) => ifDoElse((_) => e['errors'] != null, + (_) => jsonEncode(e['errors']), (_) => '')(''); - addListener(dynamic Function(dynamic response) handler) { - this.socketController.listener(handler); - } - - close() { - this.socketController.close(); - } - - open() { - this.socketController.open(); - } -} +// * -> String +errorsMessage(dynamic e) => + ifDoElse((_) => e is Map, _errorIsMap, (_) => '')(e); \ No newline at end of file diff --git a/lib/controller/function.dart b/lib/controller/function.dart index 31b4197..895df87 100644 --- a/lib/controller/function.dart +++ b/lib/controller/function.dart @@ -1,93 +1,12 @@ -import 'dart:async'; +import 'package:bfast/controller/database.dart'; +import 'package:bfast/model/raw_response.dart'; -import 'package:bfast/adapter/rest.dart'; -import 'package:bfast/bfast_config.dart'; -import 'package:bfast/controller/rest.dart'; +import '../util.dart'; -class FunctionController { - String functionPath; - String appName; - RestAdapter restAdapter; +// ((a) -> b) -> map(b.body -> Map) +// impure because it throw error. +Future Function(RawResponse Function() httpRequest) executeHttp = composeAsync([ + ifThrow((x) => errorsMessage(x) != '', (x) => errorsMessage(x)), + executeRule +]); - FunctionController(String functionPath, RestAdapter restAdapter, - {String appName = BFastConfig.DEFAULT_APP}) { - this.functionPath = functionPath; - this.restAdapter = restAdapter; - this.appName = appName; - } - - Future delete([RestRequestConfig config]) async { - RestRequestConfig deleteConfig = RestRequestConfig(); - if (config != null && config.headers != null) { - deleteConfig = config; - } else { - deleteConfig.headers = BFastConfig.getInstance().getHeaders(this.appName); - deleteConfig.params = config?.params; - deleteConfig.url = config?.url; - deleteConfig.method = config?.method; - deleteConfig.baseURL = config?.baseURL; - } - RestResponse response = await this.restAdapter.delete( - BFastConfig.getInstance().functionsURL(this.functionPath, this.appName), - config: deleteConfig); - return response.data; - } - - Future get([RestRequestConfig config]) async { - RestRequestConfig getConfig = RestRequestConfig(); - if (config != null && config.headers != null) { - getConfig = config; - } else { - getConfig.headers = BFastConfig.getInstance().getHeaders(this.appName); - getConfig.params = config?.params; - getConfig.url = config?.url; - getConfig.method = config?.method; - getConfig.baseURL = config?.baseURL; - } - RestResponse response = await this.restAdapter.get( - BFastConfig.getInstance().functionsURL(this.functionPath, this.appName), - config: getConfig); - return response.data; - } - - Future post([Map data, RestRequestConfig config]) async { - if (this.functionPath != null && this.functionPath != '') { - RestRequestConfig postConfig = RestRequestConfig(); - if (config != null && config.headers != null) { - postConfig = config; - } else { - postConfig.headers = BFastConfig.getInstance().getHeaders(this.appName); - postConfig.params = config?.params; - postConfig.url = config?.url; - postConfig.method = config?.method; - postConfig.baseURL = config?.baseURL; - } - RestResponse value = await this.restAdapter.post( - BFastConfig.getInstance() - .functionsURL(this.functionPath, this.appName), - data: data != null ? data : {}, - config: postConfig); - return value.data; - } else { - throw {"code": -1, "message": 'Please provide function path'}; - } - } - - Future put([Map data, RestRequestConfig config]) async { - RestRequestConfig putConfig = RestRequestConfig(); - if (config != null && config.headers != null) { - putConfig = config; - } else { - putConfig.headers = BFastConfig.getInstance().getHeaders(this.appName); - putConfig.params = config?.params; - putConfig.url = config?.url; - putConfig.method = config?.method; - putConfig.baseURL = config?.baseURL; - } - RestResponse response = await this.restAdapter.put( - BFastConfig.getInstance().functionsURL(this.functionPath, this.appName), - data: data != null ? data : {}, - config: putConfig); - return response.data; - } -} diff --git a/lib/controller/query.dart b/lib/controller/query.dart deleted file mode 100644 index 05619b2..0000000 --- a/lib/controller/query.dart +++ /dev/null @@ -1,276 +0,0 @@ -import 'package:bfast/adapter/cache.dart'; -import 'package:bfast/adapter/query.dart'; -import 'package:bfast/adapter/rest.dart'; -import 'package:bfast/controller/database.dart'; -import 'package:bfast/controller/realtime.dart'; -import 'package:bfast/controller/rest.dart'; -import 'package:bfast/controller/rules.dart'; -import 'package:bfast/controller/update.dart'; -import 'package:bfast/model/FullTextModel.dart'; -import 'package:bfast/model/QueryModel.dart'; - -import '../bfast_config.dart'; - -enum QueryOrder { - ASCENDING, - DESCENDING, -} - -class QueryController { - QueryModel _query = QueryModel( - filter: {}, - returnFields: [], - skip: 0, - size: 100, - orderBy: [ - {'createdAt': -1} - ], - count: false, - ); - - String domain; - String appName; - RestAdapter restAdapter; - RulesController rulesController; - CacheAdapter cacheAdapter; - - QueryController( - this.domain, this.restAdapter, this.rulesController, this.appName); - - QueryController byId(String id) { - this._query.id = id; - return this; - } - - QueryController count([bool countQuery = true]) { - this._query.count = countQuery; - return this; - } - - QueryController size(int size) { - this._query.size = size; - return this; - } - - QueryController skip(int skip) { - this._query.skip = skip; - return this; - } - - QueryController orderBy(String field, QueryOrder order) { - var orderBySet = Set.from(this._query.orderBy); - orderBySet.add({ - [field]: order - }); - this._query.orderBy = orderBySet.toList(); - return this; - } - - QueryController equalTo(String field, dynamic value) { - this._query.filter.addAll({ - [field]: {"\$eq": value} - }); - return this; - } - - QueryController notEqualTo(String field, dynamic value) { - this._query.filter.addAll({ - [field]: {"\$ne": value} - }); - return this; - } - - QueryController greaterThan(String field, dynamic value) { - this._query.filter.addAll({ - [field]: {"\$gt": value} - }); - return this; - } - - QueryController greaterThanOrEqual(String field, dynamic value) { - this._query.filter.addAll({ - [field]: {"\$gte": value} - }); - return this; - } - - includesIn(String field, List value) { - this._query.filter.addAll({ - [field]: {"\$in": value} - }); - return this; - } - - QueryController notIncludesIn(String field, List value) { - this._query.filter.addAll({ - [field]: {'\$nin': value} - }); - return this; - } - - QueryController lessThan(String field, List value) { - this._query.filter.addAll({ - [field]: {"\$lt": value} - }); - return this; - } - - QueryController lessThanOrEqual(String field, List value) { - this._query.filter.addAll({ - [field]: {"\$lte": value} - }); - return this; - } - - QueryController exists(String field, [bool value = true]) { - this._query.filter.addAll({ - [field]: {"\$exists": value} - }); - return this; - } - - QueryController searchByRegex(String field, dynamic regex) { - this._query.filter.addAll({ - [field]: {"\$regex": regex} - }); - return this; - } - - QueryController fullTextSearch(String field, FullTextModel text) { - this._query.filter.addAll({ - "\$text": { - "\$search": text.search, - "\$language": text.language, - "\$caseSensitive": text.caseSensitive, - "\$diacriticSensitive": text.diacriticSensitive - } - }); - return this; - } - - QueryController raw(dynamic query) { - this._query.filter = query; - return this; - } - - QueryModel _buildQuery() { - return this._query; - } - - Future delete([RequestOptions options]) async { - var deleteRule = await this.rulesController.deleteRule( - this.domain, - this._buildQuery(), - BFastConfig.getInstance().getAppCredential(this.appName), - options); - var response = await this.restAdapter.post( - BFastConfig.getInstance().databaseURL(this.appName), - data: deleteRule); - return DatabaseController.extractResultFromServer( - response.data, 'delete', this.domain); - } - - UpdateController updateBuilder() { - return new UpdateController(this.domain, this._buildQuery(), this.appName, - this.restAdapter, this.rulesController); - } - - DatabaseChangesController changes( - {Function onConnect, Function onDisconnect, RequestOptions options}) { - String applicationId = - BFastConfig.getInstance().getAppCredential(this.appName).applicationId; - String projectId = - BFastConfig.getInstance().getAppCredential(this.appName).projectId; - String masterKey = - BFastConfig.getInstance().getAppCredential(this.appName).appPassword; - var match = {}; - if (this._buildQuery != null && this._buildQuery().filter != null) { - match = this._buildQuery().filter; - match.keys.forEach((key) { - match['fullDocument.$key'] = match[key]; - match.remove(key); - }); - } - - SocketController socketController; - socketController = new SocketController('/v2/__changes__', - appName: this.appName, onConnect: () { - if (onConnect != null) { - onConnect(); - } - socketController.emit(auth: { - 'applicationId': applicationId, - 'topic': '${projectId}_${this.domain}', - 'masterKey': options.useMasterKey == true ? masterKey : null - }, body: { - 'domain': this.domain, - 'pipeline': match != null - ? [ - {'\$match': match} - ] - : [] - }); - }, onDisconnect: onDisconnect); - return new DatabaseChangesController(socketController); - } - -// ********* need improvement ************ // - Future aggregate(List pipeline, RequestOptions options) async { - var aggregateRule = await this.rulesController.aggregateRule( - this.domain, - pipeline, - BFastConfig.getInstance().getAppCredential(this.appName), - options); - return this.aggregateRuleRequest(aggregateRule); - } - - Future find({RequestOptions options}) async { - var queryRule = await this.rulesController.queryRule( - this.domain, - this._buildQuery(), - BFastConfig.getInstance().getAppCredential(this.appName), - options: options, - ); - return await this._queryRuleRequest(queryRule); - } - - Future _queryRuleRequest(dynamic queryRule) async { - RestResponse response = await this.restAdapter.post( - BFastConfig.getInstance().databaseURL(this.appName), - data: queryRule); - var data = response.data; - if (data != null && data['query${this.domain}'] != null) { - return data['query${this.domain}']; - } else { - Map errors = data['errors']; - var queryError = {"message": "Query not succeed"}; - errors.forEach((key, value) { - if (value.toString().contains('query') == true) { - queryError = errors[value]; - } - }); - queryError.putIfAbsent('errors', () => errors as dynamic); - throw queryError; - } - } - - Future aggregateRuleRequest(dynamic pipelineRule) async { - RestResponse response = await this.restAdapter.post( - BFastConfig.getInstance().databaseURL(this.appName), - data: pipelineRule); - var data = response.data; - if (data != null && data['aggregate${this.domain}'] != null) { - return data['aggregate${this.domain}']; - } else { - Map errors = data['errors']; - var aggregateError = {"message": "Aggregation not succeed"}; - errors.forEach((key, value) { - if (value.toString().contains('aggregate') == true) { - aggregateError = errors[value]; - } - }); - aggregateError['errors'] = errors as dynamic; - throw aggregateError; - } - } -} diff --git a/lib/controller/realtime.dart b/lib/controller/realtime.dart index 8ab86af..d6543ac 100644 --- a/lib/controller/realtime.dart +++ b/lib/controller/realtime.dart @@ -1,59 +1,123 @@ -import 'package:bfast/bfast_config.dart'; -import 'package:flutter/foundation.dart'; +import 'package:bfast/options.dart'; import 'package:socket_io_client/socket_io_client.dart'; -class SocketController { - Socket socket; - - String eventName; - - SocketController(String eventName, - {appName = BFastConfig.DEFAULT_APP, - dynamic Function() onConnect, - dynamic Function() onDisconnect}) { - String path = eventName.length > 0 && eventName[0] == '/' - ? eventName - : '/' + eventName; - var url = path == '/v2/__changes__' - ? BFastConfig.getInstance().databaseURL(appName, path) - : BFastConfig.getInstance().functionsURL(path, appName); - this.socket = io(url, { - 'transports': ['websocket'], - 'autoConnect': false, - 'reconnection': true, - 'reconnectionAttempts': double.infinity, - 'reconnectionDelay': 2000, // how long to initially wait before attempting a new reconnection - 'reconnectionDelayMax': 5000, // maximum amount of time to wait between reconnection attempts. Each attempt increases the reconnection delay by 2x along with a randomization factor - 'randomizationFactor': 0.5, - // 'extraHeaders': {'foo': 'bar'} // optional - }); - this.eventName = eventName; - if (onConnect != null) { - this.socket.on("connect", (_) => onConnect()); - } - if (onDisconnect != null) { - this.socket.on("disconnect", (_) => onDisconnect()); - } - this.open(); - } +import '../adapter/realtime.dart'; +import '../util.dart'; - void emit({dynamic auth, @required dynamic body}) { - this.socket.emit(this.eventName, {"auth": auth, "body": body}); - } +var _socketConfig = { + 'transports': ['websocket'], + 'autoConnect': true, + 'reconnection': true, + 'reconnectionAttempts': double.infinity, + 'reconnectionDelay': 2000, + // how long to initially wait before attempting a new reconnection + 'reconnectionDelayMax': 5000, + // maximum amount of time to wait between reconnection attempts. + // Each attempt increases the reconnection delay by 2x along with a + // randomization factor + 'randomizationFactor': 0.5, + // 'extraHeaders': {'foo': 'bar'} // optional +}; - void listener(dynamic Function(dynamic response) handler) { - this.socket.on(this.eventName, (data) => handler(data)); - } +// ((a) -> b) -> map(b.body -> Map) +// impure because it throw error. +// executeEvent(Function fn1, Function fn2) => (String name) => null; + +bool _isAbsolute(x) => x != null && x.length > 0 && x[0] == '/'; + +bool _isDbChanges(x) => x is String && x == '/v2/__changes__'; + +Function _eventPath = ifDoElse(_isAbsolute, (y) => y, (y) => '/$y'); + +_eventUrl(app) => ifDoElse(_isDbChanges, databaseURL(app), functionsURL(app)); + +_socketKey(app, name) => ifDoElse( + (f) => app is App, + (_) => '${app.projectId}$name', + (_) => name, + )(''); + +// app:App -> event: String -> String +realtimeServerUrl(app) => compose( + [(x) => '$x'.replaceAll('/v2/v2', '/v2'), _eventUrl(app), _eventPath]); + +class RealtimeFactory implements RealtimeAdapter { + static final RealtimeFactory _singleton = RealtimeFactory._internal(); - void close() { - if (this.socket.connected == true) { - this.socket.close(); - } + factory RealtimeFactory() => _singleton; + + RealtimeFactory._internal(); + + Map _socketMap = {}; + + Socket _freshSocket(url) => io(url, _socketConfig); + + _lazyInitializeSocket(app) => ifDoElse( + (_e) => + _socketMap.containsKey(_socketKey(app, _e)) && + _socketMap[_socketKey(app, _e)] != null, + (_e) => _socketMap[_socketKey(app, _e)], + (_e) { + var url = realtimeServerUrl(app)(_e); + var socket = _freshSocket(url); + socket.on( + 'connect', + (data) => + print('INFO:: socket connected on ${_socketKey(app, _e)}')); + socket.on( + 'disconnect', + (data) => + print('INFO:: socket disconnect on ${_socketKey(app, _e)}')); + socket.open(); + _socketMap.putIfAbsent(_socketKey(app, _e), () => socket); + return socket; + }, + ); + + @override + Function(Map x) send(App app, String event) => (Map d) { + var lazySocket = _lazyInitializeSocket(app); + Socket socket = lazySocket(event); + // {"auth": auth, "body": body} -> d + socket.emit(event, d); + }; + + close(App app, String event) => ifDoElse( + (e) => + _socketMap.containsKey(_socketKey(app, e)) && + _socketMap[_socketKey(app, e)] != null, + (e) { + _socketMap[_socketKey(app, e)]?.dispose(); + _socketMap.remove(_socketKey(app, e)); + }, + (e) => null, + )(event); + + receive(App app, String event) => (Function(dynamic x) fn) { + var lazySocket = _lazyInitializeSocket(app); + Socket socket = lazySocket(event); + socket.off(event); + socket.on(event, fn); + }; + + closeAll(){ + _socketMap.keys.forEach((k) { + _socketMap[k]?.dispose(); + }); + _socketMap.removeWhere((key, value) => true); } - void open() { - if (this.socket.disconnected == true) { - this.socket.open(); - } + closeAllOfApp(App app) { + _socketMap.keys + .where((element) => element.startsWith('${app.projectId}')) + .forEach((k) { + _socketMap[k]?.dispose(); + }); + _socketMap.removeWhere((key, value) => key.startsWith('${app.projectId}')); } + + @override + count(App app) => _socketMap.keys + .where((element) => element.startsWith('${app.projectId}')) + .length; } diff --git a/lib/controller/rest.dart b/lib/controller/rest.dart deleted file mode 100644 index bb2c8a1..0000000 --- a/lib/controller/rest.dart +++ /dev/null @@ -1,204 +0,0 @@ -import 'dart:convert'; -import 'dart:typed_data'; - -import 'package:bfast/adapter/rest.dart'; -import 'package:http/http.dart' as http; - -class BFastHttpClientController extends RestAdapter { - http.Client httpClient; - - BFastHttpClientController({this.httpClient}) { - if (this.httpClient == null) { - this.httpClient = http.Client(); - } - } - - @override - Future> delete(String url, - {RestRequestConfig config}) async { - var response = await this.httpClient.delete( - this._encodeUrlQueryParams(url, config?.params), - headers: config?.headers); - if (response.statusCode.toString().startsWith('20') == true) { - return RestResponse( - data: response.body.startsWith('{') - ? jsonDecode(response.body) - : response.body); - } else { - throw { - "message": response.body, - "reason": response.reasonPhrase, - "statusCode": response.statusCode - }; - } - } - - @override - Future> get(String url, - {RestRequestConfig config}) async { - var response = await this.httpClient.get( - this._encodeUrlQueryParams(url, config?.params), - headers: config?.headers); - if (response.statusCode.toString().startsWith('20') == true) { - return RestResponse( - data: response.body.startsWith('{') - ? jsonDecode(response.body) - : response.body); - } else { - throw { - "message": response.body, - "reason": response.reasonPhrase, - "statusCode": response.statusCode - }; - } - } - - Uri _encodeUrlQueryParams(String url, Map params) { - String urlWithParams = url; - urlWithParams += '?'; - if (params != null) { - params.forEach((key, value) { - urlWithParams += - '${key.toString()}=${Uri.encodeQueryComponent(value.toString())}&'; - }); - } - return Uri.parse(urlWithParams); - } - - @override - Future> head(String url, - {RestRequestConfig config}) async { - var response = await this.httpClient.head( - this._encodeUrlQueryParams(url, config?.params), - headers: config?.headers); - if (response.statusCode.toString().startsWith('20') == true) { - return RestResponse( - data: response.body.startsWith('{') - ? jsonDecode(response.body) - : response.body); - } else { - throw { - "message": response.body, - "reason": response.reasonPhrase, - "statusCode": response.statusCode - }; - } - } - - @override - Future> options(String url, - {RestRequestConfig config}) async { - var response = await this.httpClient.head( - this._encodeUrlQueryParams(url, config?.params), - headers: config?.headers); - if (response.statusCode.toString().startsWith('20') == true) { - return RestResponse( - data: response.body.startsWith('{') - ? jsonDecode(response.body) - : response.body); - } else { - throw { - "message": response.body, - "reason": response.reasonPhrase, - "statusCode": response.statusCode - }; - } - } - - @override - Future> patch(String url, - {T data, RestRequestConfig config}) async { - var response = await this.httpClient.patch( - this._encodeUrlQueryParams(url, config?.params), - body: data, - headers: config?.headers); - if (response.statusCode.toString().startsWith('20') == true) { - return RestResponse( - data: response.body.startsWith('{') - ? jsonDecode(response.body) - : response.body); - } else { - throw { - "message": response.body, - "reason": response.reasonPhrase, - "statusCode": response.statusCode - }; - } - } - - @override - Future> post(String url, - {T data, - RestRequestConfig config, - http.MultipartRequest multipartRequest}) async { - var response = await this.httpClient.post( - this._encodeUrlQueryParams(url, config?.params), - body: (data != null && data is Map) ? jsonEncode(data) : data, - headers: config != null && config.headers != null - ? config.headers - : {"content-type": "application/json"}); - if (response.statusCode.toString().startsWith('20') == true) { - return RestResponse( - data: response.body.startsWith('{') - ? jsonDecode(response.body) - : response.body); - } else { - throw { - "message": response.body, - "reason": response.reasonPhrase, - "statusCode": response.statusCode - }; - } - } - - @override - Future multiPartRequest(String url, ByteBuffer data, - {http.MultipartRequest multipartRequest, - String method = 'POST', - RestRequestConfig config}) async { - var uri = Uri.parse(url); - http.MultipartRequest request; - if (multipartRequest == null) { - request = http.MultipartRequest(method, uri); - } - request - ..files.add(http.MultipartFile.fromBytes('file', data.asUint32List())); - var response = await request.send(); - if (response.statusCode.toString().startsWith('20') == true) { - return RestResponse(data: response.reasonPhrase); - } else { - throw { - "message": response.reasonPhrase, - "reason": response.reasonPhrase, - "statusCode": response.statusCode - }; - } - } - - @override - Future> put(String url, - {T data, RestRequestConfig config}) async { - var response = await this.httpClient.put( - this._encodeUrlQueryParams(url, config?.params), - body: jsonEncode(data), - headers: config?.headers); - if (response.statusCode.toString().startsWith('20') == true) { - return RestResponse( - data: response.body.startsWith('{') - ? jsonDecode(response.body) - : response.body); - } else { - throw { - "message": response.body, - "reason": response.reasonPhrase, - "statusCode": response.statusCode - }; - } - } -} - -class RestResponse { - T data; - - RestResponse({this.data}); -} diff --git a/lib/controller/rules.dart b/lib/controller/rules.dart deleted file mode 100644 index daa8949..0000000 --- a/lib/controller/rules.dart +++ /dev/null @@ -1,175 +0,0 @@ -import 'package:bfast/adapter/auth.dart'; -import 'package:bfast/adapter/query.dart'; -import 'package:bfast/bfast_config.dart'; -import 'package:bfast/model/QueryModel.dart'; -import 'package:bfast/model/UpdateModel.dart'; -import 'package:bfast/model/transaction.dart'; - -class RulesController { - AuthAdapter authAdapter; - - RulesController(this.authAdapter); - - Future createRule( - String domain, var data, AppCredentials appCredential, - [RequestOptions options]) async { - assert(domain != null, 'domain must not be null'); - - var createRule = {}; - if (options != null && options.useMasterKey == true) { - createRule.addAll({"masterKey": appCredential.appPassword}); - } - createRule.addAll({"applicationId": appCredential.applicationId}); - if (data != null) { - assert( - (data is Map || - data is List>) == - true, - 'data must either be Map or List>'); - if (data is List) { - data = data.map((value) { - Map _data = Map.from(value); - _data['return'] = options != null && options.returnFields != null - ? options.returnFields - : []; - return _data; - }).toList(); - } else if (data is Map) { - Map _data = Map.from(data); - _data['return'] = options != null && options.returnFields != null - ? options.returnFields - : []; - data = _data; - } - createRule.addAll({'create$domain': data}); - return this.addToken(createRule); - } else { - throw {'message': 'please provide data to save'}; - } - } - - Future deleteRule( - String domain, QueryModel query, AppCredentials appCredential, - [RequestOptions options]) { - assert(domain != null, 'domain must not be null'); - assert(query != null, 'query must not be null'); - var deleteRule = {}; - if (options != null && options.useMasterKey == true) { - deleteRule.addAll({"masterKey": appCredential.appPassword}); - } - deleteRule.addAll({ - "applicationId": appCredential.applicationId, - 'delete$domain': {'id': query?.id, 'filter': query?.filter} - }); - return this.addToken(deleteRule); - } - - Future updateRule(String domain, QueryModel query, - UpdateModel updateModel, AppCredentials appCredential, - [RequestOptions options]) { - var updateRule = {}; - if (options != null && options.useMasterKey == true) { - updateRule.addAll({"masterKey": appCredential.appPassword}); - } - query.returnFields = options != null && options.returnFields != null - ? options.returnFields - : []; - query.update = updateModel; - updateRule.addAll({ - "applicationId": appCredential.applicationId, - ['update$domain']: query - }); - return this.addToken(updateRule); - } - - Future aggregateRule( - String domain, List pipeline, AppCredentials appCredentials, - [RequestOptions options]) { - var aggregateRule = {}; - if (options != null && options.useMasterKey == true) { - aggregateRule.addAll({'masterKey': appCredentials.appPassword}); - } - aggregateRule.addAll({ - "applicationId": appCredentials.applicationId, - ['aggregate$domain']: pipeline - }); - return this.addToken(aggregateRule); - } - - Future queryRule( - String domain, QueryModel queryModel, AppCredentials appCredentials, - {RequestOptions options}) { - var queryRule = {}; - if (options != null && options.useMasterKey == true) { - queryRule.addAll({'masterKey': appCredentials.appPassword}); - } - queryModel.returnFields = options != null && options.returnFields != null - ? options.returnFields - : []; - queryRule.addAll({ - "applicationId": appCredentials.applicationId, - 'query$domain': queryModel.toMap() - }); - return this.addToken(queryRule); - } - - Future transaction( - List transactions, AppCredentials appCredentials, - [RequestOptions options]) async { - var transactionRule = { - "transaction": {"commit": {}}, - "applicationId": appCredentials.applicationId, - "masterKey": appCredentials.appPassword, - }; - transactions.forEach((value) async { - if (value.action == TransactionAction.CREATE) { - var createRule = await this - .createRule(value.domain, value.data, appCredentials, options); - transactionRule['transaction']['commit'].addAll({ - ['${value.action}${value.domain}']: - createRule['${value.action}${value.domain}'] - }); - } else if (value.action == TransactionAction.UPDATE) { - var updateRule = await this.updateRule( - value.domain, - value.data['query'], - value.data['updateModel'], - appCredentials, - options); - transactionRule['transaction']['commit'].addAll({ - ['${value.action}${value.domain}']: - updateRule['${value.action}${value.domain}'] - }); - } else if (value.action == TransactionAction.DELETE) { - var deleteQuery = await this.deleteRule( - value.domain, value.data['query'], appCredentials, options); - transactionRule['transaction']['commit'].addAll({ - ['${value.action}${value.domain}']: - deleteQuery['${value.action}${value.domain}'] - }); - } - }); - return this.addToken(transactionRule); - } - - Future storage(String action, dynamic payload, AppCredentials appCredentials, - [RequestOptions options]) async { - var storageRule = {}; - if (options != null && options.useMasterKey == true) { - storageRule.addAll({'masterKey': appCredentials.appPassword}); - } - storageRule.addAll({ - "applicationId": appCredentials.applicationId, - "files": { - [action]: payload - } - }); - return this.addToken(storageRule); - } - - Future addToken(Map rule) async { - var token = await this.authAdapter.getToken(); - rule.addAll({"token": token}); - return rule; - } -} diff --git a/lib/controller/storage.dart b/lib/controller/storage.dart deleted file mode 100644 index 23ee2f1..0000000 --- a/lib/controller/storage.dart +++ /dev/null @@ -1,119 +0,0 @@ -//import 'dart:html' as html; -//import 'dart:io'; -//import 'dart:typed_data'; -//import 'package:http/http.dart' as http; -//import 'package:bfast/adapter/auth.dart'; -//import 'package:bfast/adapter/query.dart'; -//import 'package:bfast/adapter/rest.dart'; -//import 'package:bfast/adapter/storage.dart'; -//import 'package:bfast/bfast_config.dart'; -//import 'package:bfast/controller/rest.dart'; -//import 'package:bfast/controller/rules.dart'; -//import 'package:bfast/model/FileQueryModel.dart'; -//import 'package:flutter/foundation.dart'; -// -//class StorageController { -// RestAdapter _restAdapter; -// AuthAdapter _authAdapter; -// RulesController _rulesController; -// String _appName; -// -// StorageController(this._restAdapter, this._authAdapter, this._rulesController, -// this._appName); -// -// Future save( -// dynamic file, void Function(dynamic progress) uploadProgress, -// [RequestOptions options]) { -// if (file is File) { -// return this._handleFileUploadInNode(file, uploadProgress, -// BFastConfig.getInstance().getAppCredential(this._appName), options); -// } else if (kIsWeb == true && file is html.File) { -// return this._handleFileUploadInWeb(file, uploadProgress, -// BFastConfig.getInstance().getAppCredential(this._appName), options); -// } else { -// throw Exception('file object to save required'); -// } -// } -// -// Future> list( -// [FileQueryModel query, RequestOptions options]) async { -// var filesRule = await this._rulesController.storage("list", query, -// BFastConfig.getInstance().getAppCredential(this._appName), options); -// return this._handleFileRuleRequest(filesRule, 'list'); -// } -// -// Future delete(String filename, [RequestOptions options]) async { -// var filesRule = await this._rulesController.storage( -// "delete", -// {"filename": filename}, -// BFastConfig.getInstance().getAppCredential(this._appName), -// options); -// return this._handleFileRuleRequest(filesRule, 'delete'); -// } -// -// Future _handleFileRuleRequest( -// dynamic storageRule, String action) async { -// RestResponse response = await this._restAdapter.post( -// BFastConfig.getInstance().databaseURL(this._appName), storageRule); -// var data = response.data; -// if (data != null && -// data['files'] != null && -// data['files']['list'] != null && -// data['files']['list'] is List) { -// return data['files']['list']; -// } else { -// var errors = data['errors']; -// throw errors != null && errors['files.$action'] != null -// ? errors['files.$action'] -// : {"message": 'Fails to process your request', "errors": errors}; -// } -// } -// -// Future _handleFileUploadInNode( -// ByteBuffer readStream, -// void Function(dynamic progress) uploadProgress, -// AppCredentials appCredentials, -// [RequestOptions options]) async { -// const headers = {}; -// if (options != null && options.useMasterKey == true) { -// headers.addAll({ -// 'masterKey': appCredentials.appPassword, -// }); -// } -// var token = await this._authAdapter.getToken(); -// headers.addAll({'Authorization': 'Bearer $token'}); -// -// RestResponse response = await this._restAdapter.multiPartRequest( -// BFastConfig.getInstance().databaseURL( -// this._appName, '/storage/' + appCredentials.applicationId), -// readStream, -// config: RestRequestConfig(headers: headers, uploadProgress: uploadProgress)); -// var databaseUrl = BFastConfig.getInstance().databaseURL(this._appName); -// return databaseUrl + response.data['urls'][0]; -// } -// -// Future _handleFileUploadInWeb( -// html.File file, -// void Function(dynamic progress) uploadProgress, -// AppCredentials appCredentials, -// [RequestOptions options]) async { -// const headers = {}; -// if (options != null && options.useMasterKey == true) { -// headers.addAll({ -// 'masterKey': appCredentials.appPassword, -// }); -// } -// var token = await this._authAdapter.getToken(); -// headers.addAll({'Authorization': 'Bearer $token'}); -// headers.addAll({'content-type': 'multipart/form-data'}); -// html.FormData formData = new html.FormData(); -// formData.appendBlob('file', file); -// RestResponse response = await this._restAdapter.post( -// BFastConfig.getInstance().databaseURL( -// this._appName, '/storage/' + appCredentials.applicationId), -// formData, -// RestRequestConfig(headers: headers, uploadProgress: uploadProgress)); -// var databaseUrl = BFastConfig.getInstance().databaseURL(this._appName); -// return databaseUrl + response.data['urls'][0]; -// } -//} diff --git a/lib/controller/transaction.dart b/lib/controller/transaction.dart deleted file mode 100644 index 3d1aff2..0000000 --- a/lib/controller/transaction.dart +++ /dev/null @@ -1,71 +0,0 @@ -import 'package:bfast/adapter/query.dart'; -import 'package:bfast/adapter/rest.dart'; -import 'package:bfast/controller/rest.dart'; -import 'package:bfast/controller/rules.dart'; -import 'package:bfast/model/QueryModel.dart'; -import 'package:bfast/model/UpdateModel.dart'; -import 'package:bfast/model/transaction.dart'; - -import '../bfast_config.dart'; - -class TransactionController { - List transactionRequests = []; - RestAdapter _restAdapter; - RulesController _rulesController; - String _appName; - - TransactionController( - this._appName, this._restAdapter, this._rulesController); - - Future commit( - {Future> Function( - List transactionsRequests) - before, - Future Function() after, - bool useMasterKey}) async { - if (before != null) { - List result = await before(this.transactionRequests); - if (result != null && result is List && result.length > 0) { - this.transactionRequests = result; - } else if (result != null && result is List && result.length == 0) { - this.transactionRequests = result; - } - } - var transactionRule = await this._rulesController.transaction( - this.transactionRequests, - BFastConfig.getInstance().getAppCredential(this._appName), - RequestOptions(userMasterKey: useMasterKey)); - RestResponse response = await this._restAdapter.post( - BFastConfig.getInstance().databaseURL(this._appName), - data: transactionRule); - this.transactionRequests = []; - if (after != null) { - await after(); - } - return response.data; - } - - TransactionController create(String domainName, dynamic data) { - this.transactionRequests.add(TransactionModel( - data: data, action: TransactionAction.CREATE, domain: domainName)); - return this; - } - - TransactionController delete( - String domainName, QueryModel query) { - this.transactionRequests.add(TransactionModel( - data: {"query": query}, - action: TransactionAction.DELETE, - domain: domainName)); - return this; - } - - TransactionController update( - String domainName, QueryModel queryModel, UpdateModel updateModel) { - this.transactionRequests.add(TransactionModel( - data: {"query": queryModel, "updateModel": updateModel}, - action: TransactionAction.UPDATE, - domain: domainName)); - return this; - } -} diff --git a/lib/controller/update.dart b/lib/controller/update.dart deleted file mode 100644 index de594d3..0000000 --- a/lib/controller/update.dart +++ /dev/null @@ -1,98 +0,0 @@ -import 'package:bfast/adapter/query.dart'; -import 'package:bfast/adapter/rest.dart'; -import 'package:bfast/bfast_config.dart'; -import 'package:bfast/controller/database.dart'; -import 'package:bfast/controller/rest.dart'; -import 'package:bfast/controller/rules.dart'; -import 'package:bfast/model/QueryModel.dart'; -import 'package:bfast/model/UpdateModel.dart'; - -class UpdateController { - UpdateModel _query = UpdateModel($set: {}); - String _domain; - String _appName; - RestAdapter _restAdapter; - RulesController _rulesController; - QueryModel _queryModel; - - UpdateController(this._domain, this._queryModel, this._appName, - this._restAdapter, this._rulesController); - - UpdateController set(String field, dynamic value) { - this._query.$set.addAll({ - [field]: value - }); - return this; - } - - UpdateController increment(String field, [int amount = 1]) { - this._query.$inc.addAll({ - [field]: amount - }); - return this; - } - - UpdateController decrement(String field, [int amount = 1]) { - return this.increment(field, -amount); - } - - UpdateController currentDate(String field) { - this._query.$currentDate.addAll({ - [field]: true - }); - return this; - } - - UpdateController minimum(String field, dynamic value) { - this._query.$min.addAll({ - [field]: value - }); - return this; - } - - UpdateController maximum(String field, dynamic value) { - this._query.$max.addAll({ - [field]: value - }); - return this; - } - - UpdateController multiply(String field, int quantity) { - this._query.$mul.addAll({ - [field]: quantity - }); - return this; - } - - UpdateController renameField(String field, String value) { - this._query.$rename.addAll({ - [field]: value - }); - return this; - } - - UpdateController removeField(String field) { - this._query.$unset.addAll({ - [field]: '' - }); - return this; - } - - UpdateModel _build() { - return this._query; - } - - Future update([RequestOptions options]) async { - var updateRule = await this._rulesController.updateRule( - this._domain, - this._queryModel, - this._build(), - BFastConfig.getInstance().getAppCredential(this._appName), - options); - RestResponse response = await this._restAdapter.post( - BFastConfig.getInstance().databaseURL(this._appName), - data: updateRule); - return DatabaseController.extractResultFromServer( - response.data, 'update', this._domain); - } -} diff --git a/lib/model/FileQueryModel.dart b/lib/model/FileQueryModel.dart deleted file mode 100644 index 4054d70..0000000 --- a/lib/model/FileQueryModel.dart +++ /dev/null @@ -1,8 +0,0 @@ -class FileQueryModel { - String keyword = ''; - int size; - int skip; - String after; - - FileQueryModel({this.size, this.skip, this.after, this.keyword}); -} diff --git a/lib/model/FilterModel.dart b/lib/model/FilterModel.dart deleted file mode 100644 index 959df73..0000000 --- a/lib/model/FilterModel.dart +++ /dev/null @@ -1,76 +0,0 @@ -//mixin BasicFilterModel{ -// List> $and?; -//$nor?: Array>; -//$or?: Array>; -//$text?: { -//$search: string; -//$language?: string; -//$caseSensitive?: boolean; -//$diacraticSensitive?: boolean; -//}; -//[key: string]: any -//// $where?: string | Function; -//// $comment?: string; -//} -// -//type FilterSelector = { -//// Comparison -//$eq?: T; -//$gt?: T; -//$gte?: T; -//$in?: T[]; -//$lt?: T; -//$lte?: T; -//$ne?: T; -//$nin?: T[]; -//// Logical -//$not?: T extends string ? (FilterSelector | RegExp) : FilterSelector; -//// Element -///** -// * When `true`, `$exists` matches the documents that contain the field, -// * including documents where the field value is null. -// */ -//$exists?: boolean; -//// Evaluation -//// $expr?: any; -//// $jsonSchema?: any; -//// $mod?: T extends number ? [number, number] : never; -//$regex?: T extends string ? (RegExp | string) : never; -//// $options?: T extends string ? string : never; -//// Geospatial -//// TODO: define better types for geo queries -//$geoIntersects?: { $geometry: object }; -//$geoWithin?: object; -//$near?: object; -//$nearSphere?: object; -//$maxDistance?: number; -//$maxDistanceInMiles: number; -//$maxDistanceInKilometers: number; -//$maxDistanceInRadians: number; -//// Array -//// TODO: define better types for $all and $elemMatch -//$all?: T extends Array ? any[] : never; -//// $elemMatch?: T extends Array ? object : never; -//// $size?: T extends Array ? number : never; -//// // Bitwise -//// $bitsAllClear?: BitwiseQuery; -//// $bitsAllSet?: BitwiseQuery; -//// $bitsAnyClear?: BitwiseQuery; -//// $bitsAnySet?: BitwiseQuery; -//} -// -//type FilterCondition = FilterAltQuery | FilterSelector>; -// -//type FilterAltQuery = -//T extends Array ? (T | FilterRegExpForString) : -//FilterRegExpForString; -//type FilterRegExpForString = T extends string ? (RegExp | T) : T; -// -//class FilterModel{ -// FilterCondition [P in keyof T]?; -//} & -//BasicFilterModel; - -class FilterModel { - -} diff --git a/lib/model/FullTextModel.dart b/lib/model/FullTextModel.dart deleted file mode 100644 index ec41a4e..0000000 --- a/lib/model/FullTextModel.dart +++ /dev/null @@ -1,12 +0,0 @@ -class FullTextModel { - String search; - String language; - bool caseSensitive; - bool diacriticSensitive; - - FullTextModel( - {this.caseSensitive, - this.diacriticSensitive, - this.language, - this.search}); -} diff --git a/lib/model/QueryModel.dart b/lib/model/QueryModel.dart deleted file mode 100644 index a96f6c8..0000000 --- a/lib/model/QueryModel.dart +++ /dev/null @@ -1,62 +0,0 @@ -import 'package:bfast/model/UpdateModel.dart'; - -class QueryModel { - int skip; - int size; - List> orderBy; - Map filter; - List returnFields; - bool count; - int last; - int first; - String id; - UpdateModel update; - - QueryModel( - {this.update, - this.returnFields, - this.last, - this.first, - this.count, - this.size, - this.id, - this.filter, - this.orderBy, - this.skip}); - - Map toMap() { - var map = {}; - if (this.update != null) { - map['update'] = this.update; - } - if (this.returnFields != null) { - map['return'] = this.returnFields; - } - if (this.last != null) { - map['last'] = this.last; - } - if (this.first != null) { - map['first'] = this.first; - } - if (this.count != null) { - map['count'] = this.count; - } - if (this.size != null) { - map['size'] = this.size; - } - if (this.id != null) { - map['id'] = this.id; - } - if (this.filter != null) { - map['filter'] = this.filter; - } - if (this.orderBy != null) { - map['orderBy'] = this.orderBy; - } - if (this.skip != null) { - map['skip'] = this.skip; - } - - return map; - } -} diff --git a/lib/model/UpdateModel.dart b/lib/model/UpdateModel.dart deleted file mode 100644 index 710ddcf..0000000 --- a/lib/model/UpdateModel.dart +++ /dev/null @@ -1,20 +0,0 @@ -class UpdateModel { - Map $set; - Map $inc; - Map $currentDate; - Map $min; - Map $max; - Map $mul; - Map $rename; - Map $unset; - - UpdateModel( - {this.$set, - this.$currentDate, - this.$inc, - this.$max, - this.$min, - this.$mul, - this.$rename, - this.$unset}); -} diff --git a/lib/model/raw_response.dart b/lib/model/raw_response.dart new file mode 100644 index 0000000..b9dee82 --- /dev/null +++ b/lib/model/raw_response.dart @@ -0,0 +1,6 @@ +class RawResponse { + final int statusCode; + final dynamic body; + + RawResponse({required this.body, required this.statusCode}); +} diff --git a/lib/model/RealTimeResponse.dart b/lib/model/real_time_response.dart similarity index 97% rename from lib/model/RealTimeResponse.dart rename to lib/model/real_time_response.dart index cc619ce..0f6292e 100644 --- a/lib/model/RealTimeResponse.dart +++ b/lib/model/real_time_response.dart @@ -1,4 +1,4 @@ class RealTimeResponse{ dynamic body; RealTimeResponse(this.body); -} \ No newline at end of file +} diff --git a/lib/model/transaction.dart b/lib/model/transaction.dart deleted file mode 100644 index b635cb2..0000000 --- a/lib/model/transaction.dart +++ /dev/null @@ -1,9 +0,0 @@ -class TransactionModel { - TransactionAction action; - String domain; - T data; - - TransactionModel({this.data, this.action, this.domain}); -} - -enum TransactionAction { CREATE, UPDATE, DELETE, QUERY } diff --git a/lib/options.dart b/lib/options.dart new file mode 100644 index 0000000..e235fa2 --- /dev/null +++ b/lib/options.dart @@ -0,0 +1,87 @@ +import 'package:bfast/util.dart'; + +const DEFAULT_APP = 'DEFAULT'; +const AUTH_CACHE_NAME = '_current_user_'; +const AUTH_CACHE_DEFAULT_VALUE = '_empty_'; +const String DEFAULT_DOMAINS_CACHE_DB_NAME = '__domain'; +const String DEFAULT_CACHE_DB_NAME = '__cache'; +const String DEFAULT_CACHE_TTL_COLLECTION_NAME = '__cache_ttl'; +const String DEFAULT_AUTH_CACHE_DB_NAME = '__auth'; + +Map getInitialHeaders() => + {'Content-Type': 'application/json'}; + +var _isStartWithHttp = compose([ + (y) => y.startsWith('http'), + ifDoElse((x) => x is String, (f1) => f1, (f2) => '') +]); +var _functionURLOrEmpty = ifDoElse((t) => t is App && t.functionsURL != null, + (t) => t.functionsURL, (t) => ''); +var _hasFunctionsURLAndStartWithHttp = + compose([_isStartWithHttp, _functionURLOrEmpty]); +var _getFUrl = (app) => (path) => '${app.functionsURL}$path'; + +_getFaasUrl(app) => ifDoElse( + (_) => app is App, + (path) => 'https://${app.projectId}-faas.bfast.fahamutech.com$path', + (path) => 'https://null-faas.bfast.fahamutech.com', + ); +// (path) => 'https://${app.projectId}-faas.bfast.fahamutech.com$path'; + +_getDaasUrl(app) => ifDoElse( + (_) => app is App, + (x) => + 'https://${app?.projectId}-daas.bfast.fahamutech.com/v2${_itOrEmpty(x)}', + (x) => 'https://null-daas.bfast.fahamutech.com/v2${_itOrEmpty(x)}', + ); +var _databaseURLOrEmpty = ifDoElse( + (t) => t is App && t.databaseURL != null, (t) => t.databaseURL, (t) => ''); +var _hasDatabaseURLAndStartWithHttp = + compose([_isStartWithHttp, _databaseURLOrEmpty]); + +var _itOrEmpty = + ifDoElse((x) => x is String, (y) => '$y' /*.replaceAll('/v2', '')*/, (_) => ''); + +Function(dynamic path) functionsURL(app) => ifDoElse( + _isStartWithHttp, + map((x) => x), + ifDoElse( + (_) => _hasFunctionsURLAndStartWithHttp(app), + _getFUrl(app), + _getFaasUrl(app), + ), + ); + +Function(dynamic suffix) databaseURL(app) => ifDoElse( + (_) => _hasDatabaseURLAndStartWithHttp(app), + (x) => '${app?.databaseURL}${_itOrEmpty(x)}', + _getDaasUrl(app), + ); + +var _projectIdOrCacheDefaultName = + ifDoElse((app) => app is App, (app) => app?.projectId, (app) => 'cache'); + +cacheDatabaseName(app) { + var dbName = _projectIdOrCacheDefaultName(app); + return ifDoElse( + (name) => name is String && name.isNotEmpty, + (name) => '/bfast/$dbName/$name', + (name) => '/bfast/$dbName', + ); +} + +class App { + final String applicationId; + final String projectId; + final String? functionsURL; + final String? databaseURL; + final String? appPassword; + + App({ + required this.applicationId, + required this.projectId, + this.functionsURL, + this.databaseURL, + this.appPassword, + }); +} diff --git a/lib/util.dart b/lib/util.dart new file mode 100644 index 0000000..b96cea1 --- /dev/null +++ b/lib/util.dart @@ -0,0 +1,33 @@ +// ( (*)->bool fn, (*)->* fn1, (*)->* fn2) -> x -> fn1(x) : fn2(x) +Function(dynamic) ifDoElse( + Function(dynamic f) fn, + Function fn1, + Function fn2, +) => + (x) => fn(x) == true ? fn1(x) : fn2(x); + +// [(a->b),(c->d)] -> a -> d +Function(dynamic x) compose(List fns) => (x) { + var _x = x; + final _reversed = fns.reversed.toList(); + for (Function fn in _reversed) { + _x = fn(_x); + } + return _x; + }; + +// [(a->b),(c->d)] -> a -> d +Future Function(dynamic x) composeAsync(List fns) => (x) async { + var _x = x; + final _reversed = fns.reversed.toList(); + for (Function fn in _reversed) { + _x = await fn(_x); + } + return _x; + }; + +// * fn(*) -> a -> fn(a) +Function(dynamic x) map(Function fn) => (x) => fn(x); + +// impure, because it throw data +Function(dynamic x) ifThrow(fn1, fn2) => (x) => fn1(x) == true ? throw fn2(x) : x; diff --git a/pubspec.lock b/pubspec.lock index 9a6ae8f..b681427 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,34 +1,13 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: - _fe_analyzer_shared: - dependency: transitive - description: - name: _fe_analyzer_shared - url: "https://pub.dartlang.org" - source: hosted - version: "20.0.0" - analyzer: - dependency: transitive - description: - name: analyzer - url: "https://pub.dartlang.org" - source: hosted - version: "1.4.0" - args: - dependency: transitive - description: - name: args - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.0" async: dependency: transitive description: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.5.0" + version: "2.8.2" boolean_selector: dependency: transitive description: @@ -36,48 +15,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.1.0" - build: - dependency: transitive - description: - name: build - url: "https://pub.dartlang.org" - source: hosted - version: "1.6.3" - built_collection: - dependency: transitive - description: - name: built_collection - url: "https://pub.dartlang.org" - source: hosted - version: "5.0.0" - built_value: - dependency: transitive - description: - name: built_value - url: "https://pub.dartlang.org" - source: hosted - version: "8.0.4" characters: dependency: transitive description: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.1.0" + version: "1.2.0" charcode: dependency: transitive description: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" - cli_util: - dependency: transitive - description: - name: cli_util - url: "https://pub.dartlang.org" - source: hosted - version: "0.3.0" + version: "1.3.1" clock: dependency: transitive description: @@ -85,69 +36,34 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.0" - code_builder: - dependency: transitive - description: - name: code_builder - url: "https://pub.dartlang.org" - source: hosted - version: "3.7.0" collection: dependency: transitive description: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.15.0" - convert: - dependency: transitive - description: - name: convert - url: "https://pub.dartlang.org" - source: hosted - version: "3.0.0" - crypto: - dependency: transitive - description: - name: crypto - url: "https://pub.dartlang.org" - source: hosted - version: "3.0.1" - dart_style: - dependency: transitive - description: - name: dart_style - url: "https://pub.dartlang.org" - source: hosted - version: "1.3.14" + version: "1.16.0" fake_async: dependency: transitive description: name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.3.0" ffi: dependency: transitive description: name: ffi url: "https://pub.dartlang.org" source: hosted - version: "1.0.0" + version: "2.0.1" file: dependency: transitive description: name: file url: "https://pub.dartlang.org" source: hosted - version: "6.1.0" - fixnum: - dependency: transitive - description: - name: fixnum - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.0" + version: "6.1.2" flutter: dependency: "direct main" description: flutter @@ -158,174 +74,132 @@ packages: description: flutter source: sdk version: "0.0.0" - glob: - dependency: transitive - description: - name: glob - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.1" http: dependency: "direct main" description: name: http url: "https://pub.dartlang.org" source: hosted - version: "0.12.2" + version: "0.13.5" http_parser: dependency: transitive description: name: http_parser url: "https://pub.dartlang.org" source: hosted - version: "3.1.4" - idb_shim: - dependency: transitive - description: - name: idb_shim - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.0+2" + version: "4.0.1" js: dependency: transitive description: name: js url: "https://pub.dartlang.org" source: hosted - version: "0.6.3" + version: "0.6.4" logging: dependency: transitive description: name: logging url: "https://pub.dartlang.org" source: hosted - version: "0.11.4" + version: "1.0.2" matcher: dependency: transitive description: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.10" - meta: + version: "0.12.11" + material_color_utilities: dependency: transitive description: - name: meta + name: material_color_utilities url: "https://pub.dartlang.org" source: hosted - version: "1.3.0" - mockito: - dependency: "direct dev" - description: - name: mockito - url: "https://pub.dartlang.org" - source: hosted - version: "5.0.3" - package_config: + version: "0.1.4" + meta: dependency: transitive description: - name: package_config + name: meta url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "1.7.0" path: dependency: "direct main" description: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.0" + version: "1.8.1" path_provider: dependency: "direct main" description: name: path_provider url: "https://pub.dartlang.org" source: hosted - version: "2.0.1" + version: "2.0.11" + path_provider_android: + dependency: transitive + description: + name: path_provider_android + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.19" + path_provider_ios: + dependency: transitive + description: + name: path_provider_ios + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.11" path_provider_linux: dependency: transitive description: name: path_provider_linux url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.1.7" path_provider_macos: dependency: transitive description: name: path_provider_macos url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.0.6" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.0.1" + version: "2.0.4" path_provider_windows: dependency: transitive description: name: path_provider_windows url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" - pedantic: - dependency: transitive - description: - name: pedantic - url: "https://pub.dartlang.org" - source: hosted - version: "1.11.0" + version: "2.1.2" platform: dependency: transitive description: name: platform url: "https://pub.dartlang.org" source: hosted - version: "3.0.0" + version: "3.1.0" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface url: "https://pub.dartlang.org" source: hosted - version: "2.0.0" + version: "2.1.2" process: dependency: transitive description: name: process url: "https://pub.dartlang.org" source: hosted - version: "4.2.1" - pub_semver: - dependency: transitive - description: - name: pub_semver - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.0" - sembast: - dependency: "direct main" - description: - name: sembast - url: "https://pub.dartlang.org" - source: hosted - version: "3.0.0+4" - sembast_sqflite: - dependency: "direct main" - description: - name: sembast_sqflite - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.0+1" - sembast_web: - dependency: "direct main" - description: - name: sembast_web - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.0+2" + version: "4.2.4" sky_engine: dependency: transitive description: flutter @@ -337,42 +211,21 @@ packages: name: socket_io_client url: "https://pub.dartlang.org" source: hosted - version: "0.9.12" + version: "2.0.0" socket_io_common: dependency: transitive description: name: socket_io_common url: "https://pub.dartlang.org" source: hosted - version: "0.9.2" - source_gen: - dependency: transitive - description: - name: source_gen - url: "https://pub.dartlang.org" - source: hosted - version: "0.9.10+3" + version: "2.0.0" source_span: dependency: transitive description: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.0" - sqflite: - dependency: "direct main" - description: - name: sqflite - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.0+3" - sqflite_common: - dependency: transitive - description: - name: sqflite_common - url: "https://pub.dartlang.org" - source: hosted - version: "2.0.0+2" + version: "1.8.2" stack_trace: dependency: transitive description: @@ -394,13 +247,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.0" - synchronized: - dependency: transitive - description: - name: synchronized - url: "https://pub.dartlang.org" - source: hosted - version: "3.0.0" term_glyph: dependency: transitive description: @@ -414,49 +260,35 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.19" + version: "0.4.9" typed_data: dependency: transitive description: name: typed_data url: "https://pub.dartlang.org" source: hosted - version: "1.3.0" + version: "1.3.1" vector_math: dependency: transitive description: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.0" - watcher: - dependency: transitive - description: - name: watcher - url: "https://pub.dartlang.org" - source: hosted - version: "1.0.0" + version: "2.1.2" win32: dependency: transitive description: name: win32 url: "https://pub.dartlang.org" source: hosted - version: "2.0.5" + version: "2.7.0" xdg_directories: dependency: transitive description: name: xdg_directories url: "https://pub.dartlang.org" source: hosted - version: "0.2.0" - yaml: - dependency: transitive - description: - name: yaml - url: "https://pub.dartlang.org" - source: hosted - version: "3.1.0" + version: "0.2.0+1" sdks: - dart: ">=2.12.0 <3.0.0" - flutter: ">=1.24.0-10" + dart: ">=2.17.0 <3.0.0" + flutter: ">=3.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index 8587344..2eda879 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,26 +1,27 @@ name: bfast description: BFast client sdk for flutter -version: 4.1.1 +version: 5.0.0-beta.0 homepage: http://github.com/fahamutech/bfast-flutter environment: - sdk: ">=2.1.0 <3.0.0" + sdk: '>=2.12.0 <3.0.0' dependencies: - http: ^0.12.2 - path: ^1.8.0 - sembast: ^3.0.0+4 - path_provider: ^2.0.1 - socket_io_client: ^0.9.12 - sembast_sqflite: ^2.0.0+1 - sembast_web: ^2.0.0+2 - sqflite: ^2.0.0+3 flutter: sdk: flutter + path: ^1.8.1 + http: ^0.13.5 + socket_io_client: ^2.0.0 + path_provider: ^2.0.11 +# tekartik_app_flutter_sembast: +# git: +# url: https://github.com/tekartik/app_flutter_utils.dart +# ref: dart2_3 +# path: app_sembast +# version: '>=0.1.0' dev_dependencies: # test: ^1.15.1 - mockito: ^5.0.3 flutter_test: sdk: flutter diff --git a/test/bfast.auth._test.dart b/test/bfast.auth._test.dart deleted file mode 100644 index 780d140..0000000 --- a/test/bfast.auth._test.dart +++ /dev/null @@ -1,32 +0,0 @@ -import 'package:bfast/bfast.dart'; -import 'package:bfast/bfast_config.dart'; -import 'package:bfast/controller/auth.dart'; -import 'package:bfast/controller/rest.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:http/http.dart' as http; -import 'package:mockito/mockito.dart'; - -import 'mock/CacheMockController.dart'; - -class MockHttpClient extends Mock implements http.Client { - String mockDaasAPi = - BFastConfig.getInstance().databaseURL(BFastConfig.DEFAULT_APP); - String mockFaasAPi = - BFastConfig.getInstance().functionsURL('', BFastConfig.DEFAULT_APP); -} - -void main() { - BFast.init(AppCredentials("smartstock_lb", "smartstock")); - MockHttpClient mockHttpClient = MockHttpClient(); - - group("BFast transaction", () { - test("should do a transaction on bfast database", () async { - AuthController authController = AuthController( - BFastHttpClientController(), - MockCacheController({"name": "mock"}), - BFastConfig.DEFAULT_APP); -// var r = await authController.updateUser({"user":"joshua"}); -// print(r); - }); - }); -} diff --git a/test/bfast.domain._test.dart b/test/bfast.domain._test.dart deleted file mode 100644 index 7ba32a0..0000000 --- a/test/bfast.domain._test.dart +++ /dev/null @@ -1,284 +0,0 @@ -import 'dart:convert'; - -import 'package:bfast/bfast.dart'; -import 'package:bfast/bfast_config.dart'; -import 'package:bfast/controller/auth.dart'; -import 'package:bfast/controller/database.dart'; -import 'package:bfast/controller/rest.dart'; -import 'package:bfast/controller/rules.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:http/http.dart' as http; -import 'package:mockito/mockito.dart'; - -import 'mock/CacheMockController.dart'; - -class MockHttpClient extends Mock implements http.Client { - String mockDaasAPi = - BFastConfig.getInstance().databaseURL(BFastConfig.DEFAULT_APP); - String mockFaasAPi = - BFastConfig.getInstance().functionsURL('', BFastConfig.DEFAULT_APP); -} - -void main() { - BFast.init(AppCredentials("bfastdemo", "bfastdemo")); - MockHttpClient mockHttpClient = MockHttpClient(); - BFastHttpClientController httpClientController = - BFastHttpClientController(httpClient: mockHttpClient); - AuthController authController = AuthController( - httpClientController, MockCacheController({}), BFastConfig.DEFAULT_APP); - RulesController rulesController = RulesController(authController); - DatabaseController domainController = DatabaseController( - 'test', - httpClientController, - authController, - rulesController, - BFastConfig.DEFAULT_APP); - - group('Save data to bfast cloud database', () { - test("should save data", () async { - when(mockHttpClient.post( - argThat(startsWith('${mockHttpClient.mockDaasAPi}/classes/test')), - headers: anyNamed('headers'), - body: jsonEncode( - {"name": "Joshua"}))).thenAnswer((_) async => http.Response( - '{"id": "WpBNH3dAKXFEf6D0", "createdAt": "2020-07-02T17:13:54.411Z"}', - 200)); - - var r = await domainController.save({"name": "Joshua"}); - expect(r['id'], 'WpBNH3dAKXFEf6D0'); - // expect(r["createdAt"], !null); - }); - test("should not save null data", () async { - try { - await domainController.save(null); - } catch (r) { - expect(r['message'], 'please provide data to save'); - } - }); - test("should not save same object twice", () async { - try { - when( - mockHttpClient.post( - argThat( - startsWith('${mockHttpClient.mockDaasAPi}/classes/test')), - headers: anyNamed('headers'), - body: jsonEncode({ - "id": 'WpBNH3dAKXFEf6D0', - "name": 'joshua' - }))).thenAnswer((_) async => http.Response( - '{"code":137,"error":"A duplicate value for a field with unique values was provided"}', - 400)); - await domainController.save( - {"id": 'WpBNH3dAKXFEf6D0', "name": 'joshua'}); - } catch (r) { - expect(r['message'], - '{"code":137,"error":"A duplicate value for a field with unique values was provided"}'); - expect(r['statusCode'], 400); - } - }); - - test("should not save string data", () async { - try { - when(mockHttpClient.post( - argThat( - startsWith('${mockHttpClient.mockDaasAPi}/classes/test')), - headers: anyNamed('headers'), - body: anyNamed('body'))) - .thenAnswer((_) async => http.Response('Bad data', 400)); - await domainController.save("joshua" as dynamic); - } catch (r) { - expect(r.toString(), - "type 'String' is not a subtype of type 'Map'"); - } - }); - test("should not save number data", () async { - try { - when(mockHttpClient.post( - argThat( - startsWith('${mockHttpClient.mockDaasAPi}/classes/test')), - headers: anyNamed('headers'), - body: anyNamed('body'))) - .thenAnswer((_) async => http.Response( - 'Invalid argument(s): Invalid request body "1234".', 400)); - await domainController.save(1234 as dynamic); - } catch (r) { - expect( - r["message"], 'Invalid argument(s): Invalid request body "1234".'); - } - }); - }); - - group('Update data from bfast cloud database', () { - test("should updated exist data in bfast", () async { - when(mockHttpClient.put( - argThat(startsWith( - '${mockHttpClient.mockDaasAPi}/classes/test/WpBNH3dAKXFEf6D0')), - headers: anyNamed('headers'), - body: - jsonEncode({"name": "ethan"}))).thenAnswer( - (realInvocation) async => - http.Response('{"updatedAt": "2020-07-04T13:41:22.326Z"}', 200)); - var r = await domainController - .query() - .byId('WpBNH3dAKXFEf6D0') - .updateBuilder() - .set("name", "ethan") - .update(); - expect(r["updatedAt"], '2020-07-04T13:41:22.326Z'); - expect(true, r['updatedAt'] is String); - }); - test("should not update non exist data in bfast cloud database", () async { - try { - when(mockHttpClient.put( - argThat(startsWith( - '${mockHttpClient.mockDaasAPi}/classes/test/EpBNH3dAKXFEf6D1')), - headers: anyNamed('headers'), - body: anyNamed('body'))) - .thenAnswer((_) async => http.Response('Not Found', 404)); - await domainController - .query() - .byId('8687tugiu87t78tghfj') - .updateBuilder() - .set("name", "ethan") - .update(); - } catch (r) { - expect(r["message"], 'Not Found'); - expect(r["statusCode"], 404); - expect(true, r['message'] is String); - } - }); - }); - -// group("Delete data from bfast cloud database", () { -// test("should delete existing data in bfast cloud database", () async { -// when(mockHttpClient.delete( -// argThat(startsWith( -// '${mockHttpClient.mockDaasAPi}/classes/test/saCgUAJwEhCtK7a5')), -// headers: anyNamed('headers'))) -// .thenAnswer((_) async => http.Response('{}', 200)); -// var r = await domainController.delete('saCgUAJwEhCtK7a5'); -// expect(true, r != null); -// expect(r.length, 0); -// }); -// -// test("should return error when delete non exist record in database", -// () async { -// try { -// when(mockHttpClient.delete( -// argThat(startsWith( -// '${mockHttpClient.mockDaasAPi}/classes/test/saCgUAJwEhCtK7a5')), -// headers: anyNamed('headers'))) -// .thenAnswer((_) async => -// http.Response('{"code":101,"error":"Object not found."}', 404)); -// await domainController.delete('saCgUAJwEhCtK7a5'); -// } catch (r) { -// expect(true, r != null); -// expect(r['statusCode'], 404); -// expect(r["message"], '{"code":101,"error":"Object not found."}'); -// } -// }); -// }); -// -// group("Query data from bfast cloud database", () { -// test("should return all data available", () async { -// when(mockHttpClient.get( -// argThat(startsWith( -// '${mockHttpClient.mockDaasAPi}/classes/test?count=1&limit=0')), -// headers: anyNamed('headers'))) -// .thenAnswer( -// (_) async => http.Response('{"count":2,"results":[]}', 200)); -// when(mockHttpClient.get( -// argThat(startsWith( -// '${mockHttpClient.mockDaasAPi}/classes/test?limit=2')), -// headers: anyNamed('headers'))) -// .thenAnswer((_) async => http.Response( -// jsonEncode({ -// "results": [ -// { -// "id": "R2zZV8eSyvA8PAd9", -// "name": "Joshua", -// "createdAt": "2020-07-02T19:23:46.165Z", -// "updatedAt": "2020-07-02T19:23:46.165Z" -// }, -// { -// "id": "WpBNH3dAKXFEf6D0", -// "name": "ethan", -// "createdAt": "2020-07-02T17:13:54.411Z", -// "updatedAt": "2020-07-04T13:45:32.607Z" -// } -// ] -// }), -// 200)); -// var r = await domainController.getAll(); -// expect(true, r is List); -// expect(true, r != null); -// expect(r.length, 2); -// }); -// test("should return a single data when supply its id", () async { -// when(mockHttpClient.get( -// argThat(startsWith( -// '${mockHttpClient.mockDaasAPi}/classes/test/R2zZV8eSyvA8PAd9')), -// headers: anyNamed('headers'))) -// .thenAnswer((realInvocation) async => http.Response( -// jsonEncode({ -// "id": "R2zZV8eSyvA8PAd9", -// "name": "Joshua", -// "createdAt": "2020-07-02T19:23:46.165Z", -// "updatedAt": "2020-07-02T19:23:46.165Z" -// }), -// 200)); -// var r = await domainController.get('R2zZV8eSyvA8PAd9'); -// expect(true, r != null); -// expect(true, r['id'] is String); -// expect(r['id'], "R2zZV8eSyvA8PAd9"); -// }); -// test("should return data based on size of query model", () async { -// when(mockHttpClient.get( -// argThat(startsWith('${mockHttpClient.mockDaasAPi}/classes/test')), -// headers: anyNamed('headers'))) -// .thenAnswer((_) async => http.Response( -// jsonEncode({ -// "results": [ -// { -// "id": "R2zZV8eSyvA8PAd9", -// "name": "Joshua", -// "createdAt": "2020-07-02T19:23:46.165Z", -// "updatedAt": "2020-07-02T19:23:46.165Z" -// } -// ] -// }), -// 200)); -// var r = await domainController.query().find(QueryModel(size: 1)); -// expect(r.length, 1); -// expect(true, r != null); -// expect(r[0]["id"], "R2zZV8eSyvA8PAd9"); -// }); -// test("should return data based on query model", () async { -// when(mockHttpClient.get( -// argThat(startsWith('${mockHttpClient.mockDaasAPi}/classes/test')), -// headers: anyNamed('headers'))) -// .thenAnswer((_) async => http.Response( -// jsonEncode({ -// "results": [ -// { -// "id": "R2zZV8eSyvA8PAd9", -// "name": "Joshua", -// "createdAt": "2020-07-02T19:23:46.165Z", -// "updatedAt": "2020-07-02T19:23:46.165Z" -// } -// ] -// }), -// 200)); -// var r = await domainController.query().find(QueryModel(keys: [ -// "name" -// ], orderBy: [ -// "createdAt" -// ], filter: { -// "name": {"\$regex": "^Josh"} -// })); -// expect(r.length, 1); -// expect(true, r != null); -// expect(r[0]["id"], "R2zZV8eSyvA8PAd9"); -// }); -// }); -} diff --git a/test/bfast.functions._test.dart b/test/bfast.functions._test.dart deleted file mode 100644 index b437d82..0000000 --- a/test/bfast.functions._test.dart +++ /dev/null @@ -1,42 +0,0 @@ -import 'package:bfast/bfast.dart'; -import 'package:bfast/bfast_config.dart'; -import 'package:bfast/controller/function.dart'; -import 'package:bfast/controller/rest.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:http/http.dart' as http; -import 'package:mockito/mockito.dart'; - -class MockHttpClient extends Mock implements http.Client { - String mockDaasAPi = - BFastConfig.getInstance().databaseURL(BFastConfig.DEFAULT_APP); - String mockFaasAPi = - BFastConfig.getInstance().functionsURL('', BFastConfig.DEFAULT_APP); -} - -void main() { - BFast.init(AppCredentials("smartstock_lb", "smartstock")); - MockHttpClient mockHttpClient = MockHttpClient(); - - group("Http functions from bfast cloud", () { - test("should call a custom function from bfast cloud which uses GET method", - () async { - when(mockHttpClient.get(argThat(startsWith('${mockHttpClient.mockFaasAPi}/functions/users/verifyEmail')), - headers: anyNamed('headers'))).thenAnswer((_) async=> http.Response(' 1); - }); -// test("should call a custom function from bfast cloud which uses POST method", -// () async { -// FunctionController functionController = FunctionController( -// '/functions/users/confirmResetPassword/userId223', -// BFastHttpClientController(httpClient: null), -// appName: BFastConfig.DEFAULT_APP); -// var r = await functionController.delete(); -// expect(true, r.toString().length > 1); -// }); - }); -} diff --git a/test/bfast.realtime._test.dart b/test/bfast.realtime._test.dart deleted file mode 100644 index db97517..0000000 --- a/test/bfast.realtime._test.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'package:bfast/bfast.dart'; -import 'package:bfast/bfast_config.dart'; -import 'package:bfast/controller/realtime.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:http/http.dart' as http; -import 'package:mockito/mockito.dart'; - -class MockHttpClient extends Mock implements http.Client { - String mockDaasAPi = - BFastConfig.getInstance().databaseURL(BFastConfig.DEFAULT_APP); - String mockFaasAPi = - BFastConfig.getInstance().functionsURL('', BFastConfig.DEFAULT_APP); -} - -void main() { - BFast.init(AppCredentials("smartstock_lb", "smartstock")); - MockHttpClient mockHttpClient = MockHttpClient(); - - group("BFast realtime", () { - test("should connect to events from bfast cloud functions", () async { -// RealtimeController realtimeController = RealtimeController( -// 'test', -// appName: BFastConfig.DEFAULT_APP, -// onConnect: (e) => print("socket connected"), -// onDisconnect: (e) => print("socket disconnected"), -// ); -// realtimeController.listener((data) { -// print(data); -// }); -// realtimeController.emit(auth: {"name": "Joshua"}, payload: "Hello"); -// realtimeController.emit(auth: {"name": "ethan"}, payload: "Ethan"); -// await Future.delayed(Duration(seconds: 10)); - }); - }); -} diff --git a/test/bfast.storage._test.dart b/test/bfast.storage._test.dart deleted file mode 100644 index d510329..0000000 --- a/test/bfast.storage._test.dart +++ /dev/null @@ -1,33 +0,0 @@ -import 'package:bfast/adapter/storage.dart'; -import 'package:bfast/bfast.dart'; -import 'package:bfast/bfast_config.dart'; -import 'package:bfast/controller/rest.dart'; -import 'package:bfast/controller/storage.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:http/http.dart' as http; -import 'package:mockito/mockito.dart'; - -class MockHttpClient extends Mock implements http.Client { - String mockDaasAPi = - BFastConfig.getInstance().databaseURL(BFastConfig.DEFAULT_APP); - String mockFaasAPi = - BFastConfig.getInstance().functionsURL('', BFastConfig.DEFAULT_APP); -} - -void main() { - BFast.init(AppCredentials("smartstock_lb", "smartstock")); - MockHttpClient mockHttpClient = MockHttpClient(); - - group("BFast storage", () { - test("should save a file to bfast cloud", () async { -// StorageController storageController = StorageController( -// BFastHttpClientController(), -// appName: BFastConfig.DEFAULT_APP); -// SaveFileResponse r = await storageController.save(BFastFile( -// fileName: 'testflutter.txt', -// fileType: 'plain/text', -// data: BFastFileDataMap('aGVsbG8sIHdvcmxkCg=='))); -// print(r?.url); - }); - }); -} diff --git a/test/bfast.transaction._test.dart b/test/bfast.transaction._test.dart deleted file mode 100644 index 8842779..0000000 --- a/test/bfast.transaction._test.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'package:bfast/bfast.dart'; -import 'package:bfast/bfast_config.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:http/http.dart' as http; -import 'package:mockito/mockito.dart'; - -class MockHttpClient extends Mock implements http.Client { - String mockDaasAPi = - BFastConfig.getInstance().databaseURL(BFastConfig.DEFAULT_APP); - String mockFaasAPi = - BFastConfig.getInstance().functionsURL('', BFastConfig.DEFAULT_APP); -} - -void main() { - BFast.init(AppCredentials("smartstock_lb", "smartstock")); - MockHttpClient mockHttpClient = MockHttpClient(); - - // KwTFHsDoH8m3ivAA - // [{"success":{"objectId":"IrPEp1LZWP6O8R2f","createdAt":"2020-07-06T10:38:03.089Z"}},{"success":{"objectId":"YNpYIn0miOg5JG7t","createdAt":"2020-07-06T10:38:03.089Z"}}] - group("BFast transaction", () { - test("should do a transaction on bfast database", () async { -// TransactionController transactionController = TransactionController( -// BFastConfig.DEFAULT_APP, BFastHttpClientController(), -// isNormalBatch: false); -// var r = await transactionController.createMany('test', [{"name":"eitan"},{"name":"ageage"}]).commit( -// before: (transactionsRequests) async { -// print(transactionsRequests); -// print('before transaction is called'); -// return transactionsRequests; -// }, -// after: () async => print('after transaction called')); -// print(r); - }); - }); -} diff --git a/test/controllers/database._test.dart b/test/controllers/database._test.dart new file mode 100644 index 0000000..e540868 --- /dev/null +++ b/test/controllers/database._test.dart @@ -0,0 +1,67 @@ +import 'dart:convert'; + +import 'package:bfast/controller/database.dart'; +import 'package:bfast/model/raw_response.dart'; +import 'package:flutter_test/flutter_test.dart'; + +var mockOkResponse = RawResponse( + body: '''{"create_test":{"a": 1, "id": 1, "createdAt": "leo"}}''', + statusCode: 200); +var mockNotOkResponse = RawResponse( + body: + '''{"create_test":{},"errors": {"create._test": {"message": "err"}}}''', + statusCode: 200); +var mockNotOkNoErrObjResponse = + RawResponse(body: '''{"message": "unauthorized"}''', statusCode: 401); + +void main() { + group("Database Controller", () { + group("executeRule", () { + // var rule = { + // 'create_test': {'a': 1} + // }; + test("should return result of the http", () async { + var createRequest = await executeRule(() async => mockOkResponse); + expect(createRequest, jsonDecode(mockOkResponse.body)); + }); + test("should return error message if request fail", () async { + var createRequest = await executeRule(() => mockNotOkResponse); + expect(createRequest, jsonDecode(mockNotOkResponse.body)); + }); + test("should return error message if request fail", () async { + var createRequest = await executeRule(() => mockNotOkNoErrObjResponse); + var responseMap = { + 'errors': jsonDecode(mockNotOkNoErrObjResponse.body) + }; + expect(createRequest, responseMap); + }); + }); + group("errorsMessage", () { + test("should return errors message", () { + var errMessage = errorsMessage({ + "errors": { + "create.test": {"message": "test"} + } + }); + expect( + errMessage, + '{"create.test":{"message":"test"}}', + ); + }); + test("should return empty message", () { + String errMessage = errorsMessage({ + "err": { + "create.test": {"message": "test"} + } + }); + expect(errMessage, ''); + }); + test("should return empty string if not map", (){ + [[],()=>{},1,'','a', null].forEach((element) { + String errMessage = errorsMessage(element); + expect(errMessage, ''); + }); + }); + }); + }); +} diff --git a/test/controllers/function._test.dart b/test/controllers/function._test.dart new file mode 100644 index 0000000..962eda0 --- /dev/null +++ b/test/controllers/function._test.dart @@ -0,0 +1,27 @@ +import 'dart:convert'; + +import 'package:bfast/controller/function.dart'; +import 'package:bfast/model/raw_response.dart'; +import 'package:flutter_test/flutter_test.dart'; + +var okResponse = RawResponse(body: '''{"total": 1000}''', statusCode: 200); +var errResponse = + RawResponse(body: '''{"message": "no username"}''', statusCode: 400); + +void main() { + group("FunctionController", () { + group("executeHttpFunction", () { + test("should return the data from http request", () async { + var response = await executeHttp(() => okResponse); + expect(response, jsonDecode(okResponse.body)); + }); + test("should throw if not ok", () async { + try { + await executeHttp(() => errResponse); + } catch (e) { + expect(e, '{"message":"no username"}'); + } + }); + }); + }); +} diff --git a/test/controllers/realtime._test.dart b/test/controllers/realtime._test.dart new file mode 100644 index 0000000..fdd9600 --- /dev/null +++ b/test/controllers/realtime._test.dart @@ -0,0 +1,114 @@ +import 'dart:async'; + +import 'package:bfast/controller/realtime.dart'; +import 'package:bfast/options.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group("RealtimeController", () { + group("realtimeServerUrl", () { + test("should return a database changes event url", () { + var app = App(applicationId: 'test', projectId: 'test'); + var testAppEventUrl = realtimeServerUrl(app); + expect(testAppEventUrl('/v2/__changes__'), + 'https://test-daas.bfast.fahamutech.com/v2/__changes__'); + }); + test( + "should return a database changes event url if databaseURL is specified", + () { + var app = App( + applicationId: 'test', + projectId: 'test', + databaseURL: 'https://test.d'); + var testAppEventUrl = realtimeServerUrl(app); + expect(testAppEventUrl('/v2/__changes__'), + 'https://test.d/v2/__changes__'); + }); + test("should return a function url for event", () { + var app = App(applicationId: 'test', projectId: 'test'); + var testAppEventUrl = realtimeServerUrl(app); + expect(testAppEventUrl('/sale'), + 'https://test-faas.bfast.fahamutech.com/sale'); + }); + test("should return a function url for event if functionsURL specified", + () { + var app = App( + applicationId: 'test', + projectId: 'test', + functionsURL: 'https://test.a'); + var testAppEventUrl = realtimeServerUrl(app); + expect(testAppEventUrl('/sale'), 'https://test.a/sale'); + }); + test( + "should return a function url for event if functionsURL specified and event is not absolute", + () { + var app = App( + applicationId: 'test', + projectId: 'test', + functionsURL: 'https://test.a'); + var testAppEventUrl = realtimeServerUrl(app); + expect(testAppEventUrl('sale'), 'https://test.a/sale'); + }); + }); + group("RealtimeFactory", () { + var realtime = RealtimeFactory(); + var app = App(applicationId: 'kazisquare', projectId: 'kazisquare'); + group("singleton", () { + test("should return single object", () { + expect(realtime, RealtimeFactory()); + }); + }); + group("receive & send", () { + test("should create and send if not exist", () async { + var completer = Completer(); + var receiver = (d) { + expect(d, { + "body": {"body": 1} + }); + completer.complete(); + }; + var dbChanges = realtime.receive(app, '/echo'); + dbChanges(receiver); + var send = realtime.send(app, '/echo'); + send({"body": 1}); + return completer.future; + }); + }); + group("count & close", () { + var app2 = App(applicationId: 't', projectId: 't'); + test("must be idempotent for same app", () async { + var receiver = (d) => ''; + var dbChanges = realtime.receive(app, '/echo'); + dbChanges(receiver); + var send = realtime.send(app, '/echo'); + send({"body": 1}); + send({"body": 2}); + expect(realtime.count(app), 1); + realtime.close(app, '/echo'); + }); + test("must count all remain socket", () async { + [1,2].forEach((x) { + var receiver = (d) => ''; + var dbChanges = realtime.receive(app, '/echo/$x'); + dbChanges(receiver); + var send = realtime.send(app, '/echo/$x'); + send({"body": 1}); + }); + expect(realtime.count(app), 2); + }); + test("must close all of an app", () { + var dbChanges = realtime.receive(app2, '/echo'); + dbChanges((d)=>2); + realtime.closeAllOfApp(app); + expect(realtime.count(app), 0); + expect(realtime.count(app2), 1); + }); + test("must close all regardless app", (){ + realtime.closeAll(); + expect(realtime.count(app2), 0); + expect(realtime.count(app), 0); + }); + }); + }); + }); +} diff --git a/test/controllers/rules.controller._test.dart b/test/controllers/rules.controller._test.dart deleted file mode 100644 index c2e8820..0000000 --- a/test/controllers/rules.controller._test.dart +++ /dev/null @@ -1,255 +0,0 @@ -import 'package:bfast/adapter/query.dart'; -import 'package:bfast/bfast_config.dart'; -import 'package:bfast/controller/auth.dart'; -import 'package:bfast/controller/rest.dart'; -import 'package:bfast/controller/rules.dart'; -import 'package:bfast/model/QueryModel.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import '../mock/CacheMockController.dart'; - -void main() { - var restController = new BFastHttpClientController(); - var cache = new MockCacheController({"name": "Doe"}); - var authAdapter = - new AuthController(restController, cache, BFastConfig.DEFAULT_APP); - var ruleController = new RulesController(authAdapter); - var appCredential = AppCredentials('test', 'test', appPassword: 'test'); - - group("Create Rule Unit Test", () { - test("should return a create rule when all element supplied", () async { - var createRule = await ruleController.createRule( - 'test', {"name": "doe"}, appCredential); - expect(createRule, { - 'applicationId': 'test', - 'createtest': {'name': 'doe', 'return': []}, - 'token': null - }); - expect(createRule['applicationId'], 'test'); - expect(createRule['token'], null); - expect(createRule['createtest'], {'name': 'doe', 'return': []}); - }); - - test("should return a create rule when all element supplied as a list", - () async { - var createRule = await ruleController.createRule( - 'test', - [ - {"name": "doe"} - ], - appCredential, - ); - expect(createRule, { - 'applicationId': 'test', - 'createtest': [ - {'name': 'doe', 'return': []} - ], - 'token': null - }); - expect(createRule['applicationId'], 'test'); - expect(createRule['token'], null); - expect(createRule['createtest'], [ - {'name': 'doe', 'return': []} - ]); - }); - - test( - "should return a create rule when all element supplied as a list and return fields supplied", - () async { - var createRule = await ruleController.createRule( - 'test', - [ - { - "name": "doe", - }, - { - "name": "doe2", - } - ], - appCredential, - RequestOptions(returnFields: ['name'])); - expect(createRule, { - 'applicationId': 'test', - 'createtest': [ - { - "name": "doe", - 'return': ['name'] - }, - { - "name": "doe2", - 'return': ['name'] - }, - ], - 'token': null - }); - expect(createRule['applicationId'], 'test'); - expect(createRule['token'], null); - expect(createRule['createtest'], [ - { - 'name': 'doe', - 'return': ['name'] - }, - { - 'name': 'doe2', - 'return': ['name'] - }, - ]); - }); - - test('should return create rule with masterKey supplied', () async { - var createRule = await ruleController.createRule('test', {"name": "doe"}, - appCredential, RequestOptions(userMasterKey: true)); - expect(createRule, { - 'applicationId': 'test', - 'masterKey': 'test', - 'createtest': {'name': 'doe', 'return': []}, - 'token': null - }); - expect(createRule['applicationId'], 'test'); - expect(createRule['masterKey'], 'test'); - expect(createRule['token'], null); - expect(createRule['createtest'], {'name': 'doe', 'return': []}); - }); - - test( - 'should return create rule with returnFields supplied and use masterKey false', - () async { - var createRule = await ruleController.createRule( - 'test', - {"name": "doe"}, - appCredential, - RequestOptions(userMasterKey: false, returnFields: ['name'])); - expect(createRule, { - 'applicationId': 'test', - 'createtest': { - 'name': 'doe', - 'return': ['name'] - }, - 'token': null - }); - expect(createRule['applicationId'], 'test'); - expect(createRule['token'], null); - expect(createRule['createtest'], { - 'name': 'doe', - 'return': ['name'] - }); - }); - - test('should not return a create rule when domain name is null', () async { - try { - await ruleController.createRule(null, {"name": "doe"}, appCredential); - } catch (e) { - expect(true, e.toString().contains("domain must not be null")); - } - }); - - test('should not return a create rule when data parameter is null', - () async { - try { - await ruleController.createRule('test', null, appCredential); - } catch (e) { - expect(true, e != null); - expect(e, {'message': 'please provide data to save'}); - expect(e['message'], 'please provide data to save'); - } - }); - - test('should not return a create rule when appCredential parameter is null', - () async { - try { - await ruleController.createRule('test', {'name': 'doe'}, null); - } catch (e) { - expect(true, e != null); - } - }); - }); - group("Delete Rule Unit Test", () { - test("should return delete rule", () async { - var query = QueryModel(); - query.filter = {'name': 'doe'}; - var deleteRule = - await ruleController.deleteRule('test', query, appCredential); - expect(deleteRule, { - 'applicationId': 'test', - 'deletetest': { - 'id': null, - 'filter': {'name': 'doe'} - }, - 'token': null - }); - expect(deleteRule['applicationId'], 'test'); - expect(deleteRule['deletetest'], { - 'id': null, - 'filter': {'name': 'doe'} - }); - expect(deleteRule['deletetest']['id'], null); - expect(deleteRule['deletetest']['filter'], {'name': 'doe'}); - }); - test("should return delete rule with a masterKey", () async { - var query = QueryModel(); - query.filter = {'name': 'doe'}; - var deleteRule = await ruleController.deleteRule( - 'test', query, appCredential, RequestOptions(userMasterKey: true)); - expect(deleteRule, { - 'applicationId': 'test', - 'masterKey': 'test', - 'deletetest': { - 'id': null, - 'filter': {'name': 'doe'} - }, - 'token': null - }); - expect(deleteRule['applicationId'], 'test'); - expect(deleteRule['deletetest'], { - 'id': null, - 'filter': {'name': 'doe'} - }); - expect(deleteRule['deletetest']['id'], null); - expect(deleteRule['deletetest']['filter'], {'name': 'doe'}); - }); - - test('should throw error when domain is null', () async { - try { - var query = QueryModel(); - query.filter = {"name": "doe"}; - await ruleController.deleteRule(null, query, appCredential); - } catch (e) { - expect(true, e.toString().contains("domain must not be null")); - } - }); - - test('should not return a delete rule when data parameter is null', - () async { - try { - await ruleController.deleteRule('test', null, appCredential); - } catch (e) { - print(e); - expect(true, e != null); - expect(true, e.toString().contains("query must not be null")); - } - }); - - test('should not return a delete rule when appCredential parameter is null', - () async { - try { - await ruleController.createRule('test', {'name': 'doe'}, null); - } catch (e) { - print(e); - expect(true, e != null); - } - }); - }); - group("Query Rule Unit Test", () { - test("should return a query rule", () async { - var query = QueryModel(); - var queryRule = - await ruleController.queryRule('test', query, appCredential); - print(queryRule); - expect(queryRule, { - 'applicationId': 'test', - 'token': null, - 'querytest': {} - }); - }); - }); -} diff --git a/test/mock/CacheMockController.dart b/test/mock/CacheMockController.dart deleted file mode 100644 index 373e3a1..0000000 --- a/test/mock/CacheMockController.dart +++ /dev/null @@ -1,43 +0,0 @@ -import 'package:bfast/adapter/cache.dart'; -import 'package:bfast/adapter/query.dart'; - -class MockCacheController extends CacheAdapter { - Map _mockData = {}; - - MockCacheController(Map mockData) { - this._mockData = mockData; - } - - @override - bool cacheEnabled({RequestOptions options}) { - return false; - } - - @override - Future clearAll() async { - return true; - } - - @override - Future get(String identifier) async { - return this._mockData as T; - } - - @override - Future> keys() async { - return []; - } - - @override - Future remove(String identifier, {bool force}) async { - if (identifier == 'ok') { - return true; - } - return false; - } - - @override - Future set(String identifier, T data, {int dtl}) async { - return data; - } -} diff --git a/test/options._test.dart b/test/options._test.dart new file mode 100644 index 0000000..9e97a7a --- /dev/null +++ b/test/options._test.dart @@ -0,0 +1,110 @@ +import 'package:bfast/options.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group("Options", () { + group("databaseURL", () { + var url = 'https://test.test'; + var app = App(applicationId: 'test', projectId: 'test', databaseURL: url); + test("should return specified url if start with http", () { + var testDbUrl = databaseURL(app); + expect(testDbUrl(''), url); + }); + test("should return specified url with suffix if start with http", () { + var testDbUrl = databaseURL(app); + expect(testDbUrl('/database'), '$url/database'); + }); + test("should return default database url", () { + var _app = App(applicationId: 'test', projectId: 'test'); + var testDbUrl = databaseURL(_app); + expect(testDbUrl(''), + 'https://${_app.projectId}-daas.bfast.fahamutech.com/v2'); + }); + test("should return default database url with suffix", () { + var _app = App(applicationId: 'test', projectId: 'test'); + var testDbUrl = databaseURL(_app); + expect(testDbUrl('/v3'), + 'https://${_app.projectId}-daas.bfast.fahamutech.com/v2/v3'); + }); + test("should return null default if no app", () { + [1, '', 'a', {}, () => 2, [], null].forEach((x) { + var dbUrl = databaseURL(x as dynamic); + expect(dbUrl(''), 'https://null-daas.bfast.fahamutech.com/v2'); + }); + }); + test("should return null default if no app nor suffix", () { + [1, '', {}, () => 2, [], null].forEach((x) { + var dbUrl = databaseURL(x); + expect(dbUrl(x), 'https://null-daas.bfast.fahamutech.com/v2'); + }); + }); + }); + group("functionsURL", () { + var url = 'https://functons.test'; + var app = + App(applicationId: 'test', projectId: 'test', functionsURL: url); + test("should return path url if start with http", () { + var testDbUrl = functionsURL(app); + var _url = 'https://functions.test/a'; + expect(testDbUrl(_url), _url); + }); + test("should return specified url if start with http", () { + var testDbUrl = functionsURL(app); + expect(testDbUrl(''), url); + }); + test("should return specified url with path if start with http", () { + var testDbUrl = functionsURL(app); + expect(testDbUrl('/login'), '$url/login'); + }); + test("should return default functions url", () { + var _app = App(applicationId: 'test', projectId: 'test'); + var testDbUrl = functionsURL(_app); + expect(testDbUrl(''), + 'https://${_app.projectId}-faas.bfast.fahamutech.com'); + }); + test("should return default functions url with suffix", () { + var _app = App(applicationId: 'test', projectId: 'test'); + var testDbUrl = functionsURL(_app); + expect(testDbUrl('/v3'), + 'https://${_app.projectId}-faas.bfast.fahamutech.com/v3'); + }); + test("should return null default if no app", () { + [1, '', 'a', {}, () => 2, [], null].forEach((x) { + var dbUrl = functionsURL(x as dynamic); + expect(dbUrl(''), 'https://null-faas.bfast.fahamutech.com'); + }); + }); + test("should return null default if no app nor path", () { + [1, '', {}, () => 2, [], null].forEach((x) { + var dbUrl = functionsURL(x); + expect(dbUrl(x), 'https://null-faas.bfast.fahamutech.com'); + }); + }); + }); + group("cacheDatabaseName", () { + var app = App(applicationId: 'test', projectId: 'test'); + test("return a database url string if app exist", (){ + var tableName = cacheDatabaseName(app); + expect(tableName('sales'), '/bfast/test/sales'); + }); + test("return a database url string if app not exist", (){ + [1,'','a',[],{},()=>1].forEach((x) { + var tableName = cacheDatabaseName(x); + expect(tableName('sales'), '/bfast/cache/sales'); + }); + }); + test("should ignore table if its not string and app exist", (){ + [1,[],{},()=>1].forEach((x) { + var tableName = cacheDatabaseName(app); + expect(tableName(x), '/bfast/test'); + }); + }); + test("should ignore table if its not string and app not exist", (){ + [1,[],{},()=>1].forEach((x) { + var tableName = cacheDatabaseName(x); + expect(tableName(x), '/bfast/cache'); + }); + }); + }); + }); +} diff --git a/test/ubongo._test.dart b/test/ubongo._test.dart deleted file mode 100644 index cc2188f..0000000 --- a/test/ubongo._test.dart +++ /dev/null @@ -1,16 +0,0 @@ -import 'package:bfast/bfast.dart'; -import 'package:bfast/bfast_config.dart'; -import 'package:flutter_test/flutter_test.dart'; - -void main(){ - TestWidgetsFlutterBinding.ensureInitialized(); - group("Test ubongo", (){ - BFast.init(AppCredentials('ubongokids', 'ubongokids')); - test("save object", () async { - var response = await BFast.database() - .domain('content') - .save({"name": "test category"}); - print(response); - }); - }); -} \ No newline at end of file diff --git a/test/util._test.dart b/test/util._test.dart new file mode 100644 index 0000000..c4ba106 --- /dev/null +++ b/test/util._test.dart @@ -0,0 +1,49 @@ +import 'package:bfast/util.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + group("Utilities", () { + group("ifDoElse", () { + test("should execute do if true", () { + var one = ifDoElse((_) => true, (a) => 1, (_) => 4); + expect(one(3), 1); + }); + test('should execute else if not true', () { + [false, 1, '', () => {}, {}, 'abc'].forEach((x) { + var two = ifDoElse((_) => x==true, (a) => 1, (b) => 2); + expect(two(4), 2); + }); + }); + }); + group("ifThrow", () { + test('should throw if true', () { + try { + var err = ifThrow((_) => true, (_) => 'err'); + err(1); + } catch (e) { + expect(e, 'err'); + } + }); + test('should return pass if false', () { + [false, 1, '', () => {}, {}, 'abc'].forEach((x) { + var pass = ifThrow((_) => x, () => 3); + expect(pass(2), 2); + }); + }); + }); + group("combine", () { + test("combine functions", () async { + f1(a) => 1 + a; + f2(b) => b * 2; + var j = compose([f2, f1]); + expect(await j(1), 4); + }); + }); + group("map", () { + test("should execute a map function", () { + var to100 = map((x) => 100); + expect(to100('x'), 100); + }); + }); + }); +}