HTTP Client

Receiver

This service implements a generic http client. Preparing the body payload (or intercepting) for the http request is done via the TSInterceptor service.

docker pull registry.goingrid.io/services/httpclient:v0.1.0
Using

Table of contents

Service properties

This service implements the ingrid protocol message. Following properties are being used:

Property Usage
Class -
Operation -
Control -
Data Accepts any given data

Service capabilities

Send HTTP request

With the http service you can send HTTP requests to any web service (REST, SOAP, etc.). Mapping the incoming data from the hive to the web service’s data interface and back is done via the TSInterceptor.

JSON

POST https://rest.app.goingrid.io/user/create HTTP/1.1
content-type: application/json
Authorization: Basic admin nutz

{
    "firstname": ["Clark"],
    "lastname": ["Kent"]
}
import { Response, IngridResponse, IngridMsg, IngridResult, IngridData } from "https://raw.githubusercontent.com/itdistrict/ingrid-deno/master/lib/ingrid.ts";

export function createUserBefore(type: string, value: any, options: Record<string, string>): Response {
    var resp = new Response();
    // Before interceptor is always from type json, because the data is coming from the hive
    if (type == "json") {
        resp.data = {
            "firstname": value.Data.firstname[0],
            "lastname": value.Data.lastname[0]
        };
    }
    return resp;
}

export function createUserAfter(type: string, value: any, options: Record<string, string>): IngridResponse {
    let resp = new IngridResponse();
    // After interceptor will be the same type as the provided ContentType from the http response.
    // If no ContentType was found in the response, the default would be "text/plain; charset=utf-8".
    if (type == "json") {
      let ingMsg : IngridMsg = new IngridMsg();
      let ingResult : IngridResult = new IngridResult();

      let ingData: IngridData = {};
      ingData["_id"] = [value._id];

      ingResult.Code = 200;
      ingResult.Message = "User created";
      ingResult.Data = ingData;
      ingMsg.Result = ingResult;
      resp.data = ingMsg;
    } else if (type == "text") {
      resp.status = `Did not expect text: ${value}`
      resp.status_code = 400
    } else {
      // not supported type, needs a custom reader
      resp.status = "Not supported raw types"
      resp.status_code = 400
    }
    return resp;
}
tsinterceptor:
  image: ${ING_REGISTRY}/worker/tsinterceptor:latest
  deploy:
    restart_policy:
      condition: on-failure
  environment:
    PORT: 8080
  volumes:
    - /srv/ingrid/storage/mapper/es:/app/static
  depends_on:
    - hive
  networks:
    - ing-entry
    - ing-middle

create-user:
  image: ${ING_REGISTRY}/worker/httpclient:latest
  deploy:
    restart_policy:
      condition: on-failure
  environment:
    NAME: "create-user"
    SERVERHOST: "hive"
    SERVERAUTH: "file:///run/secrets/ing-hive-key"
    INPUTCHANNEL: "user_create"
    INTERCEPTBEFORE: "http://tsinterceptor:8080/createUserBefore"
    INTERCEPTAFTER: "http://tsinterceptor:8080/createUserAfter"
    METHOD: "POST"
    URL: "https://elastic.app.goingrid.io/users/_doc"
    HEADERS: "Authorization: ApiKey cjljT3BITUJNQkJPMVlEVzc1d3Y6RDVubUpfQ0tRMjJzaHpxWjFXSVlCZw=="
    LOGLEVEL: "trace"
  depends_on:
    - hive
    - tsinterceptor
  secrets:
    - ing-hive-key
  networks:
    - ing-middle
    - ing-worker

XML

POST https://rest.app.goingrid.io/user/create HTTP/1.1
content-type: application/json
Authorization: Basic admin nutz

{
    "firstname": ["Clark"],
    "lastname": ["Kent"]
}
// /srv/ingrid/storage/mapper/soap/func.ts
import { Response, IngridResponse, IngridResponseData, IngridData } from "https://raw.githubusercontent.com/itdistrict/ingrid-deno/master/lib/ingrid.ts";
import { xmlJS } from "../lib/xml-js.ts";
/*
* Funcs
*/
export function createUserBefore(type: string, value: IngridMsg, options: Record<string, string>): Response {
    var resp = new Response();
    // Before interceptor is always from type json, because the data is coming from the hive
    if (type == "json") {
        resp.data = xmlJS.js2xml(value, { compact: true, spaces: 0 });
    }
    resp.options = options;
    return resp;
}

export function createUserAfter(type: string, value: any, options: Record<string, string>): IngridResponse {
    let resp = new IngridResponse();
    // After interceptor will be the same type as the ContentType from the http response.
    // If no ContentType was found in the response, the type will be the one configured.
    if (type == "text") {
      let ingMsg : IngridMsg = new IngridMsg();
      let ingResult : IngridResult = new IngridResult();
      let ingData: IngridData = {};

      let respData =JSON.parse(xmlJS.xml2json(value, { compact: true, spaces: 4 }));
      ingData["_id"] = [respData.response.user.id._text];

      ingResult.Code = 200;
      ingResult.Message = "User created";
      ingResult.Data = ingData;
      ingMsg.Result = ingResult;
      resp.data = ingMsg;
    } else if (type == "json") {
      resp.status = "Did not expect json:" +  JSON.stringify(value})
      resp.status_code = 400
    } else {
      // not supported type, needs a custom reader
      resp.status = "Not supported raw types"
      resp.status_code = 400
    }
    return resp;
}
tsinterceptor:
  image: ${ING_REGISTRY}/worker/tsinterceptor:latest
  deploy:
    restart_policy:
      condition: on-failure
  environment:
    PORT: 8080
  volumes:
    - /srv/ingrid/storage/mapper/soap:/app/static
  depends_on:
    - hive
  networks:
    - ing-entry
    - ing-middle

create-user:
  image: ${ING_REGISTRY}/worker/httpclient:latest
  deploy:
    restart_policy:
      condition: on-failure
  environment:
    NAME: "create-user"
    SERVERHOST: "hive"
    SERVERAUTH: "file:///run/secrets/ing-hive-key"
    INPUTCHANNEL: "user_create"
    INTERCEPTBEFORE: "http://tsinterceptor:8080/createUserBefore"
    INTERCEPTAFTER: "http://tsinterceptor:8080/createUserAfter"
    METHOD: "POST"
    URL: "https://soap.app.goingrid.io/users"
    HEADERS: "Authorization: ApiKey cjljT3BITUJNQkJPMVlEVzc1d3Y6RDVubUpfQ0tRMjJzaHpxWjFXSVlCZw=="
    CONTENTTYPE: "application/xml"
    LOGLEVEL: "trace"
  depends_on:
    - hive
    - tsinterceptor
  secrets:
    - ing-hive-key
  networks:
    - ing-middle
    - ing-worker

Options

import { Response, IngridResponse, IngridMsg, IngridResult, IngridData } from "https://raw.githubusercontent.com/itdistrict/ingrid-deno/master/lib/ingrid.ts";

export function before(type: string, value: IngridMsg, options: Record<string, string>): Response {
  let resp = new Response();
  // Following options are available and can change the course of the http request
  options.method = "GET";
  options.url = "example.com/api";
  options.content_type = "text/plain; charset=utf-8";
  let headers = JSON.parse(options.headers);
  headers["Custom-Header"] = ["Custom-Value"];
  options.headers = JSON.stringify(headers);

  // Set the data to a type defined in the service config or in this case
  // the one overwritten above (options.content_type)
  resp.data = "some plain text";

  resp.options = options; // needs to be specified to overwrite the current options from the service config
  return resp
}


export function after(type: string, value: any, options: Record<string, string>): IngridResponse {
  let resp = new IngridResponse();
  // Following options are available after the http request was issued. Unlike the options in the before function
  // those options are only for informative.
  let http_request = JSON.parse(options.http_request); // The issued http request
  let http_response_status_code = parseInt(options.http_response_status_code);
  let http_response_status = options.http_response_status
  let http_response_headers = JSON.parse(options.http_response_headers);

  let ingMsg : IngridMsg = new IngridMsg();
  let ingResult: IngridResult = new IngridResult();
  ingResult.Status = http_response_status_code;
  ingResult.Message = http_response_status;

  if (type == "text") {
    let ingData: IngridData = {};
    ingData["message"] = [value];
    ingResult.Data = ingData;
  }

  ingMsg.Result = ingResult;
  resp.data = ingMsg;
  return resp;
}

Validation

You can validate the data and stop the httpclient from sending it the actual http request. Every status NOT between 200 and 299 will abort further processing and send the response back to the caller.

import { Response, IngridResponse, IngridMsg, IngridResult, IngridData } from "https://raw.githubusercontent.com/itdistrict/ingrid-deno/master/lib/ingrid.ts";

export function before(type: string, value: IngridMsg, options: Record<string, string>): Response {
  let resp = new Response();
  if (type == "json" && "username" in value.Data && value.Data["username"][0] != "tony") {
    resp.status = "Username not provided";
    resp.status_code = 400;
    return resp;
  }
  // do something
  return resp;
}

Service configuration

Parameter Default Description
Method "" HTTP Method used for the http request
URL "" URL used for the http request
ContentType "application/json" ContentType used for the http request. Setting ContentType in the Headers config parameter will overwrite this
Headers "" Additional http headers used for the http request (\r\n separated)
InterceptBefore "" URL to a mapper function before the actual http request
InterceptAfter "" URL to a mapper function after the actual http request

Additionally the httpclient service includes all properties of the service configuration and the input configuration.

Configuration parameter Intercept

The configuration parameters InterceptBefore and InterceptAfter are being used to configure the endpoints of the used TSInterceptor service (e.g https://tsinterceptor/hello). So in order to make the http service work, one or more TSInterceptor services need to be configured as well.