shape-api-utils

0.1.0 • Public • Published

API orchestration module (AOM)

1 - AOM provides following method to be called from within the request handling chain of an Express server:

  • orchestrateApis(opts, callback, objs)
    • This passes control to a main method in an application-specific orchestration module, or returns an error to the callback method if no application-specific orchestration module can be found.
    • Argument opts is cloned and then passed into the main method. It is expected to contain:
      • appOrchModule: a resolvable path to an application-specific orchestration module
      • apiOptions: a json configuration object containing request options for each API call
      • reqParams: an object with request params (a reference to Express' req.params)
      • reqQuery: an object with querystring values (a reference to Express' req.query)
      • contextProp: the name of a context property, to be used as container for derived API response values (see mergeResultWithDerived below)
    • Argument callback is expected to be a callback function which will be called when the orchestration finishes.
    • Argument objs is optional, it can be used to pass objects that should not be cloned, like class instances.

2 - AOM provides following methods for application's orchestration code:

  • replaceUriVars(apiOptions, uriVars)

    • takes a uri (apiOptions.uri) with placeholder variables and replaces those placeholders with actual values.
    • Argument apiOptions: options to be passed in to the request module; the method will modify the uri property of this object
    • Argument uriVars: an object containing all uri placeholders and their values
  • getApiData(apiOptions, uriVars, callback)

    • makes an API call, using the request module and returns a callback with (err, result) signature, and standardizes error handling/messaging.
    • Argument apiOptions: options to be passed in to the request module: it should contain at least a uri property which may have variable placeholders enclosed in curly braces ({myVar}), and may contain optional headers that will be passed to the API request
    • Argument uriVars: object with replacement values for the uri placeholders
  • getErrorResponse(err, body)

    • returns a standardized error response object (proper response status still needs to be handled by the server)
    • Argument err: an error ID or short string
    • Argument body: the full error dump, could be stack trace or error response from API
  • lodash

    • a wrapper for all lodash methods

3 - AOM makes an instance of a flow object available to the application-specific orchestration module. This flow object provides:

  • a facade to a select subset of methods that are derived from the vasync module:

    • parallel(ArrayOfFunctions, callback): invoke N functions in parallel (and merge the results). Each function should expect arguments (flow, callback).
    • waterfall(ArrayOfFunctions, callback): invoke N functions in series, propagating results between stages. The first function in the array should expect arguments (flow, callback). All subsequent functions should expect arguments (flow, dependency, callback), where dependency contains the data results of the previous functions within the chain.
  • access to the apiOptions, envVars, reqParams and reqQuery properties.

  • a method mergeResultWithDerived(result, derived)

    • merges a result object with a "derived" object; the derived object should contain any data that is derived/added by the orchestration logic to enhance API data. The derived data is placed in the contextProp namespace (='gc', the client-side global context)

Sample application-specific pseudo-code:

var apiUtils = require('shape-api-utils');
 
// the main function is called from the server
function main(flow, callback){
    // we need to get data for events and artist, these calls can be made in
    // 'parallel' since there is no dependency
    flow.parallel([getUpcomingEvents, getArtistInfo], function(err, result){
      // add derived data, for instance environment-specific settings
      flow.mergeResultWithDerived(result, {
        target_host: flow.apiOptions.params.host
      });
      callback(err, result);
    });
}
 
// all functions that are called by the 'flow' methods have access to the
// 'apiOptions', 'reqParams' and 'reqQuery' objects (which were passed into 'main')
function getUpcomingEvents(flow, callback){
    var uriVars = {
        host: flow.apiOptions.params.host,
        performerId: flow.reqParams.param,
        start: flow.apiOptions.upcomingEvents.params.start,
        rows: flow.apiOptions.upcomingEvents.params.rows
    };
    apiUtils.getApiData(flow.apiOptions.upcomingEvents, uriVars, callback);
}
 
// to get the artist info, we need to make two API calls: the URI for the second
// call needs to be constructed with data from the first call, hence there is a
// dependency between the two, which is handled by 'waterfall'.
function getArtistInfo(flow, callback){
    flow.waterfall([callPerformerApi, callArtistApi], callback);
}
 
function callPerformerApi(flow, callback){
    var uriVars = {
        host: flow.apiOptions.params.host,
        performerId: flow.reqParams.param
    };
    return apiUtils.getApiData(flow.apiOptions.performer, uriVars, callback);
}
 
// this method is called from within a waterfall and gets the result
// from the previous call through the 'dependency' argument
function callArtistApi(flow, dependency, callback){
    var uriVars = {
        extcatalogapi_host: flow.apiOptions.params.extcatalogapi_host,
        artistNames: getPerformerName(dependency)
    };
    return apiUtils.getApiData(flow.apiOptions.artist, uriVars,
      function(err, result){
        // add derived data, and then return the result
        flow.mergeResultWithDerived(result, {
            performerName: artistNames,
            artist_image: getArtistImagePath(result)
        });
        callback(err, result);
      }
    );
}
 
// a helper function that parses result data
function getPerformerName(result){
    var performerName = "";
    // custom logic to extract a performer name from the result
    return performerName;
}
 
// a helper function that derives an image path
function getArtistImagePath(result){
    var imgPath = "";
    // custom logic to extract an image path from the result
    return imgPath;
}

Sample SPA config for a QA environment:

General app.json:

{
  "bootstrapApiData": true,
  "suppressJs": false,
  "app_context": {
    "appName": "performer",
    "apiOptions": {
      "performer": {
        "uri": "http://{host}/shape/catalog/performers/v2/{performerId}",
        "headers": {
          "Accept": "application/json",
          "Authorization": "Bearer JYf0azPrf1RAvhUhpGZudVU9bBEa"
        }
      },
      "artist": {
        "uri": "http://{extcatalogapi_host}/extcatalogapi/artists?names={artistNames}"
      },
      "upcomingEvents": {
        "uri": "http://{host}/shape/search/catalog/events/v2/?performerId={performerId}&status=active&sort=dateLocal asc&start={start}&rows={rows}",
        "headers": {
          "Authorization": "Bearer JYf0azPrf1RAvhUhpGZudVU9bBEa"
        },
        "params": {
          "start": 0,
          "rows": 20
        }
      }
    }
  }
}

Environment-specific app_*.json, for example app_development.json:

{
  "app_context": {
    "apiOptions": {
      "params": {
        "host": "www.srwd33.com",
        "domain": "srwd33.com",
        "subdomain": "www",
        "tld": "com",
        "app_token": "JYf0azPrf1RAvhUhpGZudVU9bBEa",
        "extcatalogapi_host": "srwd10evx001.srwd10.com:8080"
      }
    }
  }
}

Readme

Keywords

Package Sidebar

Install

npm i shape-api-utils

Weekly Downloads

1

Version

0.1.0

License

none

Last publish

Collaborators

  • mjasso