Sample code for QoD

The following samples show how to use the Open Gateway Quality on Demand API to optimize your user's mobile connectivity by creating sessions tailored to the type of network traffic generated by your app, and trigger it from the app itself, or from a backend on use cases where the end-devices don't run your app (e.g. drones, cameras, etc.).

📘

Note

To try out our APIs, visit the Sandbox.

The following code shows, for didactic purposes, a hypothetical SDK, in several programming languages, from a generic Open Gateway's channel partner, also known as aggregator. The final implementation will depend on the channel partner's development tools offering. Note that channel partners' Open Gateway SDKs are just code modules wrapping authentication and API calls providing an interface in your app's programming for convenience.

Sample code on how to consume the API without an SDK, directly with HTTP requests, is also provided, and it is common and valid no matter what your partner is, thanks to the CAMARA standardization. If you do not use an SDK you need to code the HTTP calls and additional stuff like encoding your credentials, calling authorization endpoints, handling tokens, etc. You can check our sample Postman collection as a reference.

📘

It is recommended to use the API Reference tool for faster calls of our APIs

Table of contents

Code samples

📘

These are code examples

  • Remember to replace 'my-app-id' and 'my-app-secret' with the credentials of your app. (If you are using our Sandbox, you can get them here).
  • Remember also to replace "aggregator/opengateway-sdk" with the SDK from your aggregator. If you are using our sandbox SDK, check info and installation of de Sandbox SDK here

Backend flow

A lot of use cases for the Quality on Demand API are based on optimizing the connectivity of non human-interface devices, such as drones or IP cameras. In those cases, your application will be managing such devices and perform the API consumption from the backend, not from the device itself.

The authentication protocol used in Open Gateway for backend flows is the OIDC standard CIBA (Client Initiated Backchannel Authentication). You can check the CAMARA documentation on this flow here.

First step is to instantiate the Quality on Demand service class included in the corresponding SDK. By providing your app's credentials to the class constructor, it handles the CIBA authentication on its behalf. Providing the device IP address as well, as an identifier of the line to be optimized, will let your app to just effectively use the API in a single line of code below.

Authentication

Since Open Gateway authentication is 3-legged, meaning it identifies the application, the operator and the operator's mobile line providing the device with connectivity, each operation on a different device needs its own SDK class instantiation with its IP address, or access token if not using an SDK.

[TAB] SDK for Node.js
import { QoDMobile, ClientCredentials, QoSProfiles } from "aggregator/opengateway-sdk"

const credentials: ClientCredentials(
    clientId: 'my-app-id',
    clientSecret: 'my-app-secret'
)

let deviceIpPortAddress = getDeviceIP() // e.g. '203.0.113.25:8080'

const qodClient = new QoDMobile(credentials, null, deviceIpPortAddress)
[TAB] SDK for Java
import aggregator.opengatewaysdk.ClientCredentials;
import aggregator.opengatewaysdk.QoDMobile;
import aggregator.opengatewaysdk.QoSProfiles;

ClientCredentials credentials = new ClientCredentials(
    "my-app-id",
    "my-app-secret"
);

String deviceIpPortAddress = this.getDeviceIP(); // e.g. "203.0.113.25:8080"

QoDMobile qodClient = new QoDMobile(credentials, null, deviceIpPortAddress);
[TAB] SDK for Python
from aggregator_opengateway_sdk import QoDMobile, ClientCredentials, QoSProfiles

credentials = ClientCredentials(
    clientid='my-app-id',
    clientsecret='my-app-secret'
)

device_ip_address = self.get_device_ip() # e.g. '203.0.113.25:8080'

qod_client = QoDMobile(client=credentials, ip_address=device_ip_address)
[TAB] HTTP using JavaScript (ES6)
// First step:
// Perform an authorization request

let deviceIpPortAddress = getDeviceIP(); // e.g. '203.0.113.25:8080'

let clientId = "my-app-id";
let clientSecret = "my-app-secret";
let appCredentials = btoa(`${clientId}:${clientSecret}`);
let apiPurpose = "dpv:RequestedServiceProvision#qod";

const myHeaders = new Headers();
myHeaders.append("Content-Type", "application/x-www-form-urlencoded");
myHeaders.append("Authorization", `Basic ${appCredentials}`);

const urlencoded = new URLSearchParams();
urlencoded.append("login_hint", `ipport:${deviceIpPortAddress}`);
urlencoded.append("purpose", apiPurpose);

const requestOptions = {
  method: "POST",
  headers: myHeaders,
  body: urlencoded
};

let authReqId;

fetch("https://opengateway.aggregator.com/bc-authorize", requestOptions)
  .then(response => response.json())
  .then(result => {
    authReqId = result.auth_req_id;
  })

// Second step:
// Requesting an access token with the auth_req_id included in the result above

const myHeaders = new Headers();
myHeaders.append("Content-Type", "application/x-www-form-urlencoded");
myHeaders.append("Authorization", `Basic ${appCredentials}`);

const urlencoded = new URLSearchParams();
urlencoded.append("grant_type", "urn:openid:params:grant-type:ciba");
urlencoded.append("auth_req_id", authReqId);

const requestOptions = {
  method: "POST",
  headers: myHeaders,
  body: urlencoded
};

let accessToken;

fetch("https://opengateway.aggregator.com/token", requestOptions)
  .then(response => response.json())
  .then(result => {
    accessToken = result.access_token;
  })
[TAB] HTTP using Java
// First step:
// Perform an authorization request

String deviceIpPortAddress = this.getDeviceIP(); // e.g. "203.0.113.25:8080"

String clientId = "my-app-id";
String clientSecret = "my-app-secret";
String appCredentials = clientId + ":" + clientSecret;
String credentials = Base64.getEncoder().encodeToString(appCredentials.getBytes(StandardCharsets.UTF_8));
String apiPurpose = "dpv:RequestedServiceProvision#qod";

HttpClient client = HttpClient.newHttpClient();

Map<Object, Object> data = new HashMap<>();
data.put("login_hint", "ipport:" + deviceIpPortAddress);
data.put("purpose", apiPurpose);

StringBuilder requestBody = new StringBuilder();
for (Map.Entry<Object, Object> entry : data.entrySet()) {
    if (requestBody.length() > 0) {
        requestBody.append("&");
    }
    requestBody.append(URLEncoder.encode(entry.getKey().toString(), StandardCharsets.UTF_8));
    requestBody.append("=");
    requestBody.append(URLEncoder.encode(entry.getValue().toString(), StandardCharsets.UTF_8));
}

HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://opengateway.aggregator.com/bc-authorize"))
    .header("Content-Type", "application/x-www-form-urlencoded")
    .header("Authorization", "Basic " + credentials)
    .POST(BodyPublishers.ofString(requestBody.toString()))
    .build();

HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
JSONObject jsonResponse = new JSONObject(response.body);
String authReqId = jsonResponse.getString("auth_req_id");

// Second step:
// Requesting an access token with the auth_req_id included in the result above

Map<Object, Object> data = new HashMap<>();
data.put("grant_type", "urn:openid:params:grant-type:ciba");
data.put("auth_req_id", authReqId);

StringBuilder requestBody = new StringBuilder();
for (Map.Entry<Object, Object> entry : data.entrySet()) {
    if (requestBody.length() > 0) {
        requestBody.append("&");
    }
    requestBody.append(URLEncoder.encode(entry.getKey().toString(), StandardCharsets.UTF_8));
    requestBody.append("=");
    requestBody.append(URLEncoder.encode(entry.getValue().toString(), StandardCharsets.UTF_8));
}

HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://opengateway.aggregator.com/token"))
    .header("Content-Type", "application/x-www-form-urlencoded")
    .header("Authorization", "Basic " + credentials)
    .POST(BodyPublishers.ofString(requestBody.toString()))
    .build();

HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
JSONObject jsonResponse = new JSONObject(response.body());
String accessToken = jsonResponse.getString("access_token");
[TAB] HTTP using Python
# First step:
# Perform an authorization request

device_ip_address = self.get_device_ip() # e.g. '203.0.113.25:8080'

client_id = "my-app-id"
client_secret = "my-app-secret"
app_credentials = f"{client_id}:{client_secret}"
credentials = base64.b64encode(app_credentials.encode('utf-8')).decode('utf-8')
api_purpose = "dpv:RequestedServiceProvision#qod"

headers = {
    "Content-Type": "application/x-www-form-urlencoded",
    "Authorization": f"Basic {credentials}"
}

data = {
    "login_hint": f"ipport:{device_ip_address}",
    "purpose": api_purpose
}

response = requests.post(
    "https://opengateway.aggregator.com/bc-authorize",
    headers=headers,
    data=data
)

auth_req_id = response.json().get("auth_req_id")

# Second step:
# Requesting an access token with the auth_req_id included in the result above

headers = {
    "Content-Type": "application/x-www-form-urlencoded",
    "Authorization": f"Basic {credentials}"
}

data = {
    "grant_type": "urn:openid:params:grant-type:ciba",
    "auth_req_id": auth_req_id
}

response = requests.post(
    "https://opengateway.aggregator.com/token",
    headers=headers,
    data=data
)

access_token = response.json().get("access_token")

API usage

Once your app is authenticated it only takes a single line of code to use the service API and effectively optimize device connectivity.

[TAB] SDK for Node.js
const duration = 300 // Seconds

qodClient.setQualityOfService(duration, QoSProfiles.QOS_E)
[TAB] SDK for Java
int duration = 300; // Seconds

qodClient.setQualityOfService(duration, QoSProfiles.QOS_E);
[TAB] SDK for Python
duration = 300 # Seconds

qod_client.set_quality(duration, QoSProfiles.QOS_E)
[TAB] HTTP using JavaScript (ES6)
const myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
myHeaders.append("Authorization", `Bearer ${accessToken}`);

const requestBody = JSON.stringify({
  "device": {
    "ipv4Address": {
      "publicAddress": "203.0.113.25",
      "publicPort": 8080
    },
    "applicationServer": {
      "ipv4Address": "0.0.0.0/0"
    },
    "qosProfile": "QOS_E",
    "duration": 300
  }
});

const requestOptions = {
  method: "POST",
  headers: myHeaders,
  body: requestBody
};

fetch("https://opengateway.aggregator.com/qod/v0/sessions", requestOptions)
  .then(response => response.json())
  .then(result => {
    console.log(`Session created with id: ${result.sessionId}`)
  })
[TAB] HTTP using Java
JSONObject requestBody = new JSONObject();
JSONObject device = new JSONObject();
JSONObject ipv4Address = new JSONObject();
ipv4Address.put("publicAddress", "203.0.113.25");
ipv4Address.put("publicPort", 8080);
device.put("ipv4Address", ipv4Address);
JSONObject applicationServer = new JSONObject();
applicationServer.put("ipv4Address", "0.0.0.0/0");
device.put("applicationServer", applicationServer);
device.put("qosProfile", "QOS_E");
device.put("duration", 300);
requestBody.put("device", device);

HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://opengateway.aggregator.com/qod/v0/sessions"))
    .header("Content-Type", "application/json")
    .header("Authorization", "Bearer " + accessToken)
    .POST(BodyPublishers.ofString(requestBody.toString()))
    .build();

HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
JSONObject jsonResponse = new JSONObject(response.body());
String sessionId = jsonResponse.getString("sessionId");

System.out.println("Session created with id: " + sessionId);
[TAB] HTTP using Python
headers = {
    "Content-Type": "application/json",
    "Authorization": f"Bearer {access_token}"
}

data = {
    "device": {
        "ipv4Address": {
            "publicAddress": "203.0.113.25",
            "publicPort": 8080
        },
        "applicationServer": {
            "ipv4Address": "0.0.0.0/0"
        },
        "qosProfile": "QOS_E",
        "duration": 300
    }
}

response = requests.post(
    "https://opengateway.aggregator.com/qod/v0/sessions",
    headers=headers,
    data=json.dumps(data)
)

result = response.json()
print(f"Session created with id: {result.get('sessionId')}")

Frontend flow

The recommended API consumption flow when triggered from the frontend application, running on the end-user device, is implementing the OIDC standard Authorization Code Flow for authentication:

You can check the CAMARA documentation on this flow here.

  • Application's frontend performs an HTTP request to get a code, and provides a redirect_uri it wants such code to be redirected to.
  • Application's frontend will receive an HTTP redirect (status 302) and needs to be able to handle it. If it is a web application running on a web browser, the browser will natively follow the redirection. If it is not, in depends on the coding language and/or HTTP module or library used, or on its settings, how the flow will follow all the way to your application's backend through the mobile network operator authentication server.
  • Application's backend receives the code from this HTTP redirection, by publishing an endpoint in the given redirect_uri, and then exchanges it for an access token. The latter is achieved as shown in the Backend flow.

Requesting the authorization code from the frontend

The following samples show how your application can trigger the authentication flow from the frontend either from code or by submitting a simple HTML form. The same can be achieved from code in any other programming language with the ability to perform HTTP requests:

[TAB] HTTP using JavaScript (ES6)
let clientId = "my-app-id";
let clientSecret = "my-app-secret";
let apiPurpose = "dpv:RequestedServiceProvision#qod";
let myAuthCallbackEndpoint = "https://my_app_server/qod-auth-callback";

const params = {
  client_id: clientId,
  response_type: "code",
  purpose: apiPurpose,
  redirect_uri: myAuthCallbackEndpoint
};

// Create the query string
const queryString = new URLSearchParams(params).toString();

// URL with query string
const url = `https://opengateway.aggregator.com/authorize?${queryString}`;

const requestOptions = {
  method: "GET",
  redirect: "follow"
};

fetch(url, requestOptions);
[TAB] HTML form
<!--
    In this example you need your auth callback URL to continue the flow calling the service API
    and providing an HTML response to be shown in the web browser displaying the result.
    The following form is published from the same web server hosting the auth callback URL.
-->

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>API Request Form</title>
</head>
<body>
    <h1>Quality on Demand session creation</h1>
    <form id="apiRequestForm" action="https://opengateway.aggregator.com/authorize" method="GET">
        <input type="hidden" name="client_id" value="my-app-id">
        <input type="hidden" name="response_type" value="code">
        <input type="hidden" name="purpose" value="dpv:RequestedServiceProvision#qod">
        <input type="hidden" name="redirect_uri" value="/qod-auth-callback">
        
        <label for="create">This will create an improved connectivity session for your device</label>
        <button type="submit" name="create">Create session</button>
    </form>
</body>
</html>

Getting the access token from the auth callback endpoint at the backend

Samples represent how to publish the auth callback URL in Python or Node.js, so the code from the Auth Code Flow can be received. The same can be achieved in any other language with capabilities to run an HTTP server and listen for the redirect from the authentication flow:

[TAB] SDK for Python
from flask import Flask, request, jsonify
from aggregator_opengateway_sdk import ClientCredentials, QoDMobile, QoSProfiles

credentials = ClientCredentials(
    clientid='my-app-id',
    clientsecret='my-app-secret'
)

app = Flask(__name__)

@app.route('/qod-auth-callback', methods=['GET'])
def auth_callback():
    code = request.args.get('code', '')
    qod_client = QoDMobile(client=credentials, auth_code=code)

if __name__ == '__main__':
    app.run()
[TAB] SDK for Node.js
import { ClientCredentials, QoDMobile } from "aggregator/opengateway-sdk"
import express from "express"

const credentials: ClientCredentials(
    clientId: 'my-app-id',
    clientSecret: 'my-app-secret'
)

const app = express()
const port = 3000

app.get('/qod-auth-callback', (req, res) => {
    const code = req.query.code
    const qodClient = new QoDMobile(credentials, code)
})

app.listen(port, () => {
    console.log(`QoD authorization callback URL is running`);
})

API Usage

Once we have instantiated the SDK class with the authorization code received from the frontend, we can just call the class service function to effectively create a QoD session for the frontend device, by getting its IP address from the HTTP request header.

For that, still in the code of the auth callback URL endpoint listener, following to the QoDMobile class instantiation, follow the samples below:

[TAB] SDK for Python
    ip = request.remote_addr
    port = request.environ.get('REMOTE_PORT')

    qod_client.set_quality(300, QoSProfiles.QOS_E, client_ip=ip, client_port=port)
[TAB] SDK for Node.js
    const ip = req.ip
    const port = req.socket.remotePort

    qodClient.setQualityOfService(300, QoSProfiles.QOS_E, ip, port)

In case of not using an SDK, at this point, the samples for the backend flow works for the frontend flow once we have gotten the frontend device IP address from the HTTP request header.


What’s Next