When the user is registered with the method Auth.signup and this one confirmed the code received by email. I want to execute the post-confirmation trigger and update a User table made through the method #model on the schema.graphql file.
I updated the Auth like this:
andres#DESKTOP-CPTOQVN:~/TeVi$ amplify update auth
Please note that certain attributes may not be overwritten if you choose to use defaults settings.
You have configured resources that might depend on this Cognito resource. Updating this Cognito resource could have unintended side effects.
Using service: Cognito, provided by: awscloudformation
What do you want to do? Walkthrough all the auth configurations
Select the authentication/authorization services that you want to use: User Sign-Up, Sign-In, connected with AWS IAM controls (Enables per-user Storage features for images or other content, Analytics, and more)
Allow unauthenticated logins? (Provides scoped down permissions that you can control via AWS IAM) Yes
Do you want to enable 3rd party authentication providers in your identity pool? No
Do you want to add User Pool Groups? No
Do you want to add an admin queries API? No
Multifactor authentication (MFA) user login options: OFF
Email based user registration/forgot password: Enabled (Requires per-user email entry at registration)
Please specify an email verification subject: Your verification code
Please specify an email verification message: Your verification code is {####}
Do you want to override the default password policy for this User Pool? No
Specify the app's refresh token expiration period (in days): 30
Do you want to specify the user attributes this app can read and write? No
Do you want to enable any of the following capabilities?
Do you want to use an OAuth flow? No
? Do you want to configure Lambda Triggers for Cognito? Yes
? Which triggers do you want to enable for Cognito Post Confirmation
? What functionality do you want to use for Post Confirmation Create your own module
Succesfully added the Lambda function locally
? Do you want to edit your custom function now? No
Successfully updated resource tevi locally
Some next steps:
"amplify push" will build all your local backend resources and provision it in the cloud
"amplify publish" will build all your local backend and frontend resources (if you have hosting category added) and provision it in the cloud
And then I did amplify push. Then when the function was completed, I updated this one like this:
andres#DESKTOP-CPTOQVN:~/TeVi$ amplify update function
Using service: Lambda, provided by: awscloudformation
? Please select the Lambda Function you would want to update teviPostConfirmation
? Do you want to update permissions granted to this Lambda function to perform on other resources in your project? Yes
? Select the category storage
? Storage has 12 resources in this project. Select the one you would like your Lambda to access User:#model(appsync)
? Select the operations you want to permit for User:#model(appsync) create, update
You can access the following resource attributes as environment variables from your Lambda function
API_TEVI_GRAPHQLAPIIDOUTPUT
API_TEVI_USERTABLE_ARN
API_TEVI_USERTABLE_NAME
ENV
REGION
? Do you want to invoke this function on a recurring schedule? No
? Do you want to edit the local lambda function now? No
Successfully updated resource
Then I did amplify push and I got this error:
andres#DESKTOP-CPTOQVN:~/TeVi$ amplify push
✔ Successfully pulled backend environment dev from the cloud.
Current Environment: dev
| Category | Resource name | Operation | Provider plugin |
| -------- | -------------------- | --------- | ----------------- |
| Function | teviPostConfirmation | Update | awscloudformation |
| Auth | tevi | No Change | awscloudformation |
| Api | tevi | No Change | awscloudformation |
| Storage | s3c1026a67 | No Change | awscloudformation |
? Are you sure you want to continue? Yes
⠼ Updating resources in the cloud. This may take a few minutes...Error updating cloudformation stack
✖ An error occurred when pushing the resources to the cloud
Circular dependency between resources: [functionteviPostConfirmation, authtevi, UpdateRolesWithIDPFunctionOutputs, apitevi, UpdateRolesWithIDPFunction]
An error occured during the push operation: Circular dependency between resources: [functionteviPostConfirmation, authtevi, UpdateRolesWithIDPFunctionOutputs, apitevi, UpdateRolesWithIDPFunction]
This is the backend-config.json I have right now:
{
"auth": {
"tevi": {
"service": "Cognito",
"providerPlugin": "awscloudformation",
"dependsOn": [
{
"category": "function",
"resourceName": "teviPostConfirmation",
"triggerProvider": "Cognito",
"attributes": [
"Arn",
"Name"
]
}
]
}
},
"api": {
"tevi": {
"service": "AppSync",
"providerPlugin": "awscloudformation",
"output": {
"authConfig": {
"additionalAuthenticationProviders": [
{
"authenticationType": "AWS_IAM"
}
],
"defaultAuthentication": {
"authenticationType": "AMAZON_COGNITO_USER_POOLS",
"userPoolConfig": {
"userPoolId": "authtevi"
}
}
}
}
}
},
"storage": {
"s3c1026a67": {
"service": "S3",
"providerPlugin": "awscloudformation"
}
},
"function": {
"teviPostConfirmation": {
"build": true,
"providerPlugin": "awscloudformation",
"service": "Lambda",
"dependsOn": [
{
"category": "api",
"resourceName": "tevi",
"attributes": [
"GraphQLAPIIdOutput"
]
}
]
}
}
}
Amplify CLI Version
4.21.3
Expected behavior
Work with the post-confirmation function and create or update content on the User table with this one.
How can I fix it :/?
As it has been discussed in the related GitHub ticket, we can solve this problem by invoking a function from another.
First, we assume that you used amplify update auth and added Cognito Post Confirmation function to add the user to a group.
Also it seems you followed the this tutorial to make a function for adding user to a custom model after confirmation event. However, you cannot follow the last step of this tutorial as only one function can be bind to the Post confirmation trigger. So let us bind our first function to Post confirmation trigger and keep this new function unbind. I assume that you named this recent function as addusertotable.
If you have added a function with amplify update auth, you will have a file which is called add-to-group.js, modify the function as follow:
/* eslint-disable-line */ const aws = require('aws-sdk');
// My defined function starts here
var addUserToTable = function (event, context) {
// Call function to add user to table Users
var lambda = new aws.Lambda({
region: process.env.AWS_REGION
});
var params = {
FunctionName: 'addusertotable-' + process.env.ENV,
InvocationType: 'RequestResponse',
Payload: JSON.stringify(event, context)
};
lambda.invoke(params, function (err, data) {
if (err) {
console.error(err);
} else {
console.log(params.FunctionName + ': ' + data.Payload);
}
});
};
// My defined function ends here
exports.handler = async (event, context, callback) => {
// Call function to add user to table Users
addUserToTable(event, context); // this is also my code to call my defined function
// Rest are original code from amplify template
// Add group to the user
const cognitoidentityserviceprovider = new aws.CognitoIdentityServiceProvider({ apiVersion: '2016-04-18' });
const groupParams = {
GroupName: process.env.GROUP,
UserPoolId: event.userPoolId,
};
const addUserParams = {
GroupName: process.env.GROUP,
UserPoolId: event.userPoolId,
Username: event.userName,
};
try {
await cognitoidentityserviceprovider.getGroup(groupParams).promise();
} catch (e) {
await cognitoidentityserviceprovider.createGroup(groupParams).promise();
}
try {
await cognitoidentityserviceprovider.adminAddUserToGroup(addUserParams).promise();
callback(null, event);
} catch (e) {
callback(e);
}
};
As it has been discussed here, we need to add a segment to the lambda policy that allows us to invoke the secondary lambda function (i.e. addusertotable).
Then find the xyzPostConfirmation-cloudformation-template.json file, and then change its PolicyDocument entry like this:
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": {
"Fn::Sub": [
"arn:aws:logs:${region}:${account}:log-group:/aws/lambda/${lambda}:log-stream:*",
{
"region": {
"Ref": "AWS::Region"
},
"account": {
"Ref": "AWS::AccountId"
},
"lambda": {
"Ref": "LambdaFunction"
}
}
]
}
},
{
"Effect": "Allow",
"Action": "lambda:InvokeFunction",
"Resource": {
"Fn::Sub": [
"arn:aws:lambda:${region}:${account}:function:addusertotable-${env}",
{
"region": {
"Ref": "AWS::Region"
},
"account": {
"Ref": "AWS::AccountId"
},
"lambda": {
"Ref": "LambdaFunction"
},
"env": {
"Ref": "env"
}
}
]
}
Actually what I have added is the second object inside the Statement array.
Please note that addusertotable is the name of the custom function.
Related
I'm using an amplify stack and need to perform some actions to my graphql api which has dynamodb behind it. The request in my lambda function returns an Unauthorized error: "Not Authorized to access getSourceSync on type SourceSync", where getSourceSync is the gql query and SourceSync is the model name.
My schema.grapqhl for this particular model is set up as following. Note auth rule allow private provider iam:
type SourceSync #model (subscriptions: { level: off }) #auth(rules: [
{allow: private, provider: iam}
{allow: groups, groups: ["Admins"], provider: userPools},
{allow: groups, groups: ["Users"], operations: [create], provider: userPools},
{allow: groups, groupsField: "readGroups", operations: [create, read], provider: userPools},
{allow: groups, groupsField: "editGroups", provider: userPools}]) {
id: ID! #primaryKey
name: String
settings_id: ID #index(name: "bySettingsId", queryField: "sourceSyncBySettingsId")
settings: Settings #hasOne(fields: ["settings_id"])
childLookup: String
createdAt: AWSDateTime!
updatedAt: AWSDateTime!
_createdBy: String
_lastChangedBy: String
_localChanges: AWSJSON
readGroups: [String]
editGroups: [String]
}
My lambda function's role has the following inline policy attached to it. (Actual ID values have been omitted for security purposes on this post):
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"appsync:GraphQL"
],
"Resource": [
"arn:aws:appsync:us-east-1:111myaccountID:apis/11mygraphqlapiID/*"
],
"Effect": "Allow"
},
{
"Action": [
"appsync:GetType"
],
"Resource": [
"*"
],
"Effect": "Allow"
}
]
}
And finally my lambda function is set up as follows with a simple query test:
/* stuff */
"use strict";
const axios = require("axios");
const awsAppSync = require("aws-appsync").default;
const gql = require("graphql-tag");
require("cross-fetch/polyfill");
const { PassThrough } = require("stream");
const aws = require("aws-sdk");
aws.config.update({
region: process.env.AWS_REGION,
});
const appSync = new aws.AppSync();
const graphqlClient = new awsAppSync({
url: process.env.API_GRAPHQLAPIENDPOINTOUTPUT,
region: process.env.AWS_REGION,
auth: {
type: "AWS_IAM",
credentials: aws.config.credentials,
},
disableOffline: true
});
exports.handler = async (event, context) => {
console.log('context :: '+JSON.stringify(context));
console.log('aws config :: '+JSON.stringify(aws.config));
const sourceSyncTypes = await appSync
.getType({
apiId: process.env.API_GRAPHQLAPIIDOUTPUT,
format: "JSON",
typeName: "SourceSync",
})
.promise();
console.log('ss = '+JSON.stringify(sourceSyncTypes));
try {
const qs = gql`query GetSourceSync {
getSourceSync(id: "ov3") {
id
name
}
}`;
const res = await graphqlClient.query({query: qs, fetchPolicy: 'no-cache'});
console.log(JSON.stringify(res));
}
catch(e) {
console.log('ERR :: '+e);
console.log(JSON.stringify(e));
}
};
Found the solution, there seems to be an issue with triggering a rebuild of the resolvers on the api after permitting a function to access the graphql api. However there is a distinction to note:
If the graphql api is part of an amplify app stack, then only functions created through the amplify cli for that app (ex: amplify add function) and that are given access to the api through there will be able to access the api.
additonally during the update when you either create, or update the function to give it permissions, you must ensure that during the amplify push operation, the api stack will also be updating. you can trigger this by simply adding or removing a space in a comment inside of your amplify/backend/api//schema.graphql file.
If the function was created "adhoc" directly through the aws console, but it is trying to access a graphql api that was created as part of an amplify app stack, then you will need to put that function's role in amplify/backend/api/< apiname>/custom-roles.json in the format
{
"adminRoleNames": ["<role name>", "<role name 2>", ...]
}
Documentation references here.
If neither your api or lambda function were created with the amplify cli as part of an app stack, then just need to give access to the graphql resources for query, mutation and subscription to the lambda's role in IAM, via inline policies or a pre-defined policy.
I want to embed Quicksight dashboard to an application. I have gone through the AWS quicksight documents, I did not get where I will find secure signed dashboard url.
In order to generate Quicksight secure dashboard url, follow the below steps:
Step 1: Create a new Identity Pool. Go to https://console.aws.amazon.com/cognito/home?region=us-east-1 , click ‘Create new Identity Pool’
Give an appropriate name.
Go to the Authentication Providers section, select Cognito.
Give the User Pool ID(your User pool ID) and App Client ID (go to App Clients in userpool and copy id).
Click ‘Create Pool’. Then click ‘Allow’ to create roles of the identity pool in IAM.
Step 2: Assign Custom policy to the Identity Pool Role
Create a custom policy with the below JSON.
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "quicksight:RegisterUser",
"Resource": "*",
"Effect": "Allow"
},
{
"Action": "quicksight:GetDashboardEmbedUrl",
"Resource": "*",
"Effect": "Allow"
},
{
"Action": "sts:AssumeRole",
"Resource": "*",
"Effect": "Allow"
}
]
}
Note: if you want to restrict the user to only one dashboard, replace the * with the dashboard ARN name in quicksight:GetDashboardEmbedUrl,
then goto the roles in IAM.
select the IAM role of the Identity pool and assign custom policy to the role.
Step 3: Configuration for generating the temporary IAM(STS) user
Login to your application with the user credentials.
For creating temporary IAM user, we use Cognito credentials.
When user logs in, Cognito generates 3 token IDs - IDToken, AccessToken, RefreshToken. These tokens will be sent to your application server.
For creating a temporary IAM user, we use Cognito Access Token and credentials will look like below.
AWS.config.region = 'us-east-1';
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId:"Identity pool ID",
Logins: {
'cognito-idp.us-east-1.amazonaws.com/UserPoolID': AccessToken
}
});
For generating temporary IAM credentials, we call sts.assume role method with the below parameters.
var params = {
RoleArn: "Cognito Identity role arn",
RoleSessionName: "Session name"
};
sts.assumeRole(params, function (err, data) {
if (err) console.log( err, err.stack); // an error occurred
else {
console.log(data);
})
You can add additional parameters like duration (in seconds) for the user.
Now, we will get the AccessKeyId, SecretAccessKey and Session Token of the temporary user.
Step 4: Register the User in Quicksight
With the help of same Cognito credentials used in the Step 3, we will register the user in quicksight by using the quicksight.registerUser method with the below parameters
var params = {
AwsAccountId: “account id”,
Email: 'email',
IdentityType: 'IAM' ,
Namespace: 'default',
UserRole: ADMIN | AUTHOR | READER | RESTRICTED_AUTHOR | RESTRICTED_READER,
IamArn: 'Cognito Identity role arn',
SessionName: 'session name given in the assume role creation',
};
quicksight.registerUser(params, function (err, data1) {
if (err) console.log("err register user”); // an error occurred
else {
// console.log("Register User1”);
}
})
Now the user will be registered in quicksight.
Step5: Update AWS configuration with New credentials.
Below code shows how to configure the AWS.config() with new credentials generated Step 3.
AWS.config.update({
accessKeyId: AccessToken,
secretAccessKey: SecretAccessKey ,
sessionToken: SessionToken,
"region": Region
});
Step6: Generate the EmbedURL for Dashboards:
By using the credentials generated in Step 3, we will call the quicksight.getDashboardEmbedUrl with the below parameters
var params = {
AwsAccountId: "account ID",
DashboardId: "dashboard Id",
IdentityType: "IAM",
ResetDisabled: true,
SessionLifetimeInMinutes: between 15 to 600 minutes,
UndoRedoDisabled: True | False
}
quicksight.getDashboardEmbedUrl(params,
function (err, data) {
if (!err) {
console.log(data);
} else {
console.log(err);
}
});
Now, we will get the embed url for the dashboard.
Call the QuickSightEmbedding.embedDashboard from front end with the help of the above generated url.
The result will be the dashboard embedded in your application with filter controls.
this link will give you what you need from aws cli https://aws.amazon.com/blogs/big-data/embed-interactive-dashboards-in-your-application-with-amazon-quicksight/
this is the step 3 aws cli cmd to give you embeded URL ( i was able to excecute)
aws quicksight get-dashboard-embed-url --aws-account-id (your account ID) --dashboard-id (your dashgboard ID) --identity-type IAM
there are many other dependence to enable the embeded dashboard per aws dcouments. i have not able to successfully doen that. GL and let me know if you make it happen!
PHP implementation
(in addition to Siva Sumanth's answer)
https://gist.github.com/evgalak/d0d1adf099e2d7bff741c16a89bf30ba
Our firm has built a custom admin portal and we want to provide our users a new link called reports which will link to aws quicksight dashboard but we don't want them to log in again in quicksight we cant the app (admin portal) to auto log them in with a specific role to see the dashboard. How can we achieve this?
Note: This answer is applicable only if you are using AWS Cognito
In order to generate Quicksight secure dashboard url, follow the below steps:
Step 1: Create a new Identity Pool. Go to https://console.aws.amazon.com/cognito/home?region=us-east-1 , click ‘Create new Identity Pool’
Give an appropriate name.
Go to the Authentication Providers section, select Cognito.
Give the User Pool ID(your User pool ID) and App Client ID (go to App Clients in userpool and copy id).
Click ‘Create Pool’. Then click ‘Allow’ to create roles of the identity pool in IAM.
Step 2: Assign Custom policy to the Identity Pool Role
Create a custom policy with the below JSON.
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "quicksight:RegisterUser",
"Resource": "*",
"Effect": "Allow"
},
{
"Action": "quicksight:GetDashboardEmbedUrl",
"Resource": "*",
"Effect": "Allow"
},
{
"Action": "sts:AssumeRole",
"Resource": "*",
"Effect": "Allow"
}
]
}
Note: if you want to restrict the user to only one dashboard, replace the * with the dashboard ARN name in quicksight:GetDashboardEmbedUrl,
then goto the roles in IAM.
select the IAM role of the Identity pool and assign custom policy to the role.
Step 3: Configuration for generating the temporary IAM(STS) user
Login to your application with the user credentials.
For creating temporary IAM user, we use Cognito credentials.
When user logs in, Cognito generates 3 token IDs - IDToken, AccessToken, RefreshToken. These tokens will be sent to your application server.
For creating a temporary IAM user, we use Cognito Access Token and credentials will look like below.
AWS.config.region = 'us-east-1';
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId:"Identity pool ID",
Logins: {
'cognito-idp.us-east-1.amazonaws.com/UserPoolID': AccessToken
}
});
For generating temporary IAM credentials, we call sts.assume role method with the below parameters.
var params = {
RoleArn: "Cognito Identity role arn",
RoleSessionName: "Session name"
};
sts.assumeRole(params, function (err, data) {
if (err) console.log( err, err.stack); // an error occurred
else {
console.log(data);
})
You can add additional parameters like duration (in seconds) for the user.
Now, we will get the AccessKeyId, SecretAccessKey and Session Token of the temporary user.
Step 4: Register the User in Quicksight
With the help of same Cognito credentials used in the Step 3, we will register the user in quicksight by using the quicksight.registerUser method with the below parameters
var params = {
AwsAccountId: “account id”,
Email: 'email',
IdentityType: 'IAM' ,
Namespace: 'default',
UserRole: ADMIN | AUTHOR | READER | RESTRICTED_AUTHOR | RESTRICTED_READER,
IamArn: 'Cognito Identity role arn',
SessionName: 'session name given in the assume role creation',
};
quicksight.registerUser(params, function (err, data1) {
if (err) console.log("err register user”); // an error occurred
else {
// console.log("Register User1”);
}
})
Now the user will be registered in quicksight.
Step5: Update AWS configuration with New credentials.
Below code shows how to configure the AWS.config() with new credentials generated Step 3.
AWS.config.update({
accessKeyId: AccessToken,
secretAccessKey: SecretAccessKey ,
sessionToken: SessionToken,
"region": Region
});
Step6: Generate the EmbedURL for Dashboards:
By using the credentials generated in Step 3, we will call the quicksight.getDashboardEmbedUrl with the below parameters
var params = {
AwsAccountId: "account ID",
DashboardId: "dashboard Id",
IdentityType: "IAM",
ResetDisabled: true,
SessionLifetimeInMinutes: between 15 to 600 minutes,
UndoRedoDisabled: True | False
}
quicksight.getDashboardEmbedUrl(params,
function (err, data) {
if (!err) {
console.log( data);
} else {
console.log(err);
}
}
);
Now, we will get the embed url for the dashboard.
Call the QuickSightEmbedding.embedDashboard from front end with the help of the above generated url.
The result will be the dashboard embedded in your application with filter controls.
AWS support single sign on.
I believe you can use that feature for the users.
please click here for more information
I've created a simple Lambda function to call a webpage, this works fine when I test it from the functions page however when trying to create a skill to call this function I end up with a "The remote endpoint could not be called, or the response it returned was invalid." error.
Lambda Function
var http = require('http');
exports.handler = function(event, context) {
console.log('start request to ' + event.url)
http.get(event.url, function(res) {
console.log("Got response: " + res.statusCode);
context.succeed();
}).on('error', function(e) {
console.log("Got error: " + e.message);
context.done(null, 'FAILURE');
});
console.log('end request to ' + event.url);
}
The Test Event code looks like this:
{
"url": "http://mywebsite.co.uk"
}
and I've added a trigger for the "Alexa Skills Kit".
The ARN for this function is showing as:
arn:aws:lambda:us-east-1:052516835015:function:CustomFunction
Alexa Skill (Developer Portal)
I've then created a skill with a simple Intent:
{
"intents": [
{
"intent": "CustomFunction"
}
]
}
and created an Utterance as:
CustomFunction execute my custom function
In the Configuration section for my skill I have selected the "AWS Lambda ARN (Amazon Resource Name)" option and entered the ARN into the box for North America.
In the Test -> Service Simulator section, I've added "execute my custom function" as the Text and this changes the Lambda Request to show:
{
"session": {
"sessionId": "SessionId.a3e8aee0-acae-4de5-85df-XXXXXXXXX",
"application": {
"applicationId": "amzn1.ask.skill.XXXXXXXXX"
},
"attributes": {},
"user": {
"userId": "amzn1.ask.account.XXXXXXXXX"
},
"new": true
},
"request": {
"type": "IntentRequest",
"requestId": "EdwRequestId.445267bd-2b4a-45ef-8566-XXXXXXXXX",
"locale": "en-GB",
"timestamp": "2016-11-27T22:54:07Z",
"intent": {
"name": "RunWOL",
"slots": {}
}
},
"version": "1.0"
}
but when I run the test I get the following error:
The remote endpoint could not be called, or the response it returned was invalid.
Does anyone have any ideas on why the skill can't connect to the function?
Thanks
The Service Simulator built into the Amazon Alexa Developer Console has known issues. Try copying the JSON generated by the Simulator and pasting it into your lambda function's test event. To access lambda's test events first find the blue 'Test' button. Next to that button select the (Actions Drop down menu) -> (Configure Test Event) -> Paste the provided JSON into the code area -> (Save and Test). Lambda's built in testing features are much more reliable than Alexa's.
If this does not solve the problem lambda's testing event returns a complete stackTrace and error codes. It becomes much easier to trouble shoot when every error isn't "The remote endpoint could not be called, or the response it returned was invalid."
{
"session": {
"sessionId": "SessionId.a3e8aee0-acae-4de5-85df-XXXXXXXXX",
"application": {
"applicationId": "amzn1.ask.skill.XXXXXXXXX"
},
"attributes": {},
"user": {
"userId": "amzn1.ask.account.XXXXXXXXX"
},
"new": true
},
"request": {
"type": "IntentRequest",
"requestId": "EdwRequestId.445267bd-2b4a-45ef-8566-XXXXXXXXX",
"locale": "en-GB",
"timestamp": "2016-11-27T22:54:07Z",
"intent": {
"name": "RunWOL",
"slots": {}
}
},
"version": "1.0"
}
While uploading .zip, do not compress the folder into .zip.
Instead, go into the folder, select package.json, index.js and node modules & then compress them and then upload the .zip.
This error message is very broad and may imply a lot of different issues. I was getting this error and in my case it was a timeout issue. How long does that website you are pinging taking to respond? The timeout doesn't seem to be properly documented, see my original question here: Troubleshooting Amazon's Alexa Skill Kit (ASK) Lambda interaction
I have the following Lambda function configured in AWS Lambda :
var AWS = require('aws-sdk');
var DOC = require('dynamodb-doc');
var dynamo = new DOC.DynamoDB();
exports.handler = function(event, context) {
var item = { id: 123,
foo: "bar"};
var cb = function(err, data) {
if(err) {
console.log(err);
context.fail('unable to update hit at this time' + err);
} else {
console.log(data);
context.done(null, data);
}
};
// This doesn't work. How do I get current stage ?
tableName = 'my_dynamo_table_' + stage;
dynamo.putItem({TableName:tableName, Item:item}, cb);
};
Everything works as expected (I insert an item in DynamoDB every time I call it).
I would like the dynamo table name to depend on the stage in which the lambda is deployed.
My table would be:
my_dynamo_table_staging for stage staging
my_dynamo_table_prod for stage prod
However, how do I get the name of the current stage inside the lambda ?
Edit: My Lambda is invoked by HTTP via an endpoint defined with API Gateway
If you have checked "Lambda Proxy Integration" in your Method Integration Request on API Gateway, you should receive the stage from API Gateway, as well as any stageVariable you have configured.
Here's an example of an event object from a Lambda function invoked by API Gateway configured with "Lambda Proxy Integration":
{
"resource": "/resourceName",
"path": "/resourceName",
"httpMethod": "POST",
"headers": {
"header1": "value1",
"header2": "value2"
},
"queryStringParameters": null,
"pathParameters": null,
"stageVariables": null,
"requestContext": {
"accountId": "123",
"resourceId": "abc",
"stage": "dev",
"requestId": "456",
"identity": {
"cognitoIdentityPoolId": null,
"accountId": null,
"cognitoIdentityId": null,
"caller": null,
"apiKey": null,
"sourceIp": "1.1.1.1",
"accessKey": null,
"cognitoAuthenticationType": null,
"cognitoAuthenticationProvider": null,
"userArn": null,
"userAgent": "agent",
"user": null
},
"resourcePath": "/resourceName",
"httpMethod": "POST",
"apiId": "abc123"
},
"body": "body here",
"isBase64Encoded": false
}
I managed it after much fiddling. Here is a walkthrough:
I assume that you have API Gateway and Lambda configured. If not, here's a good guide. You need part-1 and part-2. You can skip the end of part-2 by clicking the newly introduced button "Enable CORS" in API Gateway
Go to API Gateway.
Click here:
Click here:
Then expand Body Mapping Templates, enter application/json as content type, click the add button, then select mapping template, click edit
And paste the following content in "Mapping Template":
{
"body" : $input.json('$'),
"headers": {
#foreach($param in $input.params().header.keySet())
"$param": "$util.escapeJavaScript($input.params().header.get($param))" #if($foreach.hasNext),#end
#end
},
"stage" : "$context.stage"
}
Then click the button "Deploy API" (this is important for changes in API Gateway to take effect)
You can test by changing the Lambda function to this:
var AWS = require('aws-sdk');
var DOC = require('dynamodb-doc');
var dynamo = new DOC.DynamoDB();
exports.handler = function(event, context) {
var currentStage = event['stage'];
if (true || !currentStage) { // Used for debugging
context.fail('Cannot find currentStage.' + ' stage is:'+currentStage);
return;
}
// ...
}
Then call your endpoint. You should have a HTTP 200 response, with the following response body:
{"errorMessage":"Cannot find currentStage. stage is:development"}
Important note:
If you have a Body Mapping Template that is too simple, like this: {"stage" : "$context.stage"}, this will override the params in the request. That's why body and headers keys are present in the Body Mapping Template. If they are not, your Lambda has not access to it.
For those who use the serverless framework it's already implemented and they can access to event.stage without any additional configurations.
See this issue for more information.
You can get it from event variable. I logged my event object and got this.
{ ...
"resource": "/test"
"stageVariables": {
"Alias": "beta"
}
}