Use DynamoDB with Alexa Lambda function - node.js

Well, I am having an issue that I've been dealing with for the last 2 days and still seem to have no progress on.
Basically, I am trying to develop a skill for Amazon's echo dot, but my particular skill requires the use of persistent data. I took to the docs and found information on account linking and DynamoDB, account linking seemed to complex for a simple research project so I took to DynamoDB.
I used a lambda function, and it ran fine until I put the DynamoDB table line:
alexa.dynamoDBTableName = 'rememberThisDB';
That line completely stops my skill from working and returns the following message:
The remote endpoint could not be called, or the response it returned was invalid.
I honestly have no idea how to deal with it; I am completely new to the whole AWS concept so I don't even know how to get the actual error message that the Lambda function is returning.
I changed the role and gave it the following configuration:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:*"
},
{
"Effect": "Allow",
"Action": [
"dynamodb:DeleteItem",
"dynamodb:GetItem",
"dynamodb:PutItem",
"dynamodb:Scan",
"dynamodb:UpdateItem"
],
"Resource": "*Yes, I did put the correct ARN*"
}
]
}
But that didn't really change anything, it still just returned the same error.
The issue is, I'm not doing anything at all with DynamoDB, I am simply defining the dynamoDBTableName property of the alexa object, that's it.
Yes, the DynamoDB table exists.
I feel like my head is about to blow up, so any help would be greatly appreciated.
UPDATE: Found out how to see logs, here is the latest log: Error fetching user state: ValidationException: The provided key element does not match the schema, not sure why it would give that error since I never queried anything, the only thing I did was declare the table name.

Just to document the resolution of the question in the comments and so that this question doesn't remain "unanswered" on SO:
Assuming you're using the alexa-skills-kit-sdk-for-nodejs, your table should have a single userId string HASH key.
var newTableParams = {
AttributeDefinitions: [
{
AttributeName: 'userId',
AttributeType: 'S'
}
],
KeySchema: [
{
AttributeName: 'userId',
KeyType: 'HASH'
}
],
ProvisionedThroughput: {
ReadCapacityUnits: 5,
WriteCapacityUnits: 5
}
}
It turned out the user did not have the appropriate schema setup for their DynamoDB table.

Related

No ParameterStore access with sub path definition

I have the following policy in place which works fine on any parameter within /network/testnet/*
{
"Statement": [
{
"Action": [
"ssm:DescribeParameters"
],
"Resource": "*",
"Effect": "Allow"
},
{
"Action": [
"ssm:GetParameters",
"ssm:GetParameter",
"ssm:GetParametersByPath"
],
"Resource": "arn:aws:ssm:eu-central-1:xx:parameter/network/*",
"Effect": "Allow"
}
]
}
but as soon as I want to restrict the resource to arn:aws:ssm:eu-central-1:xx:parameter/network/testnet/* it does no longer allow access to any of the parameters within /network/testnet/*.
My lambda function then gets the following error
"errorType": "Runtime.UnhandledPromiseRejection",
"errorMessage": "AccessDeniedException: User: arn:aws:sts::xxx:assumed-role/dsome-app/some-function is not authorized to perform: ssm:GetParametersByPath on resource: arn:aws:ssm:eu-central-1:xxx:parameter/network/testnet because no identity-based policy allows the ssm:GetParametersByPath action",
Specifically I use CloudFormation and define access like that
Policies:
- SSMParameterReadPolicy:
ParameterName: "network/testnet/*"
Are restriction to sub paths not allowed or what am I missing?
You should be able to do dynamoose.model('example_user', schema, {"create": false}) to get away from the need to create a table https://dynamoosejs.com/guide/Model/

Unable to get permissions to work properly

I'm messing around with permissions and am unable to figure out what's going on. I created a custom chat type, gallery, with permissions that look like this:
[
{
"action": "Deny",
"name": "No access",
"resources": [
"*"
],
"roles": [
"*"
],
"owner": false,
"priority": 999
}
]
So basically, nobody can do anything.
Now, to test this, I create a gallery channel with a user:
const client = new StreamChat(<STREAM_KEY>);
const token = <TOKEN>
chatClient.setUser(
{
id: 'user1',
name: 'User 1',
},
token,
);
const channel = client.channel('gallery', 'example', {
name: 'Example',
});
Using the React UI kit, user1 (or any user) should NOT be able to view the channel given the permissions, right? And yet the channel loads and I can type messages normally. What am I doing wrong? I don't think being an owner or not matters, as I've tested this with two different users.
Thanks #ferhatelmas for the heads up. Though my app was in production mode, I noticed a toggle that was ON that disabled permissions. I toggled it off and I think everything seems to be OK.

Time Series Insights - 'uniqueValues' aggregate not working as expected: does not return any data

I'm trying to execute some aggregate queries against data in TSI. For example:
{
"searchSpan": {
"from": "2018-08-25T00:00:00Z",
"to": "2019-01-01T00:00:00Z"
},
"top": {
"sort": [
{
"input": {
"builtInProperty": "$ts"
}
}
]
},
"aggregates": [
{
"dimension": {
"uniqueValues": {
"input": {
"builtInProperty": "$esn"
},
"take": 100
}
},
"measures": [
{
"count": {}
}
]
}
]
}
The above query, however, does not return any record, although there are many events stored in TSI for that specific searchSpan. Here is the response:
{
"warnings": [],
"events": []
}
The query is based on the examples in the documentation which can be found here and which is actually lacking crucial information for requirements and even some examples do not work...
Any help would be appreciated. Thanks!
#Vladislav,
I'm sorry to hear you're having issues. In reviewing your API call, I see two fixes that should help remedy this issue:
1) It looks like you're using our /events API with payload for /aggregates API. Notice the "events" in the response. Additionally, “top” will be redundant for /aggregates API as we don't support top-level limit clause for our /aggregates API.
2) We do not enforce "count" property to be present in limit clause (“take”, “top” or “sample”) and it looks like you did not specify it, so by default, the value was set to 0, that’s why the call is returning 0 events.
I would recommend that you use /aggregates API rather than /events, and that “count” is specified in the limit clause to ensure you get some data back.
Additionally, I'll note your feedback on documentation. We are ramping up a new hire on documentation now, so we hope to improve the quality soon.
I hope this helps!
Andrew

How to pass API Gateway authorizer context to a HTTP integration

I have successfully implemented a Lambda authorizer for my AWS API Gateway, but I want to pass a few custom properties from it to my Node.js endpoint.
My output from my authorizer follows the format specified by AWS, as seen below.
{
"principalId": "yyyyyyyy",
"policyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": "execute-api:Invoke",
"Effect": "Allow|Deny",
"Resource": "arn:aws:execute-api:<regionId>:<accountId>:<appId>/<stage>/<httpVerb>/[<resource>/<httpVerb>/[...]]"
}
]
},
"context": {
"company_id": "123",
...
}
}
In my case, context contains a few parameters, like company_id, that I would like to pass along to my Node endpoint.
If I was to use a Lambda endpoint, I understand that this is done with Mapping Template and something like this:
{
"company_id": "$context.authorizer.company_id"
}
However, Body Mapping Template is only available under Integration Request if Lambda is selected as Integration type. Not if HTTP is selected.
In short, how do I pass company_id from my Lambda authorizer to my Node API?
Most of the credit goes out to #Michael-sqlbot in the comments to my question, but I'll put the complete answer here if someone else finds this question.
Authorizer Lambda
It has to return an object in this format, where context contains the parameters you want to forward to your endpoint, as specified in the question.
{
"principalId": "yyyyyyyy",
"policyDocument": {
"Version": "2012-10-17",
"Statement": [{
"Action": "execute-api:Invoke",
"Effect": "Allow|Deny",
"Resource": "arn:aws:execute-api:<regionId>:<accountId>:<appId>/<stage>/<httpVerb>/[<resource>/<httpVerb>/[...]]"
}]
},
"context": {
"company_id": "123", <-- The part you want to forward
...
}
}
Method Request
Under Method Request / HTTP Request Headers, add the context property you want to forward:
Name: company_id
Required: optional
Cashing: optional
Integration Request
And under Integration Request / HTTP Headers, add:
Name: company_id
Mapped from: context.authorizer.company_id
Cashing: optional
If you're using lamda-proxy, you can access the context from your event.requestContext.authorizer.
So your company_id can be accessed using event.requestContext.authorizer.company_id.
If you're using lamda-proxy (at least with Golang backend) you can access to that values stored on authorizer context without mapping template usage!
Remember re-launch API and wait a minutes!
It's working for me.

KeyConditionExpression error and empty error response

Im using Lambda as my backend and im performing all the DynamoDB operations from it.
I have a user table Users and i want to query it via its hash-key Username
Using the KeyConditionExpression statement on my params variable but i get the following error:
There were 2 validation errors:\n* MissingRequiredParameter: Missing >required key 'KeyConditions' in params\n* UnexpectedParameter: Unexpected >key 'KeyConditionExpression' found in params
So yeah, i tried the following legacy statement:
var userQuery = {
TableName:"Users",
KeyConditions:{
Username:{
ComparisonOperator:'EQ',
AttributeValueList:[{S:"some_username"}]
}
}
};
For some reason, i get empty errors on the query callback, which looks like this:
dynamo.query(userQuery,function(err,data){
if(err) console.log("error "+JSON.stringify(err,null,2));
else console.log("pass "+JSON.stringify(data,null,2));
});
I've tried literally everything, gotten to the point of desperation...
I cant seem to query any table, but i can scan and use putItem with no problem. My policy has the query parameter as well.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "my_Stmt_num",
"Action": [
"dynamodb:DeleteItem",
"dynamodb:GetItem",
"dynamodb:PutItem",
"dynamodb:Query",
"dynamodb:Scan",
"dynamodb:UpdateItem"
],
"Effect": "Allow",
"Resource": "*"
},
{
"Sid": "",
"Resource": "*",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Effect": "Allow"
}
]
}
In case its relevant, at the top of my handler js file im getting a reference to dynamo like this:
var doc = require('dynamodb-doc');
var dynamo = new doc.DynamoDB();
My whole application is 'new' meaning nothing prior than February 2015 exists, so i dont see any point in using legacy apis, as the docs say.
It sounds like your AWS SDK associated with the document client may be out of date and does not support the new KeyConditionExpression feature. Could you please try re-installing your AWS SDK and the document SDK? Please also attach the versions you are installing if you continue to have issues after re-installing.
Previous DynamoDB Document SDK was deprecated, new client from standard Javascript SDK should be used from now on:
http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/DynamoDB/DocumentClient.html

Resources