Implement REST Integration Services

REST integration services are used to integrate an external API with the current project.

Tip:

The generated methods described on this page can be either triggered by a REST integration service or a domain service.

An external API could be integrated by adding the specification as an API dependency or not.

Make external requests against an API with provided API specification

In case that there is the API specification of the external service provided, utility methods are auto generated that help to easier consume the API.

Below is an example of the implementation of integration against an email API, which provides a POST method postEmail for sending mails:

// 1. Accessing types from API dependency where
// - integrationSchema is a grouping for all api dependencies schemas
// - emailSchema is a grouping for schemas in integration namespace with acronym "email"
// - EmailMessage schema we want to get its type

import { services, integrationSchema } from 'solution-framework';
export default class extends services.email_SendMail {
  public async execute(): Promise<void> {
    const requestBody: integrationSchema.emailSchema.EmailMessage= {
        to: [
            {
            adress: 'some address'
            }
        ],
        subject: `Order Confirmation of Order #12345`,
        sender: 'some@sender.com',
        message: 'Some message',
        messageFormat: 'HTML'
        };


    // 1. Calling api dependency operation and getting response body 
    const responseBody = (await this.apis.email.postEmail(requestBody)).data;

    // 2. Also we have full access to response returned as a result of an underlying axios call
    const axiosResponse = await this.apis.email.postEmail(requestBody);

    // Getting response body
    const responseBody = axiosResponse.data;

    // Getting response headers
    const headers = axiosResponse.headers;

    // Getting response status
    const statusCode = axiosResponse.status;

    //3. Another variation
    const {headers, data, status} =  await this.apis.email.postEmail(requestBody);

    // further logic on handling response
    // ...
  };
}
Note:

If your API dependency's API binding already contains ca_cert value, then this ca_cert value will be implicitly used to make external API facade operation calls.

Typesafety when making API requests

To easier consume the external APIs, additional interfaces and types are provided in the SDK.

IntegrationSchema

The integrationSchema interface exists only when at least integration namespace exists. It holds all schemas related to every integration namespace.

// define variable from integration schema
let myIntegrationSchemaVar:integrationSchema.namespaceSchema.schema;
myIntegrationSchemaVar.prop1 = 'prop 1 value'

Request

The request object holds (query, path and body) for every operation inside an API dependency.

import { Request } from 'solution-framework;

// define variable from addDate operation query
let myQueryVar:Request.AddDateQuery;
myQueryVar.prop1 = 'prop 1 value'

// define variable from addDate operation path
let myPathVar:Request.AddDatePath;
myPathVar.prop1 = 'prop 1 value'

// define variable from addDate operation body
let myBodyVar:Request.AddDateBody;
myBodyVar.prop1 = 'prop 1 value'

// define variable from addDate operation request 
let myRequestVar:Request.AddDateRequest;
myRequestVar.prop1 = 'prop 1 value'
// _request of type http request
myRequestVar._request

Response

The response object (body, statusCode) for every operation inside an API dependency.

import { Response } from 'solution-framework;

// define variable from addDate operation statusCode type
const myAddDateStatusCode: Response.AddDateStatusCode = 201;
// myAddDateStatusCode variable now accept values from addDate operation statusCode only 

// define variable from addDate operation schema type
// schema1 can represent for example schema for response status code 200;
const myAddDateSchema1: Response.AddDateSchema.schema1;
// schema2 can represent for example schema for response status code 404;
const myAddDateSchema2: Response.AddDateSchema.schema2;

// define variable from addDate operation body
// myAddDateBody value can be either myAddDateSchema1 or myAddDateSchema2
const myAddDateBody: Response.AddDateBody;

// define variable from addDate operation response type
const myAddDateResponse: Response.AddDateResponse;
myAddDateResponse.body = myAddDateBody;
myAddDateResponse.statusCode = myAddDateStatusCode;

Schema

The schema object holds all schemas inside an API dependency.

import { Schema } from 'solution-framework;

// define variable from api dependency schema
let mySchemaVar: Schema.Schema1;
mySchemaVar.prop1 = 'prop 1 value';

Make external requests using request utility

When creating external calls to APIs you can retrieve the API binding option and if it contains a ca_cert property, you can use it to construct an SSLConfig object that can be used when making external API calls.

public async execute(): Promise<void> {          
    // get binding of petstore API
    const apiBinding = await this.apiBindings.getPetstore();
    
    // make the request using the custom ca_cert defined in the api binding
    await this.util.request.get('www.something.com', {param1: 'val'}, {header1: 'val'}, { ca_cert: bind.ca_cert});
}
Warning:

Whenever you are querying external services, you should make sure that all communication to such services are encrypted and all TLS-encrypted protocols including HTTPS use version 1.2+. The connection to the target service should be authenticated (certificate validation should be enabled)!

Access API bindings

API bindings are externalized specifications created per API dependency. They can be used to store API-related configuration information, that may be stage-dependent.

API bindings are typically used to store, e.g.

  • url
  • username
  • password
  • API-Key
  • ... for the 3rd party service you want to access.

Example binding:

{
  "url": "http://example.com/dev"
}

DEV-Bindings

Dev-Bindings provide an easy way to specify some default values, which are easily accessible on all non-production stages. They are meant to be used for rapid development to avoid long and extensive configuration outside of the project.

DEV-Binding can be created and speciified inside the Solution. To do so, you go to the Integration Namespace you want ot use for interacting with this API (or create a new namespace) and create an API dependency. There you will find an input field where you can place your information as JSON formatted key/value pairs.

Warning:

Please be aware, that all information entered into DEV bindings will go into the Git repository! To prevent this, you can instead create an API binding for the same purpose, since they get stored as OpenShift secrets inside the cluster.

Porgrammatically access the binding

Now that you created an API dependency for this namespace with an API dependency attached to it, you have access to this information while implementing this namespace. After you deployed your solution to a project you can log in to this project with the Solution CLI. The SDK now provides you with the information by calling

// load the bindings
const mybindings = await this.apiBindings.<name-of-the-api-dependency>();

// load binding value
const url = mybindings.url;

inside an implementation file (e.g. of a service) inside that Integration Namespace. This would assign the value http://example.com/dev to url.

Currently, only the following default parameters can be accessed with this syntax:

  • url
  • ca_cert
  • k5_propagate_security_token

If you want to use custom parameters, you can access them as follows:

const myparam = mybindings['myparam'];