Implementing Pull and Push functions

Tags:

Updated:

7 minute read

It is evident that implementating adapter requires extensive knowledge about the API which you want to connect to, such that you can clearly implement and optimize the calls made to the APIs. As a developer you can implement the adapter in such a way, so that the data is perfectly transferred from one application to another without compormising performance and also giving most of the flexibility for cloud integrations. Before you move ahead on developing the connector I hope you have installed the pre-requisites correctly on the system and you have all the prebuilt code ready on your visual studio. Also you need to make sure that you follow our best practices and guidelines provided for adapter development.

Implementing the IAdapter interface

An IAdapter interface is a hook to specify the code which will run to fetch or push data to and from from the application. The main method that needs to be implemented while implementing IAdapter is the Execute, which provides you with ExecutionType which lets you specify code that needs to be implemented to invoke Request to the application.

An OperationType specifies the type of the execution which you need to perform.

OperationType Description
GET Adapter receives GET as ExecutionType when the adapter need to pull data from source application. Remember, during this operation, you will have to work with ActionFilter and you should not look for TransformedResponse.
POST Adapter receives POST as ExecutionType when the adapter need to push data to the target application. The adapter receives the actual data in TransformedResponse as XML data and the target application need to push the same to the application end.

Protip: The basic function of an adapter method Execute is to pull/push data (convert the data to XML) and set it to APPSeCONNECT using ReturnMessage<string> {: .notice–info}.

Implementing GET

The GET execution of an adapter require to do the following steps :

  • Parse ActionFilter to generate the Request structure. An ActionFilter is a Hierarchical Key-Value pair data received from APPSeCONNECT cloud.
  • Get Credential to connect to the target application.
  • Fetch data from the target application and push it the APPSeCONNECT using the api ReturnMessage.SetSuccess.

The Credential received by the interface will allow you to create the request body so as to create a communication with the end application. Let us look at the code below :


private ReturnMessage<string> ExecuteGetOperation(ExecutionSettings settings)
{
    var returnMessage = new ReturnMessage<string>();
    var returnAPIData = new ReturnMessage<bool>();

    var logger = this._context.Logger; // Gets a logger instance
    logger.StatusLog("Trying to pull data from application", "Inside Adapter GET");

    string responseData = string.Empty;
    string requestData = string.Empty;
    try
    {

        var credentials = this._context.GetConnectionDetails<CredentialModel>();
        WebServicesUtility interactAPI = new WebServicesUtility();
      
        var commandProcessor = settings.GetCommandProcessor(Protocol.REST);
        requestData = commandProcessor.PrepareCommand();
       
        if (settings.CalledFrom != CallerType.ReSync) //Represents a normal call
        {
            returnAPIData = interactAPI.InteractGetByPage(requestData, credentials, settings.EntityName);
            if (!returnAPIData.Value)
            {
                returnMessage.SetError(returnAPIData.Message);
            }
            else
            {
                AssignPrimaryKeys(settings, returnAPIData, credentials);
                returnMessage.SetSuccess("Data fetched successfully from Bigcommerce", returnAPIData.Message);
                resource.WriteMaxCreateDate(returnAPIData.Message, "date_created", settings.EntityName);
            }
        }
        
    }

    catch (WebException ex)
    {
        returnMessage.AddException(ex);
        returnMessage.SetError(ex.Message);
    }
    catch (Exception ex)
    {
        returnMessage.AddException(ex);
    }

    return returnMessage;
}

In the above code you can see an implementation of adapter GET method. This piece of code will be executed when a workflow encounters a Get node. If you properly inspect the method, you can see, that the method receives an ExecutionSettings and also it uses _context to get various data from APPSeCONNECT. The _context is an object that lets you get data application wide. This object does not change between calls to the adapter and also represents the contextual reference of the process on the application.

The line this._context.Logger gets the logger class, which helps you to write debug, status or information over APPSeCONNECT logging system.

The next important consideration is the Credential object. You can call this._context.GetConnectionDetails to retrieve the credentials which is used to connect to the application. Remember, the credentials are generally put by the customer on the local or cloud agent, and you need a model created as a class inside the project to access the deserialzied version of the object.

protip: You can also manually read / write json statements, using the overload GetConnectionDetails but it is always preferrable to have a model class. {: .notice–info}.

The settings.GetCommandProcessor(Protocol.REST)allows the user to get an url generated from the Action filters put in APPSeCONNECT cloud. We specifically generate the action filter parsers for REST and or SOAP requests, so if it does not suit your need, you can easily create an ActionFilter parser yourself.

Finally, the interactAPI.InteractGetByPage is an individual request to the application which helps in getting data from the application. Once you get the data, you can assign keys to the Resync Bucket, such that the data that is been retrieved could be properly tracked by the application. If you have defined Primary Keys for the schema in use, the data will be automatically created for you from APPSeCONNECT.

Once you retrieve the document correctly, you can pass the document to APPSeCONNECT using returnMessage.SetSuccess method. Remember, you should always communicate with APPSeCONNECT in XML data formats, so if the format is something else, like JSON or CSV etc., you need to convert it to XML before passing it to APPSeCONNECT.

Implementing Post :

Unlike GET, POST will receive the transformed response in the adapter using our SDK, which one needs to push directly to the application and set the response to appseconnect back. Let us look at the code below :


private ReturnMessage<string> ExecutePostOperation(ExecutionSettings settings)
{
    var returnMessage = new ReturnMessage<string>();
    var logger = this._context.ApplicationUtility.Logger;
    string entityName = settings.EntityName; //returns the main entity name
    string actionName = settings.ActionName; //returns the main action name
    var credential = this._context.GetConnectionDetails<CredentialModel>();

    string responseData = string.Empty;
    logger.StatusLog("Trying to push data from application", "Inside Adapter POST");
    WebServicesUtility interactAPI = new WebServicesUtility();
    var requestData = settings.TransformedResponse;

    IEnumerable<XElement> elements = XElement.Parse(requestData).Elements();
    XmlDocument nodes = new XmlDocument();
    nodes.LoadXml(requestData);
    var envelopesNode = nodes["Envelops"];
    if (envelopesNode == null)
    {
        logger.StatusLog("No data found to push in Bigcommerce");
        returnMessage.SetSuccess("No data found to push in Bigcommerce");
        return returnMessage;
    }
    var outputs = new XElement("Envelopes");
    var dataIndex = 0;
    foreach (XElement element in elements)
    {
        string destinationKey = "successfully";
        var httpResponse = interactAPI.InteractPush(element.ToString(), actionName, credential, settings, returnMessage);
        try
        {
            if (httpResponse.Message != null)
            { 
                outputs.Add(XElement.Parse(httpResponse.Message));
            }
            else
            {
                logger.ErrorLog("Failed to Push in BigCommerce");
                settings.AssignErrorEntity(dataIndex, "", "");
            }
        }
        catch (Exception ex)
        {
            logger.ErrorLog("Exception Occured at parsing response", ex.Message);
            settings.AssignErrorEntity(dataIndex, "", "");
        }
        dataIndex++;

    }
    returnMessage.Value = string.Concat(outputs);
    logger.InfoLog("Data push operation completed successfully", "Push operation completed");
    returnMessage.SetSuccess("Data push operation completed successfully");

    return returnMessage;

}

If you inspect the code above, similar to the one in GET, the POST API also receives the ApplicationContext as well as ExecutionSettings inside it. You can clearly get Credential again using the same API, and use the same to communicate with the application. Once you receive the credential, you can use settings.TransformedResponse to get the XML generated from APPSeCONNECT, which you need to push to the Application and get response. In the above code, you can see the API does not support a batch operation, and hence the request is being split in chunks and each xml is requested separately.

The output of the response is returned back using returnMessage.SetSuccess which can be used in the workflow for future use.

Handling Resync Data

Resync in case of APPSeCONNECT are special data mentioned through adapter, which are required to resync an errornous data from the application itself. As an adapter developer, you need to take care of Resync while implementing the adapter code. In case of Resync call, an individual request is made to the application (generally using the primary key field) and the data is then retrieved or resynced using the request structure. Let us see how the resync code looks like :

foreach (var entity in settings.EntityData)
{
    commandProcessor = settings.GetCommandProcessor(Protocol.REST);
    requestData = commandProcessor.PrepareCommand();
    string entityCode = entity.SourceCode;
    requestData = requestData.TrimEnd('?');
    requestData = string.Concat(requestData, "/", entityCode);
    returnAPIData = interactAPI.InteractGet(requestData, credentials, settings.EntityName);
}
returnMessage.SetSuccess("Records pulled for resync entities ", returnAPIData.Message);

Here in case of the code above, the resync data is sent using the object settings.EntityData. You can loop through the data blocks to execute the resync. You can see, the InteractGet is called here to resync the requestData passed in from APPSeCONNECT. In case of POST request as well, you can use the settings.TransformedResponse to resync the data. To detect whether the call to an adapter is for Resync or normal call, you can use the property settings.CalledFrom.

Various other methods in IAdapter interface

There are some additional properties that an IAdapter interface hosts, which lets you process various part of data.

  • Resource : You needed to implement a property Resource. This is an implementation of IAppResource inside the adpater such that APPSeCONNECT could call the methods. Remember, if you do not specify an object, the transformation in APPSeCONNECT could not call additional functions.
  • ValidateProcess: This method is used to validate the execution data before actual processing. Feel free to put logic that identifies whether the data passed to the Execute or the Request structure is valid or not.