Enterprise Java

Conversational UI with Oracle Digital Assistant and Fn Project. Part III, Moving to the cloud

In this post I am going to continue the story of implementing a conversational UI for FlexDeploy on top of Oracle Digital Assistant and
Fn Project. Today I am going to move the serverless API working around my chatbot to the cloud, so the entire solution is working in the cloud:

Conversational UI

The API is implemented as a set of Fn functions collected into an Fn application. The beauty of Fn is that it’s just a bunch of Docker containers that can equally run on your laptop on your local Docker engine and somewhere in the cloud. Having said that I can run my Fn application on a K8s cluster from any cloud provider as it is described here. But today is not that day. Today I am going to run my serverless API on a brand new cloud service Oracle Functions which is built on top of Fn. The service is not general available yet, but I participate in the Limited Availability program so I have a trial access to it, I can play with it and blog about it. In this solution I had to get rid of the Fn Flow implemented here and get back to my original implementation as Fn Flow is not supported by Oracle Functions yet. I hope it will be soon as this is actually the best part.

So, having our OCI environment configured and havingOracle Functions service up and running (I am not reposting Oracle tutorial on that here), we need to configure our Fn CLI to be able to communicate with the service:

fn create context oracle_fn --provider oracle 
fn use context oracle_fn
fn update context oracle.compartment-id MY_COMPARTMENT_ID
fn update context api-url https://functions.us-phoenix-1.oraclecloud.com
fn update context registry phx.ocir.io/flexagonoraclecloud/flexagon-repo
fn update context oracle.profile oracle_fn

Ok, so now our Fn command line interface is talking to Oracle Functions. The next step is to create an application in theOracle Functions console:

Conversational UI

Now we can deploy the Fn application toOracle Functions:

Eugenes-MacBook-Pro-3:fn fedor$ ls -l
total 8
-rw-r--r--@ 1 fedor  staff   12 Dec  4 15:41 app.yaml
drwxr-xr-x  5 fedor  staff  160 Feb  9 15:24 createsnapshotfn
drwxr-xr-x  6 fedor  staff  192 Feb  9 15:25 receiveFromBotFn
drwxr-xr-x  6 fedor  staff  192 Feb  9 15:25 sendToBotFn
Eugenes-MacBook-Pro-3:fn fedor$ 
Eugenes-MacBook-Pro-3:fn fedor$ 
Eugenes-MacBook-Pro-3:fn fedor$ fn deploy --all

Having done that we can observe the application in theOracle Functions console:

Conversational UI

The next step is to update API urls in the chatbot and on my laptop so the functions in the cloud are invoked instead of the previous local implementation. The urls can be retrieved with the following command:

fn list triggers odaapp

So far the migration from my laptop to Oracle Functions has been looking pretty nice and easy. But here is a little of pain. In order to invoke functions hosted in Oracle Functions with http requests, the requests should be signed so they can pass through the authentication. A node.js implementation of invoking a signed function call looks like this:

var fs = require('fs');
var https = require('https');
var os = require('os');
var httpSignature = require('http-signature');
var jsSHA = require("jssha");

var tenancyId = "ocid1.tenancy.oc1..aaaaaaaayonz5yhpr4vxqpbdof5rn7x5pfrlgjwjycwxasf4dkexiq";
var authUserId = "ocid1.user.oc1..aaaaaaaava2e3wd3cu6lew2sktd6by5hnz3d7prpgjho4oambterba";
var keyFingerprint = "88:3e:71:bb:a5:ea:68:b7:56:fa:3e:5d:ea:45:60:10";
var privateKeyPath = "/Users/fedor/.oci/functions_open.pem";
var privateKey = fs.readFileSync(privateKeyPath, 'ascii');
var identityDomain = "identity.us-ashburn-1.oraclecloud.com";


function sign(request, options) {
    var apiKeyId = options.tenancyId + "/" + options.userId + "/" + options.keyFingerprint;

    var headersToSign = [
        "host",
        "date",
        "(request-target)"
    ];

    var methodsThatRequireExtraHeaders = ["POST", "PUT"];

    if(methodsThatRequireExtraHeaders.indexOf(request.method.toUpperCase()) !== -1) {
        options.body = options.body || "";
        var shaObj = new jsSHA("SHA-256", "TEXT");
        shaObj.update(options.body);

        request.setHeader("Content-Length", options.body.length);
        request.setHeader("x-content-sha256", shaObj.getHash('B64'));

        headersToSign = headersToSign.concat([
            "content-type",
            "content-length",
            "x-content-sha256"
        ]);
    }


    httpSignature.sign(request, {
        key: options.privateKey,
        keyId: apiKeyId,
        headers: headersToSign
    });

    var newAuthHeaderValue = request.getHeader("Authorization").replace("Signature ", "Signature version=\"1\",");
    request.setHeader("Authorization", newAuthHeaderValue);
}


function handleRequest(callback) {

    return function(response) {
        var responseBody = "";
        response.on('data', function(chunk) {
        responseBody += chunk;
    });


        response.on('end', function() {
            callback(JSON.parse(responseBody));
        });
    }
}


function createSnapshot(release) {

    var body = release;

    var options = {
        host: 'af4qyj7yhva.us-phoenix-1.functions.oci.oraclecloud.com',
        path: '/t/createsnapshotfn',
        method: 'POST',
        headers: {
            "Content-Type": "application/text",
        }
    };


    var request = https.request(options, handleRequest(function(data) {
        console.log(data);
    }));


    sign(request, {
        body: body,
        privateKey: privateKey,
        keyFingerprint: keyFingerprint,
        tenancyId: tenancyId,
        userId: authUserId
    });

    request.end(body);
};

This approach should be used by Oracle Digital Assistant custom components and by the listener component on my laptop while invoking the serverless API hosted inOracle Functions.

That’s it!

Published on Java Code Geeks with permission by Eugene Fedorenko , partner at our JCG program. See the original article here: Conversational UI with Oracle Digital Assistant and Fn Project. Part III. Moving to the cloud.

Opinions expressed by Java Code Geeks contributors are their own.

Eugene Fedorenko

I am a Senior Architect at Flexagon focusing on ADF and many other things.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button