How to make http call on DialogFlow v2 using Javascript ajax call
Asked Answered
S

6

6

I found this example on the official site of DialogFlow using Node.js and it is working fine, but I dont know how do I integrate this into my web application.

Is it possible that I can integrate this into my other javascript jquery code? and here I need to run node index.js but do I still need to do this if I integrate with my code?

const projectId = 'xxx'; //https://dialogflow.com/docs/agents#settings
const sessionId = 'xxxxx';
const query = 'Hello';
const languageCode = 'en-US';

// Instantiate a DialogFlow client.
const dialogflow = require('dialogflow');
const sessionClient = new dialogflow.SessionsClient();

// Define session path
const sessionPath = sessionClient.sessionPath(projectId, sessionId);
console.log(sessionPath);
// The text query request.
const request = {
  session: sessionPath,
  queryInput: {
    text: {
      text: query,
      languageCode: languageCode,
    },
  },
};

// Send request and log result
sessionClient
  .detectIntent(request)
  .then(responses => {
    console.log('Detected intent');
    const result = responses[0].queryResult;
    console.log(`  Query: ${result.queryText}`);
    console.log(`  Response: ${result.fulfillmentText}`);
    if (result.intent) {
      console.log(`  Intent: ${result.intent.displayName}`);
    } else {
      console.log(`  No intent matched.`);
    }
  })
  .catch(err => {
    console.error('ERROR:', err);
  });

Are there any alternative that we can use DialogFlow v2 using normal javascript jquery, ajax without me having to do node index.js everytime I want to use dialogflow.

DialogFlow v1 was quite simple to use. I had it something like this:

fetch(url, {
    body: JSON.stringify(data),
    // cache: 'no-cache',
    // credentials: 'same-origin',
    headers: {
        'content-type': 'application/json',
        "Authorization": "Bearer " + configs.accessToken,
    },
    method: 'POST',
    mode: 'cors',
    redirect: 'follow',
    referrer: 'no-referrer',
})
    .then(response => response.json()) // parses response to JSON
Suffice answered 31/5, 2018 at 14:47 Comment(2)
what do you want to fetch from api?Heaviness
@Heaviness i would want to use dialogflow api v2 for chatbotSuffice
W
2

As others have said here before, the access token has a duration of one hour, after that time it becomes useless. So it's necessary to make a call (http call in my case) to the API in order to request an access token, one time each hour, and use it as explained by Satheesh thereafter. Instructions on how to generate the signature to make the call and use it later are given in https://developers.google.com/identity/protocols/OAuth2ServiceAccount.

Once you obtain the json file from the service account with the private key and the email you have to use (not your email, but the one generated by the service account), you can use the jsrsasign library (in pure javascript), that you can find in https://github.com/kjur/jsrsasign, to generate the JSON Web Signature (JWS) and thus the JSON Web Token (JWT), that will be needed to make the http call to get the access token.

Then you use it as described above by Satheesh to make the call to Dialogflow V2 via jQuery.

The code I have used to achieve this is the next one:

To generate the JWT (using the related library):

function _genJWS() {
    var header = '{"alg":"RS256","typ":"JWT"}';
    var claimSet = jwtClaimSet();
    var privateKey = jwtPrivateKey();

    var sHead = newline_toDos(header);
    var head = KJUR.jws.JWS.readSafeJSONString(sHead);
    var sPayload = newline_toDos(claimSet);
    var sPemPrvKey = privateKey;

    var jws = new KJUR.jws.JWS();
    var sResult = null;
    try {
        prv = KEYUTIL.getKey(sPemPrvKey);

        sResult = KJUR.jws.JWS.sign(head.alg, sHead, sPayload, prv);
    } catch (ex) {
        alert("Error: " + ex);
    }
    return sResult;
}

To request the access token:

function _requestAccessToken() {
    var access_token = accessToken;
    var assertion = _genJWS();
    console.log('Assertion: ' + assertion);
    jQuery.ajax({
        type: "POST",
        url: "https://www.googleapis.com/oauth2/v4/token",
        contentType: "application/x-www-form-urlencoded",
        dataType: "json",
        data: "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&assertion=" + assertion,
        success: function(response) {
            console.log("success");
            console.log(response);
            access_token = response.access_token;
            console.log(access_token);
        },
        error: function() {
            console.log("Error");
        }
    });
    return access_token;
}

Then use that access token to make the HTTP call to Dialogflow.

Hope it helps.

Womankind answered 22/3, 2019 at 13:24 Comment(6)
How do you implement the jwtClaimSet function? I've figured out everything else (I think) except for that.Fairlead
Nevermind, I figured it out (based on your answer), and posted the solution here: https://mcmap.net/q/1813316/-how-to-make-http-call-on-dialogflow-v2-using-javascript-ajax-callFairlead
function jwtClaimSet() { var clst = ""; if (!Date.now) { Date.now = function() { return new Date().getTime(); } } timestampInSeconds = Math.floor(Date.now() / 1000); nextHourInSeconds = timestampInSeconds + (60*60); var iss = "[email protected]"; var scope = "googleapis.com/auth/dialogflow"; var aud = "googleapis.com/oauth2/v4/token"; var exp = nextHourInSeconds; var iat = timestampInSeconds; clst = JSON.stringify({ "iss":iss, "scope":scope, "aud":aud, "exp":exp, "iat":iat }); return clst; }Womankind
I have copied all 3 functions into my javascript file and included script tag for jsrsasign library in html as <script src="cdnjs.cloudflare.com/ajax/libs/jsrsasign/8.0.20/…> Getting error, jwtPrivateKey not found. Seems this function from that library is not visible to js file. @Womankind What do i need to do?Unreason
I am running this entire (completely javascript) code on browser/client itself. No Node/server is involved.Unreason
Got it working from another link. Thanks. https://mcmap.net/q/1916337/-how-can-i-retrieve-a-service-account-oauth2-token-from-google-api-with-javascriptUnreason
H
1

You can easily call Dialogflow's V2 API detectIntent endpoint from jQuery.

The API docs show the URL and request formats:

POST https://dialogflow.googleapis.com/v2/{session=projects/*/agent/sessions/*}:detectIntent

{
  "queryParams": {
    object(QueryParameters)
  },
  "queryInput": {
    object(QueryInput)
  },
  "inputAudio": string
}

Authentication works slightly differently; instead of using an access token, you'll create a service account and key using the Cloud dashboard. This documentation page explains how.

Hesler answered 4/6, 2018 at 17:17 Comment(4)
I have already created service accout and key, how do I use these to make http/ajax requests from the example you showed? Authorization: Bearer $(gcloud auth print-access-token) this does not simply work for me, can you show some example?Suffice
I cannot see a way how do I use my service account key with the jquery, how to authenticate so I can use dialogflow api?Suffice
Same problem there is not any tutorial on using the V2 api. Incredible! already 2 weeks lost...Unready
@Suffice and Federico I figured it out (based on JDE10's answer), and posted the solution here: https://mcmap.net/q/1813316/-how-to-make-http-call-on-dialogflow-v2-using-javascript-ajax-callFairlead
A
0

https://dialogflow.com/docs/reference/v2-auth-setup

After following through the page above and created service keys, you must have a json file like this:

{
    "type": "",
    "project_id": "",
    "private_key_id": "",
    "private_key": "",
    "client_email": "",
    "client_id": "",
    "auth_uri": "",
    "token_uri": "",
    "auth_provider_x509_cert_url": "",
    "client_x509_cert_url": ""
  }  

With this file, execute these two commands (you need to install gcloud CLI if have not yet):

gcloud auth activate-service-account --key-file=<service-account-key-file.json>
gcloud auth print-access-token

After those, copy the access token and supply it to your program as an environment variable (or hardcode if you don't care). Then, you just make an HTTP post request. I use axios in my React project, here is a sample code for detectIntent:

import axios from "axios";

const DIALOG_FLOW_TOKEN = process.env.REACT_APP_DIALOG_FLOW_TOKEN;
const DIALOG_FLOW_API_ROOT_URL = "https://dialogflow.googleapis.com/v2";
const YOUR_PROJECT_ID = <YOUR_PROJECT_ID>;
const SESSION_ID = <SESSION_ID>;
const URL = `${DIALOG_FLOW_API_ROOT_URL}/projects/${YOUR_PROJECT_ID}/agent/sessions/${SESSION_ID}:detectIntent`;

var config = {
    headers: {
        "Authorization": "Bearer " + DIALOG_FLOW_TOKEN,
        "Content-Type": "application/json"
    }
};

export function sendText(text) {
    var bodyParameters = {
        "queryInput": { "text": { "text": text, "languageCode": "en" } },
    };

    const request = axios.post(
        URL,
        bodyParameters,
        config
    );

    return request;
} 
Appenzell answered 8/6, 2018 at 14:30 Comment(2)
access token expires after an hour, how do u deal with this?Suffice
Yes, you are right, sorry my mistake. But I have found this answer that may be helpful, have you checked it? #47060476Jorgensen
T
0

The below snippet explains how we can communicate from a webapp to dialogflow NLU. To get access token we can make use of Google cloud SDK to obtain the token, but it has one hour validity so having this in a different service and obtain it before making call to dialogflow will give a workaround.

$(document).ready(function(){

$("button").click(function(){

    $.ajax({
        type: "POST",
        url: "https://dialogflow.googleapis.com/v2/projects/YOUR-PROJECT-NAME/agent/sessions/SESSIONIDOFYOURCHOICE:detectIntent",
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        headers: {
            "Authorization": "Bearer " + "YOUR ACCESS TOKEN GOES HERE",
        },
        data: JSON.stringify({ "queryInput":{
            "text":{
                "text":"YOUR QUERY TO NLU",
                "languageCode":"en-US"
            }
        } }),
        success: function(data) {
            $("#div").html(data.queryResult.fulfillmentText);
        },
        error: function() {
            console.log("Internal Server Error");
        }
    });     

});

});

Tarazi answered 31/8, 2018 at 23:46 Comment(3)
But how does one go about generating the token from a "different service" like a cloud function? developers.google.com/identity/protocols/OAuth2ServiceAccount and googleapis npm module seem like good places to start, but I haven't found clear instructions onhow to generate these access tokens.Keynesianism
I still haven`t interacted enough to improve my reputation and leave a comment. This is just to try to give a hint to Doug when he asked how to generate the access token. In my case this is located in the Dialogflow console itself, inside the agents profile -clicking in the gear near the agent name-, under the "general" tab. I actually have two access tokens, a client access token and a developer access token. I have read somewhere that I need the developer one for detectIntent issues. Hope it helps.Womankind
@Keynesianism I figured it out (based on JDE10's answer), and posted the solution here: https://mcmap.net/q/1813316/-how-to-make-http-call-on-dialogflow-v2-using-javascript-ajax-callFairlead
F
0

I've found a way to get a valid access-token entirely from frontend (browser) JavaScript.

1) Install the npm package jsrsasign from npm.

2) Add the files in this Gist to your project: https://gist.github.com/Venryx/bfa9b69583638dfbf611a9f292721a75

3) Enter values for all of the constants at the start of DialogFlowExample.ts.

Your service account information can be found at: https://console.cloud.google.com/iam-admin/serviceaccounts

The service account's email is listed in the table, and its private-key (if you've created one) is found within the downloaded key json file. If you don't remember where you placed the key json file (or you haven't created one yet), you can use "Actions->Create key" to create a new one.

4) Call GetAccessToken with your claim set and private key (see Gist for example). It will return a promise.

5) Once the access-token is retrieved, the promise is resolved with the retrieved access-token. (The function caches the access-token for 60 minutes. After that period, the token is expired, so calling the function again will have it retrieve a new one.)

Example

The linked Gist contains an almost-complete usage example for converting a segment of audio to text using DialogFlow. The only part it doesn't show is how to obtain the "segment of audio".

For that, see part 1 of my answer here: https://mcmap.net/q/1916338/-how-to-record-microphone-audio-in-javascript-and-submit-to-dialogflow

Fairlead answered 9/9, 2019 at 16:15 Comment(2)
does this mean you expose your service account's private key to anyone's internet browser?Umbilicus
@systemprogrammer Yes, that's correct. Because of that, I wouldn't recommend it for large projects in production, as a malicious actor could use your service key to create bogus transcription calls that max out your quota. That said, this in-browser method is helpful for smaller projects (or projects unlikely to attract malicious users) where setting up a server component as the middleman is an unnecessary hassle for something only a small number (or trusted group of) people will be using anyway. (if it happened, you'd need to invalidate the key, change the code, then create a new one)Fairlead
R
-1

This way it works for me.

const dialogflow = require('dialogflow');
var projectId = ""; // your dialogflow project id when you click on settings of the DF agent
var sessionId = ""; // session ID
var config = {
// use credentials or keyFilename i'm using keyFile
    // credentials: {
        // private_key: privateKey,
        // client_email: clientEmail,
    // }
        keyFilename: './google.json'
};

var sessionClient = new dialogflow.SessionsClient(config);

async sendTextMessageToDialogFlow(textMessage) {
        // Define session path
        const sessionPath = sessionClient.sessionPath(projectId, sessionId);
        // The text query request.
        const request = {
            session: sessionPath,
            queryInput: {
                text: {
                    text: textMessage,
                    languageCode: 'en'
                }
            }
        }
        try {
            let [responses] = await sessionClient.detectIntent(request)
            console.log(responses);
        } catch (err) {
            console.error('DialogFlow.sendTextMessageToDialogFlow ERROR:', err);
            throw err
        }
    };
Roumell answered 2/4, 2020 at 11:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.