Dynamic_Call

pub package Null Safety

CI GitHub Tag New Commits Last Commits Pull Requests Code size License

Dynamic Call framework, including API calls integrations (HTTP, RESTful).

Paradigm

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.

Usage

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&current_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() ;

}

mercury_client

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.

Features and bugs

Please file feature requests and bugs at the issue tracker.

Author

Graciliano M. Passos: gmpassos@GitHub.

License

Dart free & open-source license.

Libraries

dynamic_call