To OVERVIEW

Client Library

Step 3. Designing an HTTP Client

The standard way of communicating with a microservice is via the HTTP protocol. It allows calling microservices that work on a separate server, or in their own container. Our example microservice uses a simplified version of the HTTP protocol that is automatically generated using the Commandable pattern.
Create a new class for the Commandable REST client. Create an implementation for each of the microservice’s methods. This is done by calling the REST API’s methods using the methods of the parent Commandable REST client, passing the necessary set of parameters, and then processing the response’s result. Since the answer from the client is returned as JSON, some programming languages will require that you first convert it to an instance with a specific type. Be sure to remember this when writing your HTTP clients.

The client’s resulting code is listed below:

/src/version1/BeaconsHttpClientV1.ts

import { FilterParams } from 'pip-services3-commons-node';
import { PagingParams } from 'pip-services3-commons-node';
import { DataPage } from 'pip-services3-commons-node';
import { CommandableHttpClient } from 'pip-services3-rpc-node';
import { BeaconV1 } from '../../../src/data/version1/BeaconV1';
import { IBeaconsClientV1 } from './IBeaconsClientV1';
export class BeaconsHttpClientV1 extends CommandableHttpClient implements IBeaconsClientV1 {    
  public constructor() {        
    super('v1/beacons');    
  }
  public getBeacons(correlationId: string, filter: FilterParams, paging: PagingParams, 
  callback: (err: any, page:   DataPage<BeaconV1>) => void): void {        
    this.callCommand('get_beacons', correlationId, {filter: filter, paging: paging }, callback);    
  }
  public getBeaconById(correlationId: string, beaconId: string, 
    callback: (err: any, beacon: BeaconV1) => void): void {        
    this.callCommand('get_beacon_by_id', correlationId, {  beacon_id: beaconId }, callback ); 
  }
  public getBeaconByUdi(correlationId: string, udi: string, 
    callback: (err: any, beacon: BeaconV1) => void): void {        
    this.callCommand( 'get_beacon_by_udi', correlationId, { udi: udi }, callback);    
  }
  public calculatePosition(correlationId: string, siteId: string, udis: string[],
    callback: (err: any, position: any) => void): void {
    this.callCommand('calculate_position', correlationId, {site_id: siteId, udis: udis },  callback);
  }
  public createBeacon(correlationId: string, beacon: BeaconV1, 
    callback: (err: any, beacon: BeaconV1) => void): void {        
    this.callCommand('create_beacon', correlationId, {beacon: beacon}, callback );    
  }
  public updateBeacon(correlationId: string, beacon: BeaconV1, 
    callback: (err: any, beacon: BeaconV1) => void): void {        
    this.callCommand('update_beacon', correlationId, {beacon: beacon}, callback);
  }
  public deleteBeaconById(correlationId: string, beaconId: string,        
    callback: (err: any, beacon: BeaconV1) => void): void {        
    this.callCommand('delete_beacon_by_id', correlationId, {beacon_id: beaconId}, callback);    
  }
}

To be sure that our code works as intended, we should perform some functional testing. Test the Commandable HTTP REST client using the class with tests that we developed in the previous step. To do this, create an instance of the HTTP REST client and pass it as a parameter to our set of tests.
An example implementation of the tests can be found in the example’s repository

Once installed, check that the installation was completed successfully by running the following commands from your console:

java -version
mvn -v

If everything was installed successfully, the screen will display the latest version of the Java programming language, as well as the version of Maven that was installed.

The client’s resulting code is listed below:

/clients/version1/BeaconsHttpClientV1.go

package clients
import (
  "reflect"
  bdata "github.com/pip-services-samples/pip-data-microservice-go/data/version1"
  cdata "github.com/pip-services3-go/pip-services3-commons-go/data"
  rpcclient "github.com/pip-services3-go/pip-services3-rpc-go/clients"
)
var (
  beaconV1DataPageType = reflect.TypeOf(&bdata.BeaconV1DataPage{})
  beaconV1Type = reflect.TypeOf(&bdata.BeaconV1{})
  geoPointV1Type = reflect.TypeOf(&bdata.GeoPointV1{})
)
type BeaconsHttpClientV1 struct {
  *rpcclient.CommandableHttpClient
}

func NewBeaconsHttpClientV1() *BeaconsHttpClientV1 {
  bhc := BeaconsHttpClientV1{}
  bhc.CommandableHttpClient = rpcclient.NewCommandableHttpClient("v1/beacons")
  return &bhc
}
func (c *BeaconsHttpClientV1) GetBeacons(correlationId string, filter *cdata.FilterParams, paging *cdata.PagingParams) (page *bdata.BeaconV1DataPage, err error) {
  params := cdata.NewEmptyStringValueMap()
  c.AddFilterParams(params, filter)
  c.AddPagingParams(params, paging)
  calValue, calErr := c.CallCommand(beaconV1DataPageType, "get_beacons", correlationId, params, nil)   if calErr != nil {
    return nil, calErr
  }
  page, _ = calValue.(*bdata.BeaconV1DataPage)
  return page, err
}

func (c *BeaconsHttpClientV1) GetBeaconById(correlationId string, beaconId string) (beacon *bdata.BeaconV1, err error) {
  params := cdata.NewEmptyStringValueMap()
  params.Put("beacon_id", beaconId)
  calValue, calErr := c.CallCommand(beaconV1Type, "get_beacon_by_id", correlationId, params, nil)
  if calErr != nil {
    return nil, calErr
  }
  beacon, _ = calValue.(*bdata.BeaconV1)
  return beacon, err
}

func (c *BeaconsHttpClientV1) GetBeaconByUdi(correlationId string, udi string) (beacon *bdata.BeaconV1, err error) {
  params := cdata.NewEmptyStringValueMap()
  params.Put("udi", udi)
  calValue, calErr := c.CallCommand(beaconV1Type, "get_beacon_by_udi", correlationId, params, nil)
  if calErr != nil {
    return nil, calErr
  }
  beacon, _ = calValue.(*bdata.BeaconV1)
  return beacon, err
}

func (c *BeaconsHttpClientV1) CalculatePosition(correlationId string, siteId string, udis []string) (position *bdata.GeoPointV1, err error) {
  params := make(map[string]interface{})
  params["site_id"] = siteId
  params["udis"] = udis
  calValue, calErr := c.CallCommand(geoPointV1Type, "calculate_position", correlationId, nil, params)
  if calErr != nil {
    return nil, calErr
  }
  position, _ = calValue.(*bdata.GeoPointV1)
  return position, err
}

func (c *BeaconsHttpClientV1) CreateBeacon(correlationId string, beacon bdata.BeaconV1) (res *bdata.BeaconV1, err error) {
  params := make(map[string]interface{})
  params["beacon"] = beacon
  calValue, calErr := c.CallCommand(beaconV1Type, "create_beacon", correlationId, nil, params) if   calErr != nil {
    return nil, calErr
  }
  res, _ = calValue.(*bdata.BeaconV1)
  return res, err
}

func (c *BeaconsHttpClientV1) UpdateBeacon(correlationId string, beacon bdata.BeaconV1) (res *bdata.BeaconV1, err error) {
  params := make(map[string]interface{})
  params["beacon"] = beacon
  calValue, calErr := c.CallCommand(beaconV1Type, "update_beacon", correlationId, nil, params) if   calErr != nil {
    return nil, calErr
  }
  res, _ = calValue.(*bdata.BeaconV1)
  return res, err
}

func (c *BeaconsHttpClientV1) DeleteBeaconById(correlationId string, beaconId string) (beacon *bdata.BeaconV1, err error) {
  params := cdata.NewEmptyStringValueMap()
  params.Put("beacon_id", beaconId)
  calValue, calErr := c.CallCommand(beaconV1Type, "delete_beacon_by_id", correlationId, params, nil)
  if calErr != nil {
    return nil, calErr
  }
  beacon, _ = calValue.(*bdata.BeaconV1) return beacon, err
}

To be sure that our code works as intended, we should perform some functional testing. Test the Commandable HTTP REST client using the class with tests that we developed in the previous step. To do this, create an instance of the HTTP REST client and pass it as a parameter to our set of tests.
An example implementation of the tests can be found in the example’s repository

The client’s resulting code is listed below:

/lib/src/version1/BeaconsHttpClientV1.dart

import 'dart:async';import 'dart:convert';
import 'package:pip_services3_commons/pip_services3_commons.dart';
import 'package:pip_services3_rpc/pip_services3_rpc.dart';
import '../../data/version1/BeaconV1.dart';
import './IBeaconsClientV1.dart';
class BeaconsCommandableHttpClientV1 extends CommandableHttpClient  implements IBeaconsClientV1 {  
  BeaconsCommandableHttpClientV1([config]) : super('v1/beacons') {    
    if (config != null) {      
      configure(ConfigParams.fromValue(config));    
    }  
  }
  @override  
  Future<DataPage<BeaconV1>> getBeacons( String correlationId, FilterParams filter, PagingParams     paging) async {    
    var result = await callCommand('get_beacons', correlationId, {'filter': filter, 'paging':     paging});    
    return DataPage<BeaconV1>.fromJson(json.decode(result), (item) { 
      var beacon = BeaconV1();      
      beacon.fromJson(item);      
      return beacon;    
    });  
  }
  @override  
  Future<BeaconV1> getBeaconById(String correlationId, String beaconId) async {    
    var result = await callCommand('get_beacon_by_id', correlationId, {'beacon_id': beaconId});    
    if (result == null) return null;    
    var item = BeaconV1();    
    item.fromJson(json.decode(result));    
    return item;  
  }
  @override  
  Future<BeaconV1> getBeaconByUdi(String correlationId, String udi) async {    
  var result =  await callCommand('get_beacon_by_udi', correlationId, {'udi': udi});    
  if (result == null) return null;    
    var item = BeaconV1();    
    item.fromJson(json.decode(result));    
    return item;  
  }
  @override  
  Future<Map<String, dynamic>> calculatePosition(  String correlationId, String siteId, List<String>     udis) async {    
    var result = await callCommand('calculate_position', correlationId, {'site_id': siteId, 'udis':     udis});    
    return json.decode(result);  
  }
  @override  
  Future<BeaconV1> createBeacon(String correlationId, BeaconV1 beacon) async {    
    var result = await callCommand('create_beacon', correlationId, {'beacon': beacon});    
    if (result == null) return null;    
    var item = BeaconV1();    
    item.fromJson(json.decode(result));    
    return item;  
  }
  @override  
  Future<BeaconV1> updateBeacon(String correlationId, BeaconV1 beacon) async {    
    var result = await callCommand('update_beacon', correlationId, {'beacon': beacon});    
    if (result == null) return null;    
    var item = BeaconV1();    
    item.fromJson(json.decode(result));    
    return item;  
  }
  @override  
  Future<BeaconV1> deleteBeaconById( String correlationId, String beaconId) async {    
    var result = await callCommand( 'delete_beacon_by_id', correlationId, {'beacon_id': beaconId});    
    if (result == null) return null;    
    var item = BeaconV1();   
    item.fromJson(json.decode(result));  
    return item; 
  }
}

To be sure that our code works as intended, we should perform some functional testing. Test the Commandable HTTP REST client using the class with tests that we developed in the previous step. To do this, create an instance of the HTTP REST client and pass it as a parameter to our set of tests.
An example implementation of the tests can be found in the example’s repository

All tests should pass successfully.This finishes the development of our clients. As a result, we ended up with 2 clients: one for working from within a monolithic application, and another for working with the microservice from a different application, when utilizing a distributed architecture.

To simulate the service, let's create a test client in Step 4. Implementing Test Memory Client.