1

I am using DialogFlow V1 node.js webhook and I have a problem. I have an intent that will connect to the Spotify API to get tracks for my chatbot:

const {DialogflowApp} = require('actions-on-google');
function spotifyIntent(app) {
    //Get search parameters
    [...]
    return asyncFunction(query).then(function (message) {
            this.app.ask(message);
            return Promise.resolve();
        }).catch(function (err) {
            this.app.ask("Une erreur est apparue: "+err);
            return Promise.resolve();
        })
}

with my async function being:

function asyncFunction(query) {
    spotifyApi.clientCredentialsGrant()
        .then(function(data) {
            // spotifyApi.setAccessToken(data.body['access_token']);
            return spotifyApi.searchTracks(query);
        }).then(function(data) {
        const link = data.body.tracks.items[0].preview_url;
        let speech = '<speak>Here is the first result found.<audio src="'+ link +'">I didn\'t found it</audio></speak>';
        return Promise.resolve(speech);
    }).catch(function(err) {
        return Promise.reject(err);
    });
}

A Promise is hidden in the call of clientCredentialsGrant() and searchTracks();

My intent is called by the classic map with action: intentFunction. I read here that the ask method should work but it doesn't for me. I tried the version with the Promise and with a callback but when I simulate my code I always get the answer:

"message": "Failed to parse Dialogflow response into AppResponse, exception thrown with message: Empty speech response

I don't understand what I'm doing wrong here? I know the problem come from the request being async but it should work fine with the Promise or the callback because right now it returns instantly?

Rémi C.
  • 339
  • 2
  • 12

1 Answers1

2

It returns instantly because, although asyncFunction() calls something that returns a Promise, and although the then() and catch() portions return a Promise... asyncFunction() itself does not return a promise. In fact, it doesn't return anything explicitly, so it returns undefined.

You probably want that code to be

function asyncFunction(query) {
    return spotifyApi.clientCredentialsGrant()
        .then(function(data) {
            // spotifyApi.setAccessToken(data.body['access_token']);
            return spotifyApi.searchTracks(query);
        }).then(function(data) {
        const link = data.body.tracks.items[0].preview_url;
        let speech = '<speak>Here is the first result found.<audio src="'+ link +'">I didn\'t found it</audio></speak>';
        return Promise.resolve(speech);
    }).catch(function(err) {
        return Promise.reject(err);
    });
}

Note the change in the first line that adds the return statement.

This is a common pattern (and a frequently overlooked problem) when dealing with async functions that need to return a Promise.

Prisoner
  • 49,922
  • 7
  • 53
  • 105
  • Thank you for your answer! Unfortunately it doesn't solve my problem... I also tried the example of using the weather API on DialogFlow website but it doesn't work too... I will try a new project with only the code in it to observe the behaviour – Rémi C. Mar 28 '18 at 07:01
  • I found my error... I still didn't make the call to Spotify API work but I managed to get the one to my weather API to work... It was because I did not activate my billing for my project on google cloud platform. Honestly they could inform us about that somewhere :( Thanks again – Rémi C. Mar 28 '18 at 09:26
  • Ok I managed to solve it! MY other error was that I used this.app.ask to send the message and I had to pass the app in argument for the function. – Rémi C. Mar 28 '18 at 12:33
  • Given the scoping, you probably didn't need to pass it and didn't need the `this`. I missed that part. – Prisoner Mar 28 '18 at 12:41