Analyst 18   Software Engineering and other random() subjects

FW/1 Example Application - Interaction with a Remote Service

In this fourth article on how to build a ColdFusion and FW/1 Example Application, I’ll present a way to interact with a Remote Service that performs some complex manipulations with the stored articles.

The external service, in this case, is flask_Summarizer

  • an API written in Python/Flask, that receives paragraphs of text, and returns a string with a summary of that content.

Interacting with a Remote Service

To make this project a little more challenging and interesting, I used an ajax call to load the summary into a modal window, when the user clicks on the “View Summary” link:

The link above is constructed with the following code:

<a class="summaryLink"
    href="javascript: ajaxViewSummary('#buildURL('clipping.summary')#',#Clipping.getClipping_Id()#);">View Summary (Ajax)</a>

Yes, I understand that there’s a lot going on, but bear with me:

ajaxViewSummary() is our custom javascript function that:

  1. Passes the ID of a clipping article to the “summary” controller
  2. Receives the output of that controller (either a summarized text or an error message)
  3. Loads a beautiful modal window with the output above.

#buildURL('clipping.summary')# is a FW/1 framework function that generates the URL to a controller or view. Here, it calls the summary() method in the clipping controller.

#Clipping.getClipping_Id()# outputs the id of the current clipping object (in more complicated terms, it invokes the “getter” function for the clipping object’s id).

All these steps render an HTML output similar to:

<a class="summaryLink"
href="javascript: ajaxViewSummary('/clipping/index.cfm?action=clipping.summary',49);">View Summary (Ajax)</a>

For your reference, here’s the function in /static/js/clipping.js:

function ajaxViewSummary(url, clipping_id){
  // if no id was passed, set it to zero
  clipping_id = typeof clipping_id !== 'undefined' ? clipping_id : 0;

  // load modal window
  $.get( url + '&clipping_id=' + clipping_id, function( data ) {
    $( ".modal-body" ).html( data );
    $( ".modal-title" ).html( "Article Summary" );
    $('#myModal').modal({show:true});
  });
}

The complicated and unelegant javascript+cfml interaction above should display something like this:

A quicker to read version of the article.

Modal windows are provided by Bootstrap CSS, so I won’t detail that here.


Summary Method in Controller

The controller simply invokes our remote summary service and outputs the resulting string.

We could do that on the client side (using nothing but javascript), but following this example’s logic, we allow the application to do something with the results, like storing summaries in the database.

/home/controllers/clipping.cfc:

component accessors="true" {

    /**
     * This controller needs the services bellow
     * (they are found in <subsystem>/models/services)
     */
    property clippingService;
    property summaryService;

    /**
     * init FW variables and methods so that they are available to this controller
     */
    property framework;

    /**
     * Uses a webservice to summarize the Article's text
     * It retuns only TEXT and does not use a layout
     */
    function summary( struct rc ) {
        framework.frameworkTrace( "Summary Method on Clipping Controller");
        rc.Clipping = variables.clippingService.getClipping(rc.clipping_id);
        rc.Summary = variables.summaryService.getSummary(rc.Clipping.getClipping_texto());

        // comment out the three lines below
        // to render the clipping.summary view instead
        // (useful for debugging)
        var contentType = 'text';
        setting showdebugoutput='false';
        framework.renderData( contentType, rc.Summary );
    }

Here’s a detailed breakdown:

  • function summary( struct rc ) receives rc, the FW/1’s “Request Context” struct that contains whatever URL or FORM parameters have been passed by the referrer;

  • rc.Clipping = variables.clippingService.getClipping(rc.clipping_id); loads an instance of the Clipping entity we selected;

  • rc.Summary = variables.summaryService.getSummary(rc.Clipping.getClipping_texto()); invokes the summaryService’s getSummary() function, and passes it the selected article’s full text. The generated summary string is stored in rc.Summary

  • framework.renderData( contentType, rc.Summary ); renders the summary string (rc.Summary ) as a “plain text” content, without any HTML layout.

Summary Service

The summary service is written in /home/models/services/summaryService.cfc and is actually quite simple:

component {

    public function getSummary(string clipping_texto){

        cfhttp(url='http://localhost:5000/ajax_resumo' method='post' result='st_summary'){
            cfhttpparam (type="formfield" name = "texto" value = clipping_texto);
        }

        .....

        if (len(st_summary.errordetail) > 0) {
            return "There was an error trying to use the summary service :'(";
        } else {
            return st_summary.filecontent;
        }
    }
}

Above, we make an http call to the API’s endpoint URL - it requires a POST method, so we pass the article’s full text as a formfield named texto.

The result of this call is saved to the st_summary struct. We then check it for errors, and if there are none we return st_summary.filecontent a key containing the summary.


For more detailed information on this project, follow the other articles in this series:

For the full source code, please visit the fw1-clipping github project page.