I've got a Dynamo DB table:
Partition key: FingerPrint (String)
Sort key: IpAddress (String)
LastUpdated (Number)
TimesUpdated (Number)
Votes (string)
And I have this Lambda to insert or update items:
const AWS = require('aws-sdk');
// As request in comments:
console.log('Event:', event);
exports.handler = async (event) => {
// Get the FingerPrint and IpAddress from the event
const fingerPrint = event.FingerPrint;
var ipAddress = '';
try {
ipAddress = event.client_ip;
//ipAddress = event.identity.sourceIp;
} catch (error) {
}
if (!ipAddress) {
ipAddress = '100.100.100.100';
}
console.log('FingerPrint:', fingerPrint);
console.log('IpAddress:', ipAddress);
console.log('Votes:', event.Votes);
// IT FAILS HERE!!
if (!fingerPrint || !ipAddress || !event.Votes) {
console.log('Missing required properties');
// Handle the missing properties error appropriately
return;
}
// Create a DynamoDB client
const ddb = new AWS.DynamoDB();
// Get the item from the table
const params = {
TableName: 'SRHTop100',
Key: {
'FingerPrint': { 'S': fingerPrint },
'IpAddress': { 'S': ipAddress },
},
};
console.log('Get Params:', params);
const item = await ddb.getItem(params).promise();
// Get the 8 digit date
const getCurrentDate = () => {
const date = new Date();
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}${month}${day}`;
};
const lastUpdated = parseInt(getCurrentDate());
// Check if the item exists
if (item.Item) {
const existingItem = item.Item;
// Update the TimesUpdated field, votes, and lastUpdated
existingItem.TimesUpdated.N = (parseInt(existingItem.TimesUpdated.N) + 1).toString();
existingItem.LastUpdated.N = lastUpdated.toString();
existingItem.Votes.S = event.Votes.toString();
// Update the item in the table
const updateParams = {
TableName: 'SRHTop100',
Key: {
'FingerPrint': { 'S': fingerPrint },
'IpAddress': { 'S': ipAddress },
},
UpdateExpression: 'SET TimesUpdated = :updated, LastUpdated = :lastUpdated, Votes = :votes',
ExpressionAttributeValues: {
':updated': { 'N': existingItem.TimesUpdated.N },
':lastUpdated': { 'N': existingItem.LastUpdated.N },
':votes': { 'S': existingItem.Votes.S },
},
};
console.log('Update Params:', updateParams);
await ddb.updateItem(updateParams).promise();
} else {
// Create a new item in the table
const newItem = {
'FingerPrint': { 'S': fingerPrint },
'IpAddress': { 'S': ipAddress },
'LastUpdated': { 'N': lastUpdated.toString() },
'TimesUpdated': { 'N': '1' },
'Votes': { 'S': event.Votes.toString() },
};
const createParams = {
TableName: 'SRHTop100',
Item: newItem,
};
console.log('Create Params:', params);
await ddb.putItem(createParams).promise();
}
console.log('Done');
};
Calling this method in Lambda with this payload works:
{
"FingerPrint": "2163923702",
"Votes": "{ '12': 1 }"
}
The problems when I call the Lambda via the API GW, Testing it fails with:
{"message": "Internal server error"}
I can see in the CloudWatch logs:
INFO FingerPrint: undefined
INFO IpAddress: 100.100.100.100
INFO Votes: undefined
INFO Missing required properties
As per the comments after printing out the event this is what I get:
2023-06-14T14:41:31.347Z 8b34c7b8-1934-48b3-b416-a29844b96533 INFO Event: {
resource: '/srhTop100Lambda',
path: '/srhTop100Lambda',
httpMethod: 'POST',
headers: null,
multiValueHeaders: null,
queryStringParameters: null,
multiValueQueryStringParameters: null,
pathParameters: null,
stageVariables: null,
requestContext: {
resourceId: 'fbpc7m',
resourcePath: '/srhTop100Lambda',
httpMethod: 'POST',
extendedRequestId: 'Gg2wMEliywMFvog=',
requestTime: '14/Jun/2023:14:41:30 +0000',
path: '/srhTop100Lambda',
accountId: '456567667775',
protocol: 'HTTP/1.1',
stage: 'test-invoke-stage',
domainPrefix: 'testPrefix',
requestTimeEpoch: 1686753690532,
requestId: 'c1942d7f-9c29-4ed6-8f21-8e7de67139cd',
identity: {
cognitoIdentityPoolId: null,
cognitoIdentityId: null,
apiKey: 'test-invoke-api-key',
principalOrgId: null,
cognitoAuthenticationType: null,
userArn: 'arn:aws:iam::456567667775:user/jezza.thompson',
apiKeyId: 'test-invoke-api-key-id',
userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 Edg/114.0.1823.41',
accountId: '456567667775',
caller: 'AIDAWUTMNXQ75DPQPXNSV',
sourceIp: 'test-invoke-source-ip',
accessKey: 'ASIAWUTMNXQ7SFXRZFZQ',
cognitoAuthenticationProvider: null,
user: 'AIDAWUTMNXQ75DPQPXNSV'
},
domainName: 'testPrefix.testDomainName',
apiId: 'lxpz295o9e'
},
body: '{\r\n' +
' "FingerPrint": "2163923702",\r\n' +
' "Votes": \r\n' +
` "{ '12': 2 }"\r\n` +
'}',
isBase64Encoded: false
}
I don't have any Mapping Templates and I'm not sure what's going on and why the Payload isn't being sent.
You are trying to parse event.Fingerprint
but the FingerPrint is held in body
:
body: '{\r\n' +
' "FingerPrint": "2163923702",\r\n' +
' "Votes": \r\n' +
` "{ '12': 2 }"\r\n` +
'}',
So you need to parse the event.body and subtract your data from there.
const requestBody = JSON.parse(event.body);
var fingerPrint = requestBody.FingerPrint;
var votes = requestBody.Votes;