Вызов DynamoDB с помощью запроса Alexa

Я пытаюсь научиться использовать Alexa для чтения данных из моей таблицы DynamoDB, используя функцию сканирования или запроса (или и то, и другое).

Столбцы в моей таблице - это дата, время и название фильма.

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

Вот мой текущий код

console.log('Loading function');

var AWSregion = 'us-east-1';  // us-east-1
var AWS = require('aws-sdk');
var dclient = new AWS.DynamoDB.DocumentClient();

var getItems = (event, context, callback)=>{
    
    dclient.get(event.params,(error,data)=>{
        if(error){
            callback(null,"error occurerd");
        }
        else{
            callback(null,data);
        }
    });
};

exports.handler = getItems;

exports.handler = (event, context, callback) => {
    try {

        var request = event.request;

        if (request.type === "LaunchRequest") {
            context.succeed(buildResponse({
                speechText: "Welcome to H.S.S.M.I skill, what would you like to find",
                repromptText: "I repeat, Welcome to my skill, what would you like to find",
                endSession: false
            }));
        }
        else if (request.type === "IntentRequest") {
            let options = {};         


            if (request.intent.name === "cinema") {
            } else if (request.intent.name === "AMAZON.StopIntent" || request.intent.name === "AMAZON.CancelIntent") {
                options.speechText = "ok, good bye.";
                options.endSession = true;
                context.succeed(buildResponse(options));
            }
             else if (request.intent.name === "AMAZON.HelpIntent") {
                options.speechText = "My skill will read your table depending on what is asked. For example, you can ask what about a specific date. Please refer to skill description for all possible utterences.";
                options.repromptText = "What is the data sign you want to know  about today? If you want to exit from my  skill please say stop or cancel.";
                options.endSession = false;
                context.succeed(buildResponse(options));
            }
            else {
                context.fail("Unknown Intent");
            }
        }

        else if (request.type === "SessionEndedRequest") {
            options.endSession = true;
            context.succeed();
        }
        else {
            context.fail("Unknown Intent type");
        }




    } catch (e) {

    }


};

function buildResponse(options) {
    var response = {
        version: "1.0",
        response: {
            outputSpeech: {
                "type": "SSML",
                "ssml": `<speak><prosody rate="slow">${options.speechText}</prosody></speak>`
            },

            shouldEndSession: options.endSession
        }
    };

    if (options.repromptText) {
        response.response.reprompt = {
            outputSpeech: {
                "type": "SSML",
                "ssml": `<speak><prosody rate="slow">${options.repromptText}</prosody></speak>`
            }
        };
    }

    return response;
}

function readDynamoItem(params, callback) {
    
    var AWS = require('aws-sdk');
    AWS.config.update({region: AWSregion});
    var dynamodb = new AWS.DynamoDB();
    console.log('reading item from DynamoDB table');

    dynamodb.scan(params, function(err, data) {
        if (err) console.log(err, err.stack); // an error occurred
        else{
            console.log(data); // successful response
            callback(JSON.stringify(data));
        }
    });
    var docClient = new AWS.DynamoDB.DocumentClient();
    //Get item by key
    docClient.get(params, (err, data) => {
        if (err) {
            console.error("Unable to read item. Error JSON:", JSON.stringify(err, null, 2));
        } else {
            console.log("GetItem succeeded:", JSON.stringify(data, null, 2));

            callback(data.Item.message);  // this particular row has an attribute called message

        }
    });

}

///////////////////////////////////////////////////////////////////////////////

А вот и мой DBHandler

var AWS = require('aws-sdk');
AWS.config.update({
    region: "'us-east-1'"
});

let docClient = new AWS.DynamoDB.DocumentClient();

var table = "Cinema";

let getItems = (Id,callback) => {   
  
    var params = {
        TableName: "cinema",
        Key: {
            "date": "2018-01-04",
            "filmname": "rugrats"
        }
    };

    docClient.get(params, function (err, data) {
        callback(err, data);
    });

};
module.exports = {
    getItems
};

Я могу запустить приложение, и у меня есть функция Lambda, которая работает сама по себе, когда я настраиваю тестовое событие для поиска фильма с определенной даты, но я не могу заставить его работать с Alexa.

Может ли кто-нибудь помочь мне или указать, где я ошибаюсь

ОБНОВИТЬ*************

Вот как настраивается моя схема намерений

{
  "intents": [
    {
      "slots": [
        {
          "name": "sincedate",
          "type": "AMAZON.DATE"
        }
      ],
      "intent": "date"
    },
    {
      "intent": "AMAZON.CancelIntent"
    },
    {
      "intent": "AMAZON.HelpIntent"
    },
    {
      "intent": "AMAZON.StopIntent"
    },
    {
      "slots": [
        {
          "name": "sincedate",
          "type": "AMAZON.DATE"
        }
      ],
      "intent": "cinema"
    },
    {
      "intent": "MyIntent"
    }
  ]
}


person zuba    schedule 01.03.2018    source источник


Ответы (1)


Часть 1 - Разрешения

Одна из возможных причин, по которой вы не можете читать из DynamoDB в своем навыке, - это разрешения.

Вам следует дважды проверить роль IAM, которую вы назначили лямбда-выражению вашего навыка, чтобы убедиться, что у нее есть разрешения на чтение из DynamoDB.

Некоторые ссылки:

Часть 2 - Фактический обработчик навыков Лямбда

Я перечитал ваш вопрос и меня смущает та часть, в которой вы говорите о настройке второй лямбды для чтения данных из Dynamo. У вас не должно быть двух функций Lambda - только одна, которая будет обрабатывать запросы от Alexa, и в этой функции вы должны вернуть свой ответ Alexa после вызова Dynamo.

Теперь к конкретике. В вашем первом фрагменте кода у вас есть:

exports.handler = getItems;

exports.handler = (event, context, callback) => {
    // here you have your handler to handle alexa responses 
}

Сразу бросается в глаза то, что вы сначала устанавливаете обработчик на getItems, а затем сбрасываете обратно на обработчик, который должен отвечать на Alexa.

Другая вещь, о которой я предполагаю, это то, что иногда навык работает, например, когда вы впервые запускаете его, и, вероятно, если вы говорите Help, но в других случаях это не так, например, когда вы отправляете ему намерение «кино».

Это связано с тем, что точкой входа от запроса Alexa к вашему навыку является exports.handler, который в основном определяется как функция с тремя параметрами (это похоже на void main (int argc, char * argv []) программы c).

Первый параметр - event - это вход в ваш навык. Alexa предоставит здесь информацию, такую ​​как тип запроса, если это намерение, имя намерения, информацию о сеансе и т. Д.

Второй и третий параметры - context и callback - это то, что вы используете для возврата управления из вашей лямбда-функции, в зависимости от времени выполнения узла. Для Note v4 и новее вы используете обратный вызов, для более старых версий вы используете context.

Вы можете использовать что-то вроде этого, чтобы отправить ответ об успешном завершении:

if(typeof callback === 'undefined') {
     context.succeed("successful response message");
} else {
     callback(null, "successful response message");
}

И что-то вроде этого, чтобы отправить ответ об ошибке

if(typeof callback === 'undefined') {
     context.fail("failure response message");
} else {
     callback("failure response message", null);
}

Собирая все вместе, вот базовый обработчик Lambda, который всегда реагирует на вызовы ваших навыков:

function sendResponse(context, callback, responseOptions) {
  if(typeof callback === 'undefined') {
    context.succeed(buildResponse(responseOptions));
  } else {
    callback(null, buildResponse(responseOptions));
  }
}

function buildResponse(options) {
  var alexaResponse = {
    version: "1.0",
    response: {
      outputSpeech: {
        "type": "SSML",
        "ssml": `<speak><prosody rate="slow">${options.output}</prosody></speak>`
      },
      shouldEndSession: options.endSession
    }
  };
  if (options.repromptText) {
    alexaResponse.response.reprompt = {
      outputSpeech: {
        "type": "SSML",
        "ssml": `<speak><prosody rate="slow">${options.reprompt}</prosody></speak>`
      }
    };
  }
  return alexaResponse;
}

exports.handler = (event, context, callback) => {
  try {
    var request = event.request;
    if (request.type === "LaunchRequest") {
      sendResponse(context, callback, {
        output: "welcome to my skill. what do you want to find?",
        endSession: false
      });
    }
    else if (request.type === "IntentRequest") {
      let options = {};         
      if (request.intent.name === "cinema") {
        // this is where we will wire up the dynamo call
        // for now, just send a simple response and end the session
        sendResponse(context, callback, {
          output: "cinema not implemented yet!",
          endSession: true
        });
      } else if (request.intent.name === "AMAZON.StopIntent" || request.intent.name === "AMAZON.CancelIntent") {
        sendResponse(context, callback, {
          output: "ok. good bye!",
          endSession: true
        });
      }
      else if (request.intent.name === "AMAZON.HelpIntent") {
        sendResponse(context, callback, {
          output: "you can ask me about films",
          reprompt: "what can I help you with?"
          endSession: false
        });
      }
      else {
        sendResponse(context, callback, {
          output: "I don't know that one! Good bye!",
          endSession: true
        });
      }
    }
    else if (request.type === "SessionEndedRequest") {
      sendResponse(context, callback, ""); // no response needed
    }
    else {
      // un unexpected request type received.. just say I don't know..
      sendResponse(context, callback, {
          output: "I don't know that one! Good bye!",
          endSession: true
      });
    }
  } catch (e) {
    // handle the error by logging it and sending back an failure
    console.log('Unexpected error occurred in the skill handler!', e);
    if(typeof callback === 'undefined') {
       context.fail("Unexpected error");
    } else {
       callback("Unexpected error");
    }
  }
};

На этом этапе навык должен быть функциональным и уметь обрабатывать все ваши запросы. Предполагая, что вы правильно настроили свою модель взаимодействия и что cinema намерение передается вашему навыку, то вот как вы можете использовать динамо-клиент для ответа данными из таблицы.

var AWSregion = 'us-east-1';  // us-east-1
var AWS = require('aws-sdk');
var dbClient = new AWS.DynamoDB.DocumentClient();

let handleCinemaIntent = (context, callback) => {    
  let params = {
    TableName: "cinema",
    Key: {
        "date": "2018-01-04",
        "filmname": "rugrats"
    }
  };
  dbClient.get(params, function (err, data) {
    if (err) {
       // failed to read from table for some reason..
       console.log('failed to load data item:\n' + JSON.stringify(err, null, 2));
       // let skill tell the user that it couldn't find the data 
       sendResponse(context, callback, {
          output: "the data could not be loaded from Dynamo",
          endSession: true
       });
    } else {
       console.log('loaded data item:\n' + JSON.stringify(data.Item, null, 2))
       // assuming the item has an attribute called "message"..
       sendResponse(context, callback, {
          output: data.Item.message,
          endSession: true
       });
    }
  });
};


function sendResponse(context, callback, responseOptions) {
  if(typeof callback === 'undefined') {
    context.succeed(buildResponse(responseOptions));
  } else {
    callback(null, buildResponse(responseOptions));
  }
}

function buildResponse(options) {
  var alexaResponse = {
    version: "1.0",
    response: {
      outputSpeech: {
        "type": "SSML",
        "ssml": `<speak><prosody rate="slow">${options.output}</prosody></speak>`
      },
      shouldEndSession: options.endSession
    }
  };
  if (options.repromptText) {
    alexaResponse.response.reprompt = {
      outputSpeech: {
        "type": "SSML",
        "ssml": `<speak><prosody rate="slow">${options.reprompt}</prosody></speak>`
      }
    };
  }
  return alexaResponse;
}

exports.handler = (event, context, callback) => {
  try {
    var request = event.request;
    if (request.type === "LaunchRequest") {
      sendResponse(context, callback, {
        output: "welcome to my skill. what do you want to find?",
        endSession: false
      });
    }
    else if (request.type === "IntentRequest") {
      let options = {};         
      if (request.intent.name === "cinema") {
        handleCinemaIntent(context, callback);
      } else if (request.intent.name === "AMAZON.StopIntent" || request.intent.name === "AMAZON.CancelIntent") {
        sendResponse(context, callback, {
          output: "ok. good bye!",
          endSession: true
        });
      }
      else if (request.intent.name === "AMAZON.HelpIntent") {
        sendResponse(context, callback, {
          output: "you can ask me about films",
          reprompt: "what can I help you with?"
          endSession: false
        });
      }
      else {
        sendResponse(context, callback, {
          output: "I don't know that one! Good bye!",
          endSession: true
        });
      }
    }
    else if (request.type === "SessionEndedRequest") {
      sendResponse(context, callback, ""); // no response needed
    }
    else {
      // un unexpected request type received.. just say I don't know..
      sendResponse(context, callback, {
          output: "I don't know that one! Good bye!",
          endSession: true
      });
    }
  } catch (e) {
    // handle the error by logging it and sending back an failure
    console.log('Unexpected error occurred in the skill handler!', e);
    if(typeof callback === 'undefined') {
       context.fail("Unexpected error");
    } else {
       callback("Unexpected error");
    }
  }
};
person Mike Dinescu    schedule 02.03.2018
comment
Привет, я считаю, что с моими разрешениями все в порядке, я думаю, что мой код неправильный, но я не знаю, где я ошибаюсь - person zuba; 02.03.2018
comment
если это помогает, текущий ответ, который я получаю от своего навыка, - это «ответ недействителен» - person zuba; 02.03.2018
comment
хорошо - я обновил ответ - надеюсь, он должен прояснить ситуацию - person Mike Dinescu; 03.03.2018
comment
спасибо за ваш ответ, и спасибо, что нашли время, чтобы объяснить вещи, помогли мне многому научиться. Ваш код помог, но в настоящее время я получаю в качестве ответа «данные не могут быть загружены из Dynamo». Могу ли я это исправить? - person zuba; 05.03.2018
comment
Рад, что ответ помог (возможно, рассмотрите возможность проголосовать за;) и да, если вы получаете сообщение об ошибке при загрузке данных, это означает, что обратный вызов от handleCinemaIntent возвращает объект с ошибкой, поэтому вам нужно посмотреть журнал лямбда-выражения в CloudWatch, чтобы точно узнать, что именно ошибка есть. Либо прокомментируйте здесь, либо добавьте еще вопрос - person Mike Dinescu; 05.03.2018
comment
спасибо, уже сделали, но «Голоса, отданные теми, у кого репутация менее 15, записываются, но не меняют общедоступный рейтинг публикации», и я проверю это сейчас и прокомментирую здесь спасибо за вашу помощь - person zuba; 05.03.2018
comment
Кажется, я не могу найти ошибку журнала в облачных часах. Может быть, это как-то связано с моей моделью взаимодействия? - person zuba; 05.03.2018
comment
не обращайте внимания на его обновление сейчас, это то, что вы хотели. Это то, что вы хотели? Я опубликую это в следующем комментарии - person zuba; 05.03.2018
comment
Сведения о состоянии: текущее состояние аварийного сигнала: OK, ALARM или INSUFFICIENT_DATA. Включает информацию о том, когда он перешел в это состояние и почему. Состояние было изменено на НЕДОСТАТОЧНО ДАННЫХ на 2018/03/05. Причина: недостаточно данных: 5 точек данных были неизвестны. Описание: описание, предоставленное при создании или изменении сигнала тревоги. Пороговое значение: условие, при котором аварийный сигнал переходит в состояние ТРЕВОГА. ConsumedReadCapacityUnits ›= 240 для 5 точек данных в течение 5 минут. Действия: Действия, которые будут выполняться при изменении состояния аварийного сигнала. В СИГНАЛЕ: - person zuba; 05.03.2018
comment
Отправить сообщение в тему Dynamodb Отправить сообщение в тему: Dynamodb (Предупреждение: этот сигнал тревоги не настроен на уведомление. Измените этот сигнал и добавьте адрес электронной почты.) - person zuba; 05.03.2018
comment
нет, вы должны иметь возможность перейти на консоль Lambda для своего навыка и открыть вкладку «Мониторинг», затем щелкнуть «Просмотр в CloudWatch», что приведет вас на страницу журналов облачного мониторинга, на которой отображаются сообщения журнала консоли для вашего навыка; там будет один, который регистрируется с помощью handleCinemaIntent, который показывает, в чем проблема с получением элемента из динамо-машины - person Mike Dinescu; 05.03.2018
comment
Привет, это то, что я должен искать? 2018-03-05T11: 04: 51.322Z 1a3ec8c3-0c75-4921-9f73-6484c3e4bab1 В обработчике навыков возникла непредвиденная ошибка! TypeError: невозможно прочитать свойство 'type' undefined в файле exports.handler.e (/var/task/index.js:67:16) - person zuba; 06.03.2018
comment
да, что-то в этом роде, хотя я подозреваю, что ошибка была связана с другим вызовом - возможно, вы тестировали лямбда-вызов? это не похоже на реальный вызов навыка - ошибка сообщает вам, что объект события на самом деле не содержал параметр запроса, хотя все запросы навыков alexa содержат, поэтому лямбда-вызов не должен происходить из фактического запроса навыка - person Mike Dinescu; 06.03.2018
comment
вам следует поискать в журналах тот, в котором написано что-то вроде: не удалось загрузить элемент данных, а затем посмотреть, в чем заключается ошибка; скорее всего, дело в структуре этого элемента данных .. но я не могу сказать без сообщения об ошибке - person Mike Dinescu; 06.03.2018
comment
Привет, это все, что я могу найти на данный момент. Я попробую сегодня еще раз. Я только что щелкнул раздел Dynamodb под своей лямбда-функцией, и там написано: «Последний результат обработки: ПРОБЛЕМА: не удалось вызвать функцию». - person zuba; 07.03.2018
comment
Привет, Майк, я пошел дальше и добавил свою схему намерений, если это может быть причиной проблемы. - person zuba; 07.03.2018
comment
Я также только что нашел это 2018-03-07T12: 17: 33.188Z 837c1935-2201-11e8-954c-9f5848878a0b не удалось загрузить элемент данных: {сообщение: предоставленный ключевой элемент не соответствует схеме, код: ValidationException, время : 2018-03-07T12: 17: 33.188Z, requestId: GOEV2364PM9BASV2PG0A3NUN4JVV4KQNSO5AEMVJF66Q9ASUAAJG, statusCode: 400, повторная попытка: false, retryDelay: 2.3846124370334842 - person zuba; 07.03.2018
comment
Это именно тот журнал, который вам нужен! В сообщении об ошибке объясняется, что параметры, переданные команде get клиента DynamoDb, неверны - в частности, очевидно, что схема ключей неверна, то есть она не соответствует схеме ключей, которую вы использовали при создании таблицы; если вы посмотрите на ключ, который передается в get, он отправляет «дату» и «название фильма», но, видимо, это неверно. Вам следует взглянуть на определение таблицы в консоли DynamoDB, а затем удвоить эти параметры, чтобы получить в функции handleCinemaIntent. - person Mike Dinescu; 08.03.2018
comment
Обязательно проверьте написание «дата» и «название фильма» в схеме таблицы и убедитесь, что они оба строковые. Убедитесь, что в схеме есть ключ раздела и ключ диапазона. - person Mike Dinescu; 08.03.2018
comment
спасибо, я прочитал онлайн и прочитал, что я должен только определить свой ключ раздела. Я сделал это, и он работает правильно для одного из моих элементов в моей таблице, но все остальное, что я ищу, дает мне тот же ответ, что и один элемент, который работает. Есть идеи, как это преодолеть? - person zuba; 08.03.2018
comment
Да, но мне кажется, что теперь это совсем другой вопрос / проблема, чем то, с чего вы изначально начали. Я рекомендую вам задать новый вопрос, относящийся к динамо-машине. Таким образом, ответ на него принесет пользу всему сообществу (плюс ответ требует большего, чем то, что я могу уместить в комментарии). После того, как вы разместите вопрос, не стесняйтесь добавлять к нему комментарий и упоминать меня, и я тоже постараюсь ответить на него. - person Mike Dinescu; 08.03.2018
comment
спасибо, ты прав, поэтому я принял твой ответ! Я пошел дальше и добавил еще один пост. - person zuba; 08.03.2018