Using Electronic Reporting for API requests (II)

In this second part, I’m going to explain the code from the executable class. I’m using attachments because I find them very helpful during testing and even in production. However, you can also work in memory; at the end of this guide, I’ll explain how.

Let’s start with the executable class. We’ll begin by using a couple of directives, declaring a few class scope variables, and writing the main method.

using Microsoft.Dynamics365.LocalizationFramework;
using Microsoft.Dynamics.ElectronicReporting.Instrumentation;

internal final class TST_RunAPI
{
    TST_TableAPI tst_tableApi;       
    str nameExp;
    str nameImp;
    str expBody;
    str answer;
    static void main (Args _args)
    {        
        TST_RunAPI tst_RunAPI = new TST_RunAPI();
        // Select the record
        tst_RunAPI.fillTable(_args);
        // Execute ER to file attachment
        tst_RunAPI.runEr();    
        // Gets attachment and saves it on memory
        tst_RunAPI.docuRefToMemory();
        // Sends the request to the API
        tst_RunAPI.sendCom();
        // Executes ER with the answer
        tst_RunAPI.importAnswer();
        // Saves a copy of the answer in memory
        tst_RunAPI.saveAnswerToDocRef();
    }
}

This method finds the record from where we are calling the class:

protected void fillTable(Args _args)
    {
        select firstonly * from tst_tableApi 
            where tst_tableApi.RecId == _args.record().RecId;
    }

Runs the ER and saves it as an attachment:

protected void runEr()
    {  
        // Fills the user input parameters
        ERModelDefinitionInputParametersAction  obj = new ERModelDefinitionInputParametersAction();
        obj.addParameter('model/$Id', tst_tableApi.Id); 
       
        // The Destination is a file
        ERIFileDestination fileDestination = ERObjectsFactory::createFileDestinationAttachmentWithOtherDocuType(
            tst_tableApi);
        
        // Runs the ER, saving the filename to a variable, with 
        // Destinations and Parameters
        nameExp = ERObjectsFactory::createFormatMappingRunByFormatMappingId(
        AssetParameters::find().TST_ERExport, "", false)
        .withFileDestination(fileDestination)
        .withParameter(obj)
        .run();
    }

Finds the generated JSON and saves it in memory:

   protected void docuRefToMemory()
    {
        DocuRef drExpMessage;
        System.IO.StreamReader streamReader;     
   
        // Finds the created document
        select firstonly drExpMessage order by createdDateTime desc
                where drExpMessage.RefCompanyId == tst_tableApi.dataAreaId
                    && drExpMessage.RefRecId == tst_tableApi.RecId
                    && drExpMessage.RefTableId == tst_tableApi.TableId
                    && drExpMessage.Name == nameExp;

        if (!drExpMessage)
        {
            throw error("API body not found");
        }

        // Read the content of the document attachment into memory
        streamReader = new System.IO.StreamReader(
            DocumentManagement::getAttachmentStream(drExpMessage));
        expBody = streamReader.ReadToEnd();
        streamReader.Close();
        streamReader.Dispose();
    }

Sends the request to the API:

protected void sendCom()
    {
        str headerKey;
        str headerValue;
        System.Net.WebResponse response;

        // Retrieve the API endpoint, user, and password from parameters
        str endpoint = AssetParameters::find().TST_ApiUrl;
        str password = AssetParameters::find().TST_ApipassNameEdit(false, "");
        str user = AssetParameters::find().TST_APIuser;

        System.Text.Encoding encoding = System.Text.Encoding::UTF8;

        // Encode the user and password for basic authentication
        System.Byte[] bytes = encoding.GetBytes(user + ":" + password);
        System.String base64 = System.Convert::ToBase64String(bytes);

        // Authorization header
        headerKey = "Authorization";
        headerValue = "Basic " + base64;

        // Create the request
        System.Net.WebRequest request = System.Net.WebRequest::Create(endpoint);
        request.Method = "POST";
        request.ContentType = "application/json";

        // Add the authorization header
        System.Net.WebHeaderCollection headers = request.Headers;
        headers.Add(headerKey, headerValue);

        // Write the JSON body to the request stream
        using (var streamWriter = new System.IO.StreamWriter(request.GetRequestStream()))
        {
            streamWriter.Write(expBody);
        }

        try
        {
            // Get the response from the API
            response = request.GetResponse();

            // Read the response stream and store the answer
            using (System.IO.StreamReader reader = new System.IO.StreamReader(response.GetResponseStream()))
            {
                answer = reader.ReadToEnd();
            }
        }
        catch
        {            
            throw error("Error communicating with API");
        }
    }

Imports the response from the API using ER:

protected void importAnswer()
{
    // Fills the user input parameters
    ERmodelDefinitionInputParametersAction obj = new ERmodelDefinitionInputParametersAction();
    obj.addParameter('$RecId', tst_tableApi.RecId);

    // Integration point, this is found in the import mapping if the check
    // of the integration point of the destination was selected
    str integrationPoint = 'ERTableDestination#TST_TableAPI';

    // Create a runner for the import format mapping using the integration point 
    // and user input parameters
    ERIModelMappingDestinationRun runner 
        = ERObjectsFactory::createMappingDestinationRunByImportFormatMappingId(
        AssetParameters::find().TST_ERImport, integrationPoint).withParameter(obj);
    runner.init();

    // Runs the import without user interaction
    if (runner.promptsContractedModelMapping())
    {
        ERModelDefinitionParameters parameters = runner.getParameters();
        ERModelDefinitionParametersTraverser traverser = new ERModelDefinitionParametersTraverser(parameters);

        while (traverser.moveNext())
        {
            ERIImportFormatDataSourceContract current = ERCast::asAny(traverser.current()) as ERIImportFormatDataSourceContract;
            if (current)
            {
                System.IO.MemoryStream stream;   
                stream = new System.IO.MemoryStream(System.Text.Encoding::UTF8.GetBytes(answer));
                stream.Seek(0u, System.IO.SeekOrigin::Begin);
                current.parmInputDataStream(stream);
            }
        }
        runner.runUnattended();
    }
}

Saves the response to a docuRef:

protected void saveAnswerToDocRef()
{        
    DocuValue docuValue;
    DocuRef drImpMessage;
    
    // A new name based on the export name
    nameImp = "Answer_" + nameExp;

    // Convert the answer string to a byte array and then to a base64 string
    System.Text.Encoding encoding = System.Text.Encoding::UTF8;
    System.Byte[] bytes = encoding.GetBytes(answer);
    System.String base64String = System.Convert::ToBase64String(bytes);

    // Creates the docuValue table, where the attachment is stored and the 
    // DocuRef table, related to our record
    ttsbegin;       
    docuValue.File = BinData::loadFromBase64(base64String);
    docuValue.Name = nameImp;
    docuValue.FileName = nameImp;
    docuValue.FileType = "JSON";
    docuValue.OriginalFileName = nameImp;
    docuValue.insert();

    drImpMessage.TypeId = DocuType::typeFile();
    drImpMessage.Name = nameImp;        
    drImpMessage.Restriction = DocuRestriction::Internal;
    drImpMessage.RefTableId = tst_tableApi.TableId;
    drImpMessage.RefRecId = tst_tableApi.RecId;
    drImpMessage.RefCompanyId = tst_tableApi.dataAreaId;
    drImpMessage.ValueRecId = docuValue.RecId;
    drImpMessage.insert();        
    ttscommit;
}

The entire code:

using Microsoft.Dynamics365.LocalizationFramework;
using Microsoft.Dynamics.ElectronicReporting.Instrumentation;

internal final class TST_RunAPI
{
    TST_TableAPI tst_tableApi;       
    str nameExp;
    str nameImp;
    str expBody;
    str answer;
    static void main (Args _args)
    {        
        TST_RunAPI tst_RunAPI = new TST_RunAPI();
        // Select the record
        tst_RunAPI.fillTable(_args);
        // Execute ER to file attachment
        tst_RunAPI.runEr();    
        // Gets attachment and saves it on memory
        tst_RunAPI.docuRefToMemory();
        // Sends the request to the API
        tst_RunAPI.sendCom();
        // Executes ER with the answer
        tst_RunAPI.importAnswer();
        // Saves a copy of the answer in memory
        tst_RunAPI.saveAnswerToDocRef();
    }

    protected void fillTable(Args _args)
    {
        select firstonly * from tst_tableApi 
            where tst_tableApi.RecId == _args.record().RecId;
    }

    protected void runEr()
    {  
        // Fills the user input parameters
        ERModelDefinitionInputParametersAction  obj = new ERModelDefinitionInputParametersAction();
        obj.addParameter('model/$Id', tst_tableApi.Id); 
       
        // The Destination is a file
        ERIFileDestination fileDestination = ERObjectsFactory::createFileDestinationAttachmentWithOtherDocuType(
            tst_tableApi);
        
        // Runs the ER, saving the filename to a global variable, with 
        // Destinations and Parameters
        nameExp = ERObjectsFactory::createFormatMappingRunByFormatMappingId(
        AssetParameters::find().TST_ERExport, "", false)
        .withFileDestination(fileDestination)
        .withParameter(obj)
        .run();
    }
    protected void docuRefToMemory()
    {
        DocuRef drExpMessage;
        System.IO.StreamReader streamReader;     
   
        // Finds the created document
        select firstonly drExpMessage order by createdDateTime desc
                where drExpMessage.RefCompanyId == tst_tableApi.dataAreaId
                    && drExpMessage.RefRecId == tst_tableApi.RecId
                    && drExpMessage.RefTableId == tst_tableApi.TableId
                    && drExpMessage.Name == nameExp;

        if (!drExpMessage)
        {
            throw error("API body not found");
        }

        // Read the content of the document attachment into memory
        streamReader = new System.IO.StreamReader(
            DocumentManagement::getAttachmentStream(drExpMessage));
        expBody = streamReader.ReadToEnd();
        streamReader.Close();
        streamReader.Dispose();
    }

    protected void sendCom()
    {
        str headerKey;
        str headerValue;
        System.Net.WebResponse response;

        // Retrieve the API endpoint, user, and password from parameters
        str endpoint = AssetParameters::find().TST_ApiUrl;
        str password = AssetParameters::find().TST_ApipassNameEdit(false, "");
        str user = AssetParameters::find().TST_APIuser;

        System.Text.Encoding encoding = System.Text.Encoding::UTF8;

        // Encode the user and password for basic authentication
        System.Byte[] bytes = encoding.GetBytes(user + ":" + password);
        System.String base64 = System.Convert::ToBase64String(bytes);

        // Authorization header
        headerKey = "Authorization";
        headerValue = "Basic " + base64;

        // Create the request
        System.Net.WebRequest request = System.Net.WebRequest::Create(endpoint);
        request.Method = "POST";
        request.ContentType = "application/json";

        // Add the authorization header
        System.Net.WebHeaderCollection headers = request.Headers;
        headers.Add(headerKey, headerValue);

        // Write the JSON body to the request stream
        using (var streamWriter = new System.IO.StreamWriter(request.GetRequestStream()))
        {
            streamWriter.Write(expBody);
        }

        try
        {
            // Get the response from the API
            response = request.GetResponse();

            // Read the response stream and store the answer
            using (System.IO.StreamReader reader = new System.IO.StreamReader(response.GetResponseStream()))
            {
                answer = reader.ReadToEnd();
            }
        }
        catch
        {            
            throw error("Error communicating with API");
        }
    }

    protected void importAnswer()
    {
        // Fills the user input parameters
        ERmodelDefinitionInputParametersAction obj = new ERmodelDefinitionInputParametersAction();
        obj.addParameter('$RecId', tst_tableApi.RecId);

        // Integration point, this is found in the import mapping if the check
        // of the integration point of the destination was selected
        str integrationPoint = 'ERTableDestination#TST_TableAPI';

        // Create a runner for the import format mapping using the integration point 
        // and user input parameters
        ERIModelMappingDestinationRun runner 
            = ERObjectsFactory::createMappingDestinationRunByImportFormatMappingId(
            AssetParameters::find().TST_ERImport, integrationPoint).withParameter(obj);
        runner.init();

        // Runs the import without user interaction
        if (runner.promptsContractedModelMapping())
        {
            ERModelDefinitionParameters parameters = runner.getParameters();
            ERModelDefinitionParametersTraverser traverser = new ERModelDefinitionParametersTraverser(parameters);

            while (traverser.moveNext())
            {
                ERIImportFormatDataSourceContract current = ERCast::asAny(traverser.current()) as ERIImportFormatDataSourceContract;
                if (current)
                {
                    System.IO.MemoryStream stream;   
                    stream = new System.IO.MemoryStream(System.Text.Encoding::UTF8.GetBytes(answer));
                    stream.Seek(0u, System.IO.SeekOrigin::Begin);
                    current.parmInputDataStream(stream);
                }
            }
            runner.runUnattended();
        }
    }

    protected void saveAnswerToDocRef()
    {
        DocuValue docuValue;
        DocuRef drImpMessage;
    
        // A new name based on the export name
        nameImp = "Answer_" + nameExp;

        // Convert the answer string to a byte array and then to a base64 string
        System.Text.Encoding encoding = System.Text.Encoding::UTF8;
        System.Byte[] bytes = encoding.GetBytes(answer);
        System.String base64String = System.Convert::ToBase64String(bytes);

        // Creates the docuValue table, where the attachment is stored and the
        // DocuRef table, related to our record
        ttsbegin;
        docuValue.File = BinData::loadFromBase64(base64String);
        docuValue.Name = nameImp;
        docuValue.FileName = nameImp;
        docuValue.FileType = "JSON";
        docuValue.OriginalFileName = nameImp;
        docuValue.insert();

        drImpMessage.TypeId = DocuType::typeFile();
        drImpMessage.Name = nameImp;
        drImpMessage.Restriction = DocuRestriction::Internal;
        drImpMessage.RefTableId = tst_tableApi.TableId;
        drImpMessage.RefRecId = tst_tableApi.RecId;
        drImpMessage.RefCompanyId = tst_tableApi.dataAreaId;
        drImpMessage.ValueRecId = docuValue.RecId;
        drImpMessage.insert();
        ttscommit;
    }
}

It’s also possible to work in memory; this example will return the content of the ER as a string:

protected str runErToMemory()
    {
        ERFileDestinationMemory fileDestinationMemory = new ERFileDestinationMemory();
    
        ERModelDefinitionInputParametersAction obj = new ERModelDefinitionInputParametersAction();
        obj.addParameter('model/$Id', tst_tableApi.Id);
    
        ERObjectsFactory::createFormatMappingRunByFormatMappingId(
        AssetParameters::find().TST_ERExport, "", false)
        .withFileDestination(fileDestinationMemory)
        .withParameter(obj)
        .run();
    
        System.Byte[] byteER = fileDestinationMemory.GetByteArray();
        System.Text.Encoding encoding = System.Text.Encoding::UTF8;
    
        return encoding.GetString(byteER);
    }

Now we can test our API call:

And the attachments:


Posted

in

, ,

by

Tags:

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *