Options
All
  • Public
  • Public/Protected
  • All
Menu

Akera REST Middleware

Akera Logo

REST Handler Middleware for Akera Application Server.

As opposed to the @akera/web that can only execute special 'handlers' - either the 'classical' WebSpeed wrap-cgi procedures (includes wrap-cgi.i) or the new WebHandler objects (implements OpenEdge.Web.WebHandler) - this module ca be used to expose generic business logic by dynamically calling any procedure (internal entries included) through the api module or expose database(s) content for basic CRUD operation through the crud or jsdo modules.

Installation

$ npm set registry "http://repository.akera.io/"
$ npm install @akera/rest

Docs

API

var akeraRest = require('@akera/rest')

The akeraRest object exposes a number of factories that can be used to create an Express middleware. Often when building web interfaces it might happen the web server does not share the same machine as the server exposing micro-services and CORS related issues might be experienced. This is also true for akera.io REST services, this can be solved by using the CORS module.

akeraWeb.api(configuration, [logger])

Returns an akera.io API Handler middleware that can be used on an Express application to execute any generic business logic by passing input/output parameters much like the @akera/api does.

The middleware can then be used to call business logic procedures on the akera.io broker(s) by making HTTP POST or GET requests and send the business logic call information using the request parameters.

The api interface must be enabled on all akera.io application server(s) used, otherwise the API connections are rejected by the brokers.

config: AkeraWeb | IBrokerConfig | Array

Can be either a 'shared' @akera/web instance or one or more broker configuration(s) for remote akera.io brokers. If one configuration is used then all requests on the middleware router will be dispatched directly to that broker.

When multiple configurations are used then for each unique broker a sub-route is added to the router using the broker's alias as route and each request on the middleware requires one route parameter - the broker alias, based on which the request is dispatched to the corresponding broker. If all configurations have the same alias - fail-over configuration, then the broker will be mounted on the router's root as if a single configuration is provided.

Optional logger: AkeraLogger

An instance of AkeraLogger to be used by the router for logging.

Routes

When a single broker is configured for the middleware the api interface will dispatch all requests to that one broker:

  • POST

    • URL: /
    • BODY: Business Logic Information in JSON format.
    • Content-Type: application/json
  • GET

    • URL: /
    • Query String: ?call=[call]&parameters=[parameters]

Parameters must be an encoded string value of the JSON array holding parameters information. Optional information can be sent through additional query string parameters: method, type, returns.

For convenience more specific routes were defined for calling internal entries:

  • /[external procedure path]/procedure/[internal procedure name]
  • /[external procedure path]/function/[internal function name]
  • /[class full name]/method/[instance method name]
  • /[class full name]/static/[static method name]

Business Logic Information

The information on what business logic need to be executed is sent either in a JSON format for POST requests or through query parameters for GET requests.

  • call: the business logic object name
  • method: the internal entry of the business logic object
  • type: the call type (procedure, function, method or static).
  • returns: the data type of return parameter, only for function or method.
  • parameters: array of optional call parameters

The business logic object name must include the relative path (if any), and it can be an external procedure or an OO class.

If method value is not set the business logic must be an external procedure that normally takes parameters although this is not required.

When calling internal entries of 'super procedures' the external procedure must not have any parameters. Depending on the type value an internal function or procedure can be executed, the name of the internal entry is specified using the method information.

When calling methods of OO instance objects the class must have a public constructor with no parameters. Depending on the type value an instance (method) or a static method can be executed, the name of the method is specified using the method information.

If type value is not set it defaults to procedure.

If the parameters sent does not match the business logic entry parameters an error will be thrown back.

Parameters

Each parameter entry has the following structure:

  • type: parameter data type
  • direction: parameter direction
  • value: parameter value for input/input-output parameters
  • json: for output/input-output parameters of type character/longchar.

All Progress primitive data types are supported as input/output/input-output parameters - character, date, datetime, dataset, decimal, integer, int64, logical, longchar, memptr - for output complex data types like table and dataset are also supported.

If type is not specified it defaults to character.

Valid values for parameter direction are:

  • input (in, i), if not set this is the default.
  • output (out, o)
  • input-output (inout, io)

For input/input-output parameters if value is not set it defaults to null (? in 4GL).

If json flag is set to true the value is sent as a JSON object instead of encoding it as a string. Dataset and table parameters are always sent as JSON objects.

Result

The response is a always a JSON object sent as response body with a parameters property as array.

Only output and input-output parameters are sent back in the parameters array keeping the same order as in the input parameters array - the first entry in the array will be the first parameter with direction input or input-output.

If an internal function or method is executed the response object might also have an return object, the value depends on the data type of the function/method return parameter.

{ "parameters": [
        "Hello world!",
        2018
    ],
    "return": true
}

If there was an error while executing the business logic the response body will contain an error property containing the error message as string.

{"error": "Mismatched number of parameters passed to routine xxxxxx.p. (3234)"}

Notes

The Express.js application or router where the middleware is mounted must have a JSON body parser installed in order for POST requests to work (Content-Type header value set to application/json).

Only the procedures available in akera.io application server WEBPATH can be executed - not everything from agent's PROPATH is available through the REST API interface.

Examples

Mount a single akera.io broker

A 'failsafe' configuration is defined using two akera.io application servers running on different ports on the same computer as the Express web server (localhost). All requests sent on /api path will be handled by the API Handler middleware and will be dispatched to the first broker to which a connection can be established. All requests sent on /crud path will be handled by the CRUD Handler middleware while those made on /jsdo will be handled by the JSDO Handler.

import * as express from 'express';
import * as bodyParser from 'body-parser';
import akeraRest from '@akera/rest';

let app: express.Application = express();
let config = [ { "name": "demo",
                 "host": "localhost",
                 "port": 8900 },
               { "name": "failsafe",
                 "alias": "demo",
                 "host": "localhost",
                 "port": 9900 } ];

app.use(bodyParser.json());
app.use('/api', akeraRest.api(config));
app.use('/crud', akeraRest.crud(config));
app.use('/jsdo', akeraRest.jsdo(config));

A HTTP POST request made on /api with the body:

{
    "call": "demo/sports/getCustBalance.p", 
    "parameters": [
        { "direction": "i", "value": 2 },
    { "direction": "o", "type": "decimal" },
    { "direction": "o", "type": "decimal" },
    { "direction": "o", "type": "decimal" }
    ] 
}

will run the external procedure demo/sports/getCustBalance.p on the akera.io application server passing one input parameter with a value of 2 and expecting three output parameters all of decimal data type.

Mount multiple akera.io brokers

A configuration is defined using two separate akera.io application servers running on different ports on the same computer as the Express web server (localhost). All requests sent on /api path will be handled by the API Handler middleware and will be dispatched to the corresponding broker based on the broker alias (first route parameter). All requests sent on /crud path will be handled by the CRUD Handler middleware while those made on /jsdo will be handled by the JSDO Handler.

import * as express from 'express';
import {AkeraWeb} from '@akera/web';
import akeraRest from '@akera/rest';

let app: express.Application = express();
let config = [ { "name": "demo1",
                 "host": "localhost",
                 "port": 8900 },
               { "name": "demo2",
                 "host": "localhost",
                 "port": 9900 } ];
let akeraWeb = new AkeraWeb(config);

app.use('/api', akeraRest.api(akeraWeb));
app.use('/crud', akeraRest.crud(akeraWeb));
app.use('/jsdo', akeraRest.jsdo(akeraWeb));

A request made on /api/demo1 will be executed on the demo1 broker while requests made on /api/demo2 will be dispatched to the demo2 broker. Multiple akera.io application servers can be configured for each 'broker' - using the same alias, for failover setup.

Change log

As of version 1.0.8 the interface also support GET requests with procedure and parameters values passed through the query string value, also on POST requests the call wrapper object is no longer mandatory - procedure and parameters can be sent directly into the root JSON object.

As of version 1.0.10 the interface also support json flag for output/input-output string parameters (characters/longchar).

As of version 1.1.1 the API and CRUD interfaces were integrated in @akera/rest and code was migrated to typescript.

License

MIT