Как отловить ошибку тайм-аута в лямбда-функции для навыка Алексы? - PullRequest
0 голосов
/ 27 марта 2019

Я создаю навык Alexa, связанный с лямбда-функцией AWS.Для некоторых целей мне нужно сделать http-запрос, и это может не получиться из-за задержки соединения, которую я не могу предсказать.Проблема в том, что лямбда прерывает мое выполнение, если я использую более 128 МБ или выполнение занимает более 7000 мс.Так как я не хочу увеличивать свой лимит времени, я искал способ предотвратить лямбду, отправляющую сообщение об ошибке моему навыку Alexa, который сообщит мне, что возникла проблема, но отправит себе сообщение о том, что соединение может быть медленным, и Alexa не можетне могу найти то, что спросил пользователь.

Я попытался получить доступ к отчету консоли, чтобы проверить наличие ошибки тайм-аута, но я новичок в nodejs и не могу предотвратить лямбда-отправку этой ошибки в Alexa.

Это мой лямбда-код:

var https = require('https');
const fs = require('fs');


let speechOutput;
let reprompt;
let welcomeOutput = "Salve Guardiano, per ora puoi chiedermi cosa porta Xur questa settimana, quali cala la notte ci sono, dove si trova focolai o anche in che stato si trova la città sognante.";
let welcomeReprompt = "Come posso aiutarti?";
"use strict";
const Alexa = require('alexa-sdk');
const APP_ID = undefined;  // TODO replace with your app ID (OPTIONAL).
speechOutput = '';


const handlers = {
    'LaunchRequest': function () {
        this.emit(':ask', welcomeOutput, welcomeReprompt);
    },
    'AMAZON.HelpIntent': function () {
        speechOutput = 'Per ora puoi chiedermi cosa porta Xur questa settimana, quali cala la notte ci sono, dove si trova focolai o anche in che stato si trova la città sognante.';
        this.emit(':ask', speechOutput);
    },
   'AMAZON.CancelIntent': function () {
        speechOutput = 'Ok, cancello.';
        this.emit(':tell', speechOutput);
    },
   'AMAZON.StopIntent': function () {
        speechOutput = 'Arrivederci, non camperare troppo.';
        this.emit(':tell', speechOutput);
   },
   'SessionEndedRequest': function () {
        speechOutput = '';
        //this.emit(':saveState', true);//uncomment to save attributes to db on session end
        this.emit(':tell', speechOutput);
   },
    'AMAZON.NavigateHomeIntent': function () {
        speechOutput = '';

        //any intent slot variables are listed here for convenience


        //Your custom intent handling goes here
        speechOutput = "Chiedimi aiuto.";
        this.emit(":ask", speechOutput);
    },
    'informazioni_settimanali': async function () {

        //prendo il codice corrispondente allo slot comunicato
        let activityID = this.event.request.intent.slots.activity.resolutions.resolutionsPerAuthority[0].values[0].value.id;
        //prendo il token di accesso
        let token = this.event.context.System.user.accessToken;

        speechOutput = '';

        speechOutput = await richiesta_activities(activityID);
        speechOutput = speechOutput + "Posso fare altro per la tua luce?"

        this.emit(":ask", speechOutput );
    },  
    'xur_location': async function () {
        speechOutput='Non ho ancora sviluppato questa funzionalità. Posso aiutarti in altro modo?';
        this.emit(":ask", speechOutput, speechOutput);
    },
    'what_sells_xur': async function () {
        //prendo il token di accesso
        let token = this.event.context.System.user.accessToken;
        if(token==null){
            speechOutput = 'Per richiedere questo tipo di informazioni devi collegare il tuo account Bungie alla skill Spettro dall\'app Alexa.';
            this.emit(":tellWithLinkAccountCard",speechOutput);

        }else{
            speechOutput='';
            speechOutput = await xur_inventory(token);
            speechOutput = speechOutput + " Posso fare altro per la tua luce?"
            this.emit(":ask", speechOutput );
        }
    },
    'Unhandled': function () {
        speechOutput = "Perdonami, non ho capito.";
        this.emit(':ask', speechOutput, speechOutput);
    }
};

exports.handler = (event, context,callback) => {
    const alexa = Alexa.handler(event, context);
    alexa.appId = APP_ID;
    // To enable string internationalization (i18n) features, set a resources object.
    //alexa.resources = languageStrings;
    alexa.registerHandlers(handlers);
    //alexa.dynamoDBTableName = 'DYNAMODB_TABLE_NAME'; //uncomment this line to save attributes to DB
    alexa.execute();
};

//    END of Intent Handlers {} ========================================================================================


async function xur_inventory(token){
    let Hash='2190858386';  //xur's hash
    let endpoint = '/Platform/Destiny2/2/Profile/4611686018456602435/Character/2305843009289717384/Vendors/?components=vendorsales';
    const response = await httpTokenGet(endpoint,token);
    let ans='';
    //console.log(response);
    if(response.Response.sales.data[Hash]!=null){
        let items = new Array();
        var i=0;
        let temp;
        temp = response.Response.sales.data[Hash].saleItems;
        for(var key in temp){
            //console.log(key);
            //console.log(temp[key].itemHash);
            items[i]=temp[key].itemHash;
            i++;
        }

        //LETTURA AGGIORNATA DAL WEB
        let jsonendpoint = '/it/reducedCollectableInventoryItems.json';
        const DestinyItems = await httpJsonGet(jsonendpoint);
        //console.log(DestinyItems);
        let itemsPlain = new Array();
        let itemsType = new Array();

        for(let i=0;i<items.length;i++){
            if(DestinyItems[items[i]]!=null){
                itemsPlain[i]=DestinyItems[items[i]].displayProperties.name;
                itemsType[i]=DestinyItems[items[i]].itemTypeDisplayName;
                //console.log(itemsPlain[i]);
                //console.log(itemsType[i]);
            }
        }
        ans = "Ecco cosa vende Xur: ";
        for (var i=0;i<itemsPlain.length;i++){
            ans = ans + itemsPlain[i] + " ("+ itemsType[i]+"). ";
        }
        return ans;

    }else {
        return "Mi spiace non riesco a trovare Xur. ";
    }
}

async function richiesta_activities(activityID,token){
    //this function returns the activity's infos selected by the user
    let endpoint = '';
    if(activityID==0){
        //cala la notte
        let Hash='2171429505';

        endpoint = '/Platform/Destiny2/Milestones/';
        const response = await httpGet(endpoint);
        let nightfalls = new Array();
        if(response.Response[Hash]!=null){
            for(let i=0;i<3;i++){
                nightfalls[i]=response.Response[Hash].activities[i].activityHash;
            }


            //LETTURA AGGIORNATA DAL WEB
            let jsonendpoint = '/en/reducedActivities.json';
            const Destinyactivities = await httpJsonGet(jsonendpoint);

            let nightfallsPlain = new Array();

            for(let i=0;i<3;i++){
                nightfallsPlain[i]=Destinyactivities[nightfalls[i]].displayProperties.name;
                console.log(nightfallsPlain[i]);
            }
            let ans = "Ecco i cala la notte che ho trovato: ";
            for (var i=0;i<3;i++){
                let end=nightfallsPlain[i].lengt;
                nightfallsPlain[i]=nightfallsPlain[i].substring(10,end);
                ans = ans + nightfallsPlain[i] + ", "; 
            }
            return ans;
        }
        else{
            return "Purtroppo non riesco ad aiutarti.";
        }
    }
    else if(activityID==1){
        //focolai
        let Hash = '463010297';
        endpoint = '/Platform/Destiny2/Milestones/';
        const response = await httpGet(endpoint);
        if(response.Response[Hash]!=null){
            var flashpoint = response.Response[Hash].availableQuests[0].questItemHash;
            console.log(flashpoint);
            var text = '';

            //LETTURA DAL WEB
            let jsonendpoint = 'https://destiny.plumbing/en/raw/DestinyInventoryItemDefinition.json';
            const DestinyFlashpoint = await httpJsonGet(jsonendpoint);

            if(DestinyFlashpoint[flashpoint]!=null){
                console.log(DestinyFlashpoint[flashpoint]);
                let flashpointPlain = DestinyFlashpoint[flashpoint].displayProperties.name;
                let location=flashpointPlain.substring(10,flashpointPlain.length);

                return "Questa settimana, il focolaio si trova su "+location+".";
            }else{
                return "Purtroppo non riesco ad aiutarti.";
            }

        }else{
            return "Purtroppo non riesco ad aiutarti.";
        }
    }else{
        return "Devo ancora sviluppare le altre attività.";
    }
}


function httpTokenGet(endpoint,token) {
  return new Promise(((resolve, reject) => {
    var options = {
        host: 'www.bungie.net',
        path: endpoint,
        method: 'GET',
        headers: {'X-API-KEY': 'xxxxxxxxxxxxxxxxxxxxxx', 
        'Authorization': 'Bearer '+ token},
    };

    const request = https.request(options, (response) => {
      response.setEncoding('utf8');
      let returnData = '';

      response.on('data', (chunk) => {
        returnData += chunk;
      });

      response.on('end', () => {
        resolve(JSON.parse(returnData));
      });

      response.on('error', (error) => {
        reject(error);
      });
    });
    request.end();
  }));
}

function httpGet(endpoint) {
  return new Promise(((resolve, reject) => {
    var options = {
        host: 'www.bungie.net',
        path: endpoint,
        method: 'GET',
        headers: {'X-API-KEY': 'xxxxxxxxxxxxxxxxxxxxxx'},
    };

    const request = https.request(options, (response) => {
      response.setEncoding('utf8');
      let returnData = '';

      response.on('data', (chunk) => {
        returnData += chunk;
      });

      response.on('end', () => {
        resolve(JSON.parse(returnData));
      });

      response.on('error', (error) => {
        reject(error);
      });
    });
    request.end();
  }));
}


function httpJsonGet(endpoint) {
  return new Promise(((resolve, reject) => {
    var options = {
        host: 'destiny.plumbing',
        path: endpoint,
        method: 'GET',
    };

    const request = https.request(options, (response) => {
      response.setEncoding('utf8');
      let returnData = '';

      response.on('data', (chunk) => {
        returnData += chunk;
      });

      response.on('end', () => {
        resolve(JSON.parse(returnData));
      });

      response.on('error', (error) => {
        reject(error);
      });
    });
    request.end();
  }));
}

Проблема может возникнуть здесь в функции async function richiesta_activities(activityID,token)

//LETTURA DAL WEB
            let jsonendpoint = 'https://destiny.plumbing/en/raw/DestinyInventoryItemDefinition.json';
            const DestinyFlashpoint = await httpJsonGet(jsonendpoint);

, когда я вызываю await httpJsonGet(jsonendpoint);

На самом делево время тестирования лямбда-консоли записывает следующее:

Response:
{
  "errorMessage": "2019-03-27T17:21:48.437Z 855210e7-4c89-474b-af57-0bdd3e9d89d5 Task timed out after 7.01 seconds"
}

Request ID:
"855210e7-4c89-474b-af57-0bdd3e9d89d5"

Function Logs:
START RequestId: 855210e7-4c89-474b-af57-0bdd3e9d89d5 Version: $LATEST
2019-03-27T17:21:41.520Z    855210e7-4c89-474b-af57-0bdd3e9d89d5    Warning: Application ID is not set
END RequestId: 855210e7-4c89-474b-af57-0bdd3e9d89d5
REPORT RequestId: 855210e7-4c89-474b-af57-0bdd3e9d89d5  Duration: 7007.93 ms    Billed Duration: 7000 ms    Memory Size: 128 MB Max Memory Used: 128 MB 
2019-03-27T17:21:48.437Z 855210e7-4c89-474b-af57-0bdd3e9d89d5 Task timed out after 7.01 seconds

Но Алекса говорит, что была проблема с навыком.Как я могу предотвратить эту ситуацию и самостоятельно обработать ее, чтобы она не возвращала ошибку?

РЕДАКТИРОВАТЬ: Благодаря вам работает обработчик тайм-аута @Deiv.

Я все еще хочу найти способ доступа к лямбда-отчетупотому что я заметил, что у меня может быть недостаточно памяти, даже если выполнение длится менее 7000 мс, и было бы неплохо справиться с этой ситуацией.

1 Ответ

0 голосов
/ 27 марта 2019

Вы можете установить внутреннее время ожидания и обратный вызов с сообщением об успешном завершении до того, как сама функция отключится. Это может выглядеть так:

const self = this;
setTimeout(function(){
  //after 6 seconds, exit function
  self.emit(':tell', "The connection is too slow, nothing was found.");
  process.exit();
}, 6000);
//rest of your code

Поскольку setTimeout () выполняется асинхронно, он не будет блокировать остальную часть вашего кода, но вернется через 6 секунд с успешным сообщением, что позволит избежать ошибки тайм-аута.

РЕДАКТИРОВАТЬ ОП: Я добавил const self = this;, потому что внутри области функций это ничего не значит. Благодаря тебе @Deiv теперь это работает.

Я все еще хочу найти способ доступа к лямбда-отчету, потому что заметил, что у меня может быть недостаточно памяти, даже если выполнение длится менее 7000 мс, и было бы неплохо справиться с этой ситуацией.

...