FICO
FICO Xpress Optimization Examples Repository
FICO Optimization Community FICO Xpress Optimization Home
Back to examples browserPrevious exampleNext example

Solving a Mosel model using a REST webservice, from NodeJS

Description
In this example, the Xpress Executor should first be configured with the blend3c.mos model. Then, you run the blendxe.js program locally; this uses REST webservice requests to send the blend.csv file to the Xpress Executor and remotely solve the model using this, then downloads and displays the results. This example requires a local installation of Xpress for compiling the initial model, but not for starting the execution.

executor_rest_mosel_nodejs.zip[download all files]

Source Files

Data Files





blendxe.js

/*******************************************************
  Xpress Executor Example Model
  =============================

  file blendxe.js
  ```````````````
  Demonstrates executing the 'blend3c' model using Xpress Executor
  with the supplied input data & displaying the results.  This example is written
  in JavaScript and intended to be run using node.js.

  Local prerequisites:
    node.js
    npm

  Instructions
    1) Configure your Xpress Executor component with the blend3c example model
    2) Fill in the DMP_* variables with the details of your Xpress Executor component
    3) Open a command prompt and type:   npm install
    4) When that completes, type:        node blendxe.js

  (c) 2017 Fair Isaac Corporation
  author: J. Farmer, Jul. 2017
*******************************************************/

// The REST endpoint of the Xpress Executor DMP component
// ! Obtain this by clicking "View Links" for the Xpress Executor component on the DMP UI
var DMP_XE_REST_ENDPOINT="";

// The client ID of solution containing the Xpress Executor DMP component
// Obtain this through the DMP UI
var DMP_SOLUTION_CLIENT_ID="";

// The secret of the solution containing the Xpress Executor DMP component
// Obtain this through the DMP UI
var DMP_SOLUTION_SECRET="";

// The root DMP manager URL. This will be different depending on which instance of DMP you are using.
var DMP_MANAGER_URL="https://manager-svc.prd-platform.ficoanalyticcloud.com";

// The input file for the remote model
var INPUTFILE="../data/blend.csv";



// Third-party dependency: prequest
// for promise-based HTTP requests
var prequest = require('prequest');

// Third-party dependency: delay
// for promise-based delays
var delay = require('delay');

// Third-party dependency: csv-parse
// for parsing CSV data
// The example model blend3c.mos reads & outputs CSV; this is a feature of this example rather than a requirement
// of Xpress Executor.
var parse = require('csv-parse/lib/sync');


// Standard node.js filesystem module
var fs = require('fs');
// Standard node.js URL handling module
var url = require('url');

// Request an authentication token from DMP
console.log("Requesting authorization token from DMP");
var authorizationToken;
prequest({
    method: 'POST',
    url: DMP_MANAGER_URL+"/registration/rest/client/token",
    body: {
        clientId: DMP_SOLUTION_CLIENT_ID,
        secret: DMP_SOLUTION_SECRET
    }
}).then(function(body) {
    // This token can be re-used in subsequent requests, but should be refreshed every half hour
    authorizationToken = body;

    // Start the execution of the model in our Xpress Executor service
    console.log("Initiating model execution");
    return prequest({
        method: 'POST',
        url: DMP_XE_REST_ENDPOINT,
        headers: {
            "Authorization": 'Bearer '+authorizationToken
        },
        body: {
            // parameters defined in 'parameters' section at the top of blend3c.mos
            parameters: {
                INPUTFILE: "input",
                RESULTFILE: "result"
            },
            // Use inputText for passing input data as a string, inputBase64 for encoded binary input data
            inputText: fs.readFileSync(INPUTFILE,"utf8")
        }
    });
}).then(function(executionStatus) {
    // executionStatus is a standard structure that contains various meta-data about an execution in
    // the Xpress Executor service.  It also contains relative paths to various REST resources
    // relating to this execution - e.g. input, result, status, run log...

    // Model will be executing asynchronously; repeatedly wait 1/4 second then re-fetch status until it
    // is complete
    console.log("Waiting for completion of execution");
    function waitForCompletion() {
        if (executionStatus.status!=='NOT_COMPLETED' && executionStatus.status!=='NOT_LOADED') {
            // Execution has finishd!
            return Promise.resolve(executionStatus);
        }
        else {
            // Wait 250ms
            return delay(250).then(function() {
                // Refresh executionInfo
                return prequest({
                    method: 'GET',
                    url: url.resolve(DMP_XE_REST_ENDPOINT, executionStatus.statusPath),
                    headers: {
                        "Authorization": 'Bearer '+authorizationToken
                    }
                }).then(function(body) {
                    // Request returns updated executionStatus
                    executionStatus = body;
                    return waitForCompletion();
                });
            });
        }
    }
    return waitForCompletion();
}).then(function(executionStatus) {

    // Execution has completed; check that it was successful and display results as appropriate
    console.log("Processing model results");

    // In event of failure, echo the remote model status, exit code & run log to aid with troubleshooting
    if (executionStatus.status!=='OK' || executionStatus.exitCode!==0) {
        // Execution failed for some reason
        console.log("Execution failed!");
        console.log("Execution status: "+executionStatus.status);
        console.log("Execution exit code: "+executionStatus.exitCode);
        console.log("");
        console.log("Execution log:");
        // Fetch the remote execution log as it will likely contain error messages from the model
        return prequest({
            method: 'GET',
            url: url.resolve(DMP_XE_REST_ENDPOINT, executionStatus.runLogPath),
            headers: {
                "Authorization": 'Bearer '+authorizationToken,
                "Accept": 'text/plain'
            }
        }).then(function(runLog) {
            console.log(runLog);
            return executionStatus;
        });
    }

    else {
        // Download results file
        return prequest({
            method: 'GET',
            url: url.resolve(DMP_XE_REST_ENDPOINT, executionStatus.resultPath),
            headers: {
                "Authorization": 'Bearer '+authorizationToken,
                "Accept": 'application/octet-stream'
            }
        }).then(function(csvResults) {

            // The blend3c.mos example downloads its results as a CSV file.  Use the NodeJS 'csv' module to parse this.
            console.log();
            console.log("Results of optimization:");
            records = parse(csvResults);
            records.forEach(function(record) {
                console.log("  use("+record[0]+"): "+record[1]);
            });
            console.log();

            return executionStatus;
        });
    }

}).then(function(executionStatus) {
    // Finally, delete execution from component, to free the resources it holds
    console.log("Deleting execution from component");
    return prequest({
        method: 'DELETE',
        url: url.resolve(DMP_XE_REST_ENDPOINT, executionStatus.statusPath),
        headers: {
            "Authorization": 'Bearer '+authorizationToken
        }
    });

}).catch(function(err) {
    if (err.statusCode) {
        console.error("ERROR returned by Xpress Executor service: HTTP status code "+err.statusCode);
    }
    else {
        console.error("ERROR encountered: "+err.message);
    }
});
Back to examples browserPrevious exampleNext example