Dynamic Call framework, including API calls integrations (HTTP, RESTful).
The main idea with this framework is to separate the desired behavior of an external call
from the real execution/implementation of the call.
The call
behavior specifies:
- Input fields.
- Separates execution output and call output, that generally have different types, what demands mapping.
- Call output (
DynCallType
): parsing execution response to predefined types. - Output filter Function: Useful for mapping the execution response to an Object type.
- If the call can be retried in case of errors.
- Attach callbacks.
The execution implementation can be of any kind. As example, we use DynCallHttpExecutor
,
generated by DynCallHttpExecutorFactory
, that will perform HTTP requests to execute
the calls.
With this framework is possible to reuse the call
definitions in different projects,
with different execution layers, for example, different RESTful APIs.
dynamic_call
Also, helps to write tests, since you don't need to implement an API response to mock
executions, you can use DynCallStaticExecutor
, that defines a response for a call in the tests.
A simple usage example:
import 'package:dynamic_call/dynamic_call.dart';
import 'dart:async' ;
// Entity for logged user:
class UserLogin {
static UserLogin parse(dynamic json) {
if ( json is Map ) {
var username = json['username'] ;
if (username != null) {
try {
String id = json['id'].toString() ;
String name = json['name'] ;
return UserLogin(id, username) ;
}
catch (e) {
print(e);
}
}
}
return null ;
}
String _id ;
String get id => _id ;
String _username ;
String get username => _username ;
UserLogin(this._id, this._username);
}
// A generic AppSystem that can be used in many projects:
class AppSystem {
final DynCall<dynamic,UserLogin> callLogin = DynCall<dynamic,UserLogin>( ['username','pass'] , DynCallType.JSON , (dynamic userJson) => UserLogin.parse(userJson) , true ) ;
Future<UserLogin> login(String username, String pass, void Function(UserLogin user) onUserLogin) {
return callLogin.call( {'username': username, 'pass': pass} , onUserLogin) ;
}
final DynCall<bool,bool> callLogout = DynCall<bool,bool>( [] , DynCallType.BOOL , null, true ) ;
Future<bool> logout() {
return callLogout.call() ;
}
final DynCall<dynamic,UserLogin> callRegister = DynCall<dynamic,UserLogin>( ['name', 'email', 'username', 'password'] , DynCallType.JSON , (dynamic userJson) => UserLogin.parse(userJson) ) ;
Future<UserLogin> register(String name, String email, String username, String pass, void Function(UserLogin user) onRegisteredUser) {
return callRegister.call( {'name': name, 'email': email, 'username': username, 'password': pass} , onRegisteredUser) ;
}
final DynCall<bool,bool> callChangePassword = DynCall( ['username','currentPass','newPass'] , DynCallType.BOOL) ;
Future<bool> changePassword(String username, String currentPass, String newPass) {
return callChangePassword.call( {'username': username, 'currentPass': currentPass, 'newPass': newPass} ) ;
}
}
// Example of usage of the system and integrate with a WS API:
main() async {
var appSystem = AppSystem() ;
///////////////////////////////////////////
////// Configure Dynamic Call Executors:
var httpClient = DynCallHttpClient('https://your.domain') ;
// DynCallExecutor with HttpClient with base URL: https://your.domain/path/to/api-1
var executorFactory = DynCallHttpExecutorFactory( httpClient , "path/to/api-1" ) ;
// POST https://your.domain/path/to/api-1/login
// Authorization: Basic BASE64($username:$pass)
executorFactory.call( appSystem.callLogin ).executor( HttpMethod.POST, path: "login", authorizationFields: ['username', 'pass'] ) ;
// GET https://your.domain/path/to/api-1/logout
executorFactory.call( appSystem.callLogout ).executor( HttpMethod.GET, path: "logout" ) ;
// POST https://your.domain/path/to/api-1/register
// Content-Type: application/x-www-form-urlencoded; charset=UTF-8
// name=$name&email=$email&username=$username&password=$password
executorFactory.call( appSystem.callRegister ).executor( HttpMethod.POST, path: "register", parametersMap: {'*': '*'} ) ;
// POST https://your.domain/path/to/api-1/changePassword
// Content-Type: application/x-www-form-urlencoded; charset=UTF-8
// username=$username¤t_password=$currentPass&password=$newPass
executorFactory.call( appSystem.callChangePassword ).executor( HttpMethod.POST, path: "changePassword",
parametersMap: {'*': '*', 'currentPass': 'current_password', 'newPass': 'password'},
errorResponse: false
) ;
///////////////////////////////////////////
////// Usage of AppSystem:
var loggedUser = await appSystem.login('joe', 'pass123', (user) => notifyLogin(user) ) ;
// ... Or register a new user (back-end automatically logins with register user):
var loggedUser = await appSystem.register('Joe Smith', 'joe@email.com', 'joe', 'pass123', (user) => notifyLogin(user) ) ;
//... Change some user password:
bool changePassOK = await appSystem.changePassword('joe','pass123','pass456') ;
//... Logout current user:
bool logoutOK = await appSystem.logout() ;
}
The HTTP executor DynCallHttpExecutor
uses the package mercury_client,
that is a portable HTTP Client, that can work in the Browser, Flutter, VM and native.
- See the dynamic_call example.
- See the mercury_client example.
Please file feature requests and bugs at the issue tracker.
Graciliano M. Passos: gmpassos@GitHub.
Dart free & open-source license.