I was just having fun in sending me SMS from a Lambda called via API Gateway (REST API). No problem at all until it suddendly stopped sending SMS. I'm actually using a free tier level on my AWS account, but in no way I have reached the maximum sendable SMS limit.
Permissions and roles have not been modified from yesterday when text messages still were arriving to my verified mobile number.
As you can see, inside my code, I used a "test2" variable to return the result for SMS sending but it's barely useless.
Is there any way to better undestand the error behind this problem? And why am I getting it all of a sudden?
My lambda code:
const AWS = require("aws-sdk");
const dynamo = new AWS.DynamoDB.DocumentClient();
const SNS = new AWS.SNS();
const tableName = "Testing";
const smsMessage = {
PhoneNumber: '+39#mynumber#',
Message: 'Someone called your api!'
};
const httpMethodConverter = 'http-method';
const pathRequestConverter = "resource-path";
const headers = {
"Access-Control-Allow-Origin":"https://#myaccount#.github.io",
"Content-Type": "application/json",
"Access-Control-Allow-Methods": "GET, POST, PATCH, PUT, DELETE, OPTIONS",
"Access-Control-Allow-Headers": "Origin, Content-Type, X-Auth-Token"
};
let body;
let statusCode = 200;
let test2;
exports.handler = async (event, context) => {
try {
let test = JSON.stringify(event);
let callContext = event.context;
let httpMethod = callContext[httpMethodConverter];
let path = callContext[pathRequestConverter];
let methodAndPath = httpMethod + " "+ path;
test2= await SNS.publish(smsMessage).promise();
switch (methodAndPath) {
case "DELETE /items/{id}":
await dynamo
.delete({
TableName: tableName,
Key: {
Id: parseInt(JSON.parse(test).params.path.id)
}
})
.promise();
body = `Deleted item ${JSON.parse(test).params.path.id}`;
break;
case "GET /items/{id}":
body = await dynamo
.get({
TableName: tableName,
Key: {
Id: parseInt(JSON.parse(test).params.path.id)
}
})
.promise();
break;
case "GET /items":
body = await dynamo.scan({ TableName: tableName }).promise();
break;
case "POST /items":
var bodyParser = "body-json";
var bodyPassed = JSON.parse(test)[bodyParser];
await dynamo
.put({
TableName: tableName,
Item: {
Id: parseInt(bodyPassed.Id),
Type: bodyPassed.Type
}
})
.promise();
body = "Put item "+ bodyPassed.Type+ " id: " + parseInt(bodyPassed.Id);
break;
default:
throw new Error(`Unsupported route: "${event.context}"`);
}
} catch (err) {
statusCode = 400;
body = err.message;
} finally {
body = JSON.stringify(body);
}
return {
statusCode,
body,
headers,
test2
};
};
This is the answer for my SNS.publish(message).promise
"test2": {
"ResponseMetadata": {
"RequestId": "b2fbd9fc-4cc0-54e8-a660-82#########"
},
"MessageId": "c94393bf-cbb3-5cb9-8155-c#########"
}
If you enable delivery status logging through e.g. https://docs.aws.amazon.com/sns/latest/dg/sms_stats_cloudwatch.html, you should receive an error message detailing why the message could not be delivered. Most likely, you've hit the default monthly $1 spend limit quota.
Related
new to AWS and just not sure how to define the relevant authenitcation to get my lambda function to be able to call my graphQL endpoint for a post req. Assuming I need to put an API key somewhere in this function but just am a bit lost. Any help at all would be great. Have put the function below - created it using the amplify cli and the generategraphqlpermissions flag is set to true if thats any help narrowing it down.
import crypto from '#aws-crypto/sha256-js';
import { defaultProvider } from '#aws-sdk/credential-provider-node';
import { SignatureV4 } from '#aws-sdk/signature-v4';
import { HttpRequest } from '#aws-sdk/protocol-http';
import { default as fetch, Request } from 'node-fetch';
const GRAPHQL_ENDPOINT = <myEndpoint>;
const AWS_REGION = process.env.AWS_REGION || 'us-east-1';
const { Sha256 } = crypto;
const query = /* GraphQL */ `mutation CreateCalendarEvent($input: CreateCalendarEventInput!, $condition: ModelCalendarEventConditionInput) {
createCalendarEvent(input: $input, condition: $condition) {
__typename
id
start
end
title
actions
allDay
resizable
draggable
colour
createdAt
updatedAt
}
}`;
/**
* #type {import('#types/aws-lambda').APIGatewayProxyHandler}
*/
export const handler = async (event) => {
console.log(`EVENT: ${JSON.stringify(event)}`);
console.log(GRAPHQL_ENDPOINT);
const endpoint = new URL(GRAPHQL_ENDPOINT);
const signer = new SignatureV4({
credentials: defaultProvider(),
region: AWS_REGION,
service: 'appsync',
sha256: Sha256
});
const requestToBeSigned = new HttpRequest({
method: 'POST',
headers: {
'Content-Type': 'application/json',
host: endpoint.host
},
hostname: endpoint.host,
body: JSON.stringify({ query }),
path: endpoint.pathname
});
const signed = await signer.sign(requestToBeSigned);
const request = new Request(endpoint, signed);
let statusCode = 200;
let body;
let response;
try {
response = await fetch(request);
body = await response.json();
if (body.errors) statusCode = 400;
} catch (error) {
statusCode = 500;
body = {
errors: [
{
message: error.message
}
]
};
}
return {
statusCode,
// Uncomment below to enable CORS requests
// headers: {
// "Access-Control-Allow-Origin": "*",
// "Access-Control-Allow-Headers": "*"
// },
body: JSON.stringify(body)
};
};
WHen invoking an AWS Service from Lambda, you do not need the keys. Instead, you can give the IAM role that the Lambda function runs under the permissions to invoke that service. In your case, give the role permission to invoke app sync.
More information can be found here:
https://docs.aws.amazon.com/lambda/latest/dg/lambda-permissions.html
I got "Internal server error" Status: 502 on AWS API Gateway. Searched solutions this post says need to handle callback with a callback function. Would anyone please advise how to incorporate the callback codes with my AWS-Lambda function below? Thank you so much.
message: "Internal server error" when try to access aws gateway api
callback(null, {
statusCode: 200,
body: JSON.stringify(message),
headers: {'Content-Type': 'application/json'}
});
My AWS-Lambda function
'use strict';
const AWS = require('aws-sdk');
exports.handler = async (event, context) => {
const documentClient = new AWS.DynamoDB.documentClient();
let responseBody = "";
let statusCode = 0;
const {
id,
productname
} = JSON.parse(event.body);
const params = {
TableName: "Products",
Item: {
id: id,
productname: productname
}
};
try {
const data = await documentClient.put(params).promise();
responseBody = JSON.stringify(data);
statusCode = 201;
} catch (err) {
responseBody = `Unable to put product: ${err}`;
statusCode = 403;
}
const response = {
statusCode: statusCode,
headers: {
"Content-Type": "application/json"
},
body: responseBody
};
return response
};
I am working on a custom auth solution, I backend is on NODE (lambda) and calling that lambda by API gateway (it's a post-call).
For every thin is working fine if I use no-Proxy APIs, but in my case in need to pass custom additional headers. And when I tried with prox it is not responding. (it look like Cognito async call takes time or response is not resolving)
I am not sure I am missing some configuration or something wrong with code (code is working fine individual lambda and with API without Proxy).
here is my Lambda code.
// const AWS = require("aws-sdk");
// var crypto = require("crypto-js");
var CryptoJS = require("crypto-js");
var Crypto = require("crypto");
const { CognitoIdentityServiceProvider } = require("aws-sdk");
const hashSecret = (clientSecret, username, clientId) =>
Crypto.createHmac("SHA256", clientSecret)
.update(username + clientId)
.digest("base64");
async function isUserValid() {
const USER_POOL_ID = "poolid";
const CLIENT_ID = "clidntID";
const CLIENT_SECRET = "secreteCode";
const Password = "userName";
const Username = "password";
const cognito = new CognitoIdentityServiceProvider({
region: "ap-southeast-2",
});
try {
const payload = {
UserPoolId: USER_POOL_ID,
AuthFlow: "ADMIN_NO_SRP_AUTH",
ClientId: CLIENT_ID,
AuthParameters: {
USERNAME: "username",
PASSWORD: "password",
SECRET_HASH: hashSecret(CLIENT_SECRET, Username, CLIENT_ID),
},
};
const response = await cognito.adminInitiateAuth(payload).promise();
// // console.log("respone :", response)
// console.log("before response node js lambda ::: ")
return response;
} catch (e) {
console.log("error : ", e.message);
}
}
async function test() {
return "test ";
}
exports.handler = async (event) => {
// TODO implement
console.log("event : ", event);
'
const response = {
statusCode: 200,
headers: {
"Content-Type" : "application/json",
"Access-Control-Allow-Origin" : "*",
"X-Content-Type-Option" : "nosniff",
"Content-Security-Policy" : "default-src self",
"X-frame-options" : "DENY",
"Cache-Control" : "max-age=86400",
"X-XSS-protection" : 0,
"X-rate-limit": 600,
"Strict-Transport-Security": "max-age=31536000; includeSubDomains",
},
// body: await test() // (this response is working fine
body: await isUserValid(),
};
return response;
};
Errors when tested from API gateway with proxy setting on :
{
"message": "Internal server error"
}
Response Headers
{"x-amzn-ErrorType":"InternalServerErrorException"}
I tried multiple options but nothing is working for me.
The 'body' property must be a JSON string.
await cognito.adminInitiateAuth(payload).promise() is returning an object. You need to JSON.stringify() that response. Be sure to take care of the error case , too.
I am having a bit of an issue getting push notifications to be sent remotely from a lambda using AWS pinpoint.
In my application I have the following code:
PushNotification.configure(awsconfig)
PushNotificationIOS.addEventListener('registrationError', console.log)
PushNotification.onRegister(async token => {
console.log('in app registration', token)
PushNotification.updateEndpoint(token)
setRegistrationToken(token)
})
PushNotification.onNotification(notification => {
console.log('in app notification', notification)
if (Platform.OS === 'ios') {
notification.finish(PushNotificationIOS.FetchResult.NoData)
}
})
PushNotification.onNotificationOpened(notification => {
console.log('the notification is opened', notification)
})
const endpointId = Analytics.getPluggable('AWSPinpoint')._config.endpointId
console.log(`endpoint ID: ${endpointId}`)
if (Platform.OS === 'ios') {
PushNotification.requestIOSPermissions()
}
That code runs perfectly and I can take the token that is logged, go to the pinpoint console, and send a "Test Notification" by choosing "Device Tokens" and "APNS Sandbox."
After that, I set up my lambda and added the following code:
const AWS = require('aws-sdk');
async function sendTestNotification() {
console.log("CALLED SEND TEST")
const title = "Test Message"
const message = "This is a message from Pinpoint dynamically delivered via code"
const applicationId = <HARD CODED APPLICATION ID FROM PINPOINT CONSOLE>
const token = <HARD CODED DEVICE TOKEN I USED IN THE TEST NOTIFICATION>
var messageRequest = {
'Addresses': {
[token]: {
'ChannelType' : 'APNS_SANDBOX'
}
},
'MessageConfiguration': {
'APNSMessage': {
'Action': 'OPEN_APP',
'Body': message,
'Priority': 'normal',
'SilentPush': false,
'Title': title,
'TimeToLive': 30
}
}
};
AWS.config.update({ region: 'us-east-1'});
var pinpoint = new AWS.Pinpoint();
var params = {
"ApplicationId": applicationId,
"MessageRequest": messageRequest
}
pinpoint.sendMessages(params, (sendMessagesErr, sendMessagesData) => console.log(sendMessagesErr, sendMessagesData))
}
exports.handler = async (event) => {
console.log('running')
await sendTestNotification();
const response = {
statusCode: 200,
body: JSON.stringify('Hello from Lambda!'),
};
return response;
};
When I test the function, it runs and completes normally with no errors. However, no notifications come through. Initially, I thought it may have to do with permissions, but I have given the lambda full access to Pinpoint via a custom role, and that still did not change the result.
Any help would be much appreciated, as I am not sure where to troubleshoot next.
You haven't set the token in your MessageRequest. You have [token] under addresses:. You need to put the actual APNS token here. (I've put in some random letters in numbers here for an example...
var messageRequest = {
'Addresses': {
'483hgto483gh87egf8o2phqradgq3984hgeorigheg27r': {
'ChannelType' : 'APNS_SANDBOX'
}
},
'MessageConfiguration': {
'APNSMessage': {
'Action': 'OPEN_APP',
'Body': message,
'Priority': 'normal',
'SilentPush': false,
'Title': title,
'TimeToLive': 30
}
}
};
Goal: Get real-time stock data from IEX API, store it in DynamoDB, use data in DynamoDB to display on site.
Edit: I want to use the pull mechanism from the API where I can pull data periodically.
Currently, on API gateway, I created a get method for my stocks resource where I set the integration type to HTTP and entered the IEX Endpoint URL as the Endpoint URL. For now, I just got the data for one stock and the JSON Response Body looks like:
{
"symbol": "AAPL",
"companyName": "Apple Inc",
"primaryExchange": "NASDAQ/NGS (GLOBAL SELECT MARKET)",
"calculationPrice": "close",
"open": 132.5,
"openTime": 1610116201607,
"openSource": "official",
"close": 132.05,
"closeTime": 1610139600449,
"closeSource": "official"
}
I want to integrate Lambda here somehow so that it can get this API data and store it in DynamoDB. So far this is what my Lambda function looks like:
'use strict';
const AWS = require('aws-sdk');
exports.handler = async (event, context) => {
const documentClient = new AWS.DynamoDB.DocumentClient();
let responseBody = "";
let statusCode = 0;
const { symbol, companyName } = JSON.parse(event.body);
const params = {
TableName: "Stocks",
Item: {
symbol: symbol,
companyName: companyName
}
};
try {
const data = await documentClient.put(params).promise();
responseBody = JSON.stringify(data);
statusCode = 201;
} catch(err) {
responseBody = `Unable to put product: ${err}`;
statusCode = 403;
}
const response = {
statusCode: statusCode,
headers: {
"Content-Type": "application/json"
},
body: responseBody
};
return response
};
How do I get the API data from API Gateway and integrate it with a Lambda function that stores this data to DynamoDB?