Well, my lambda function work's well according to the log's, but it never get completed in the codepipeline stage, I have already set permission to role for allow notificate pipeline ("codepipeline:PutJobSuccessResult",
"codepipeline:PutJobFailureResult") and even set maximun time to 20sec but still not working (it actually ends at 800ms).
const axios = require('axios')
const AWS = require('aws-sdk');
const url = 'www.exampleurl.com'
exports.handler = async (event, context) => {
const codepipeline = new AWS.CodePipeline();
const jobId = event["CodePipeline.job"].id;
const stage = event["CodePipeline.job"].data.actionConfiguration.configuration.UserParameters;
const putJobSuccess = function(message) {
var params = {
jobId: jobId
};
codepipeline.putJobSuccessResult(params, function(err, data) {
if (err) {context.fail(err); }
else {context.succeed(message);}
});
};
const putJobFailure = function(message) {
var params = {
jobId: jobId,
failureDetails: {
message: JSON.stringify(message),
type: 'JobFailed',
externalExecutionId: context.invokeid
}
};
codepipeline.putJobFailureResult(params, function(err, data) {
if (err) console.log(err)
context.fail(message);
});
};
try {
await axios.post(url, { content: stage})
putJobSuccess('all fine')
} catch (e) {
putJobFailure(e)
}
};
The root issue
Because nodeJS runs everything async by default, codepipeline.putJobSuccessResult is being run async. The issue seems to be that the Lambda function is finishing it's execution before codepipeline.putJobSuccessResult has a chance to complete.
The solution
Run codepipeline.putJobSuccessResult synchronously so that it is forced to complete before the response is returned to Lambda for the lambdaHandler.
const putJobSuccess = function(id) {
//await sleep(60);
console.log("Telling Codepipeline test passed for job: " + id)
var params = {
jobId: id
};
return codepipeline.putJobSuccessResult(params, function(err, data) {
if(err) {
console.error(err)
} else {
console.log(data)
}
}).promise()
};
exports.lambdaHandler = async (event, context) => {
...
await putJobSuccess( jobId )
return response
};
Whenever I see this issue, most of the time it is due to 'PutJobSuccessResult' never being invoked. The best way to check this is to go to CloudTrail > 'Event History' and look for 'Event name' = 'PutJobSuccessResult' during the time range you expect the Lambda function calling this API. Probably you will not find the 'PutJobSuccessResult', then please have a look at the code again and the Lambda execution logs in CloudWatch.
Related
I want to achieve something like this:
call my website url https://mywebsite/api/something
then my next.js website api will call external api
get external api data
update external api data to mongodb database one by one
then return respose it's status.
Below code is working correctly correctly. data is updating on mongodb but when I request to my api url it respond me very quickly then it updates data in database.
But I want to first update data in database and then respond me
No matter how much time its take.
Below is my code
export default async function handler(req, res) {
async function updateServer(){
return new Promise(async function(resolve, reject){
const statusArray = [];
const apiUrl = `https://example.com/api`;
const response = await fetch(apiUrl, {headers: { "Content-Type": "application/json" }});
const newsResults = await response.json();
const articles = await newsResults["articles"];
for (let i = 0; i < articles.length; i++) {
const article = articles[i];
try {
insertionData["title"] = article["title"];
insertionData["description"] = article["description"];
MongoClient.connect(mongoUri, async function (error, db) {
if (error) throw error;
const articlesCollection = db.db("database").collection("collectionname");
const customQuery = { url: article["url"] };
const customUpdate = { $set: insertionData };
const customOptions = { upsert: true };
const status = await articlesCollection.updateOne(customQuery,customUpdate,customOptions);
statusArray.push(status);
db.close();
});
} catch (error) {console.log(error);}
}
if(statusArray){
console.log("success", statusArray.length);
resolve(statusArray);
} else {
console.log("error");
reject("reject because no statusArray");
}
});
}
updateServer().then(
function(statusArray){
return res.status(200).json({ "response": "success","statusArray":statusArray }).end();
}
).catch(
function(error){
return res.status(500).json({ "response": "error", }).end();
}
);
}
How to achieve that?
Any suggestions are always welcome!
Objective: show errors on AWS X-ray (all errors from lambda).
'use strict';
const AWSXRay = require('aws-xray-sdk-core'),
AWS = AWSXRay.captureAWS(require('aws-sdk')),
env = process.env;
AWS.config.update({
region: env.REGION
});
const dynamodbDocumentClient = new AWS.DynamoDB.DocumentClient();
exports.handler = async (event, context, callback) => {
let seg=AWSXRay.getSegment();
try {
const params = {
/*PARAMS HERE*/
};
let res = await dynamodbDocumentClient.scan(params).promise();
throw('ERROR FOR TESTING');
return res;
//callback(null,res);
}
catch(err) {
let subseg=seg.addNewSubsegment('error');
subseg.addMetadata("error", "error", "my_error");
subseg.addAnnotation('errr', 'this is a test');
subseg.addError(err);
subseg.addErrorFlag();
subseg.close();
console.log('==ERROR==',err);
return err;
}
};
When I use AWSXRay.captureAWS the subsegment 'error' doesnt show on X-ray. If I dont use captureAWS the error appear in X-ray correctly.
The issue is likely with how you're structuring your Lambda. First, you shouldn't be using callback in an async function handler, async function handlers should return native promises or errors per the docs.
If you want to use a callback to return the result of your function, which it seems like you do, the handler should be synchronous. That being said, you're throwing your error after the callback, which will not be reflected in X-Ray because the X-Ray segment for Lambda functions closes when the callback is called. If an error is thrown before the callback (e.g. during the DynamoDB call) it should be captured in your error subsegment.
EDIT
After the original post was edited, I was unable to reproduce the error with this code:
const AWSXRay = require('aws-xray-sdk');
const AWS = AWSXRay.captureAWS(require('aws-sdk'));
const ddb = new AWS.DynamoDB.DocumentClient();
exports.handler = async (event, context) => {
try {
const res = await ddb.scan({TableName: 'scorekeep-game'}).promise();
throw('Test Error');
return result;
} catch (e) {
console.log('caught!');
var sub2 = AWSXRay.getSegment().addNewSubsegment('err');
sub2.addError(e);
sub2.addErrorFlag();
sub2.close();
return e;
}
};
I am new to async functions and promises. I have written a Lambda function which queries a DynamoDB table and returns the result. The code is executing inside the callback success block and I am able to see the response in the log from the console.log(res) line. However the Lambda response is always showing as null, i.e. the response object below is not returned at all. I was able to make this work using a Synchronous Lambda function using a callback to return the data. Can you please suggest what I may be doing incorrectly.
const doc = require('dynamodb-doc');
var dynamodbclient;
const tablename = process.env.TABLE_NAME;
exports.handler = async(event) => {
if (!dynamodbclient) {
dynamodbclient = new doc.DynamoDB();
}
let id = event.params.id;
let queryparams = {
TableName: 'table-name',
Key: { id: id }
};[![enter image description here][1]][1]
var getItemsCallback = (err, res) => {
console.log('inside');
if (err) {
}
else {
console.log('success');
console.log(res);
const response = {
statusCode: 200,
body: JSON.stringify('Hello from Lambda!'),
};
return response;
}
};
var item = await dynamodbclient.getItem(queryparams, getItemsCallback).promise();
};
Your callback is still executing after the promise resolves, so the lambda will terminate and your callback will not finish.
Try:
try {
const item = await dynamodbclient.getItem(queryparams).promise();
} catch (err) {}
console.log('success');
console.log(item);
const response = {
statusCode: 200,
body: JSON.stringify('Hello from Lambda!'),
};
return response;
I've got 3 functions.
Cron job lambda function
Event driven function which detects when a new record is added to the DynamoDB
A reusable function which is currently called by the 2 above functions
The Cron job function
export async function scheduledFunction() {
const detailsHistory = await sharedFunction(param1);
}
The event driven function
export async function eventFunction(event) {
event.Records.forEach(async record => {
if (record.eventName === 'INSERT') {
await sharedFunction(param1)
}
}
}
The function called by both of the event and scheduled function
const sharedFunction = async (param1) {
const apiUrl = 'xxxxxx';
const details = await axios.get(apiUrl, {
headers: {
'x-api-key': xxxx
}
});
}
The event function works when the DynamoDB has a new insert and then calls the 3rd party API which works as expected
The scheduled function fires every 4 hours and is works and gets to the sharedFunction, but when its gets to the API call await axios.get it just does nothing, I'm not getting any errors in the CloudWatch. I've placed console.logs() before and after the call and it logs the one before but nothing after.
You should always put async code inside try ... catch block. Also forEach won't work with promise you will need to use for loop. Try this:
export async function eventFunction(event) {
try {
for (let record of event.Records) {
if (record.eventName === 'INSERT') {
await sharedFunction(param1)
}
}
}
catch (err) {
console.log(err);
return err;
}
}
Shared function:
const sharedFunction = async (param1) => {
try {
const apiUrl = 'xxxxxx';
return await axios.get(apiUrl, {
headers: {
'x-api-key': xxxx
}
});
}
catch (err) {
return err;
}
}
below is the code I'm trying to test by passing "type" property as "all". However, the returned data is null. The role set to this lambda is also given appropriate access to DB. There is data in the table as well.
const AWS = require('aws-sdk');
const dynamodb = new AWS.DynamoDB({ region: 'us-east-2', apiVersion: '2012-08-10' });
exports.handler = async (event, context, callback) => {
// TODO implement
const type = event.type;
if(type === "all"){
const params = {
TableName: 'compare-yourself'
};
dynamodb.scan(params, function(err, data){
if(err){
console.log(err);
callback(err);
} else {
console.log(data);
console.log(type);
callback(null, data);
}
});
} else if(type === "single") {
console.log(type);
callback(null, "Just my Data");
} else {
callback(null, "Hello from Lambda!");
}
};
I added a promise resolve rejection against the scan function and it resolved with a null always. When I removed the same, it worked fine.
This question is old, but I recognize the code. It's from the Udemy course on AWS by Maximilian Schwarzmüller.
If you come here with the same question, to implement the DynamoDB scan function in that course today, with newer versions of nodejs, use a promise and place the dynamodb.scan in a try catch. This code is in the Q&A section of that course under the title "I'm not able to get data".
const AWS = require('aws-sdk');
const dynamodb = new AWS.DynamoDB({ region: 'us-east-2', apiVersion: '2012-08-10' });
exports.handler = async (event, context, callback) => {
const type = event.type;
if (type === 'all') {
const params = {
TableName: 'compare-yourself'
}
try {
const data = await dynamodb.scan(params).promise()
callback(null, data)
} catch(err) {
callback(err);
}
} else if (type === 'single') {
callback(null, 'Just my data');
} else {
callback(null, 'Hello from Lambda');
}
};