public void TestFunction() { var function = new Function(); var context = new TestLambdaContext(); SNSEvent.SNSMessage snsMessage = new SNSEvent.SNSMessage { Message = "{\"notificationType\":\"Bounce\",\"bounce\":{\"bounceType\":\"Permanent\",\"feedbackId\":\"feedback_id\",\"bouncedRecipients\":[{\"emailAddress\":\"[email protected]\"}]}}", Timestamp = DateTime.Parse("1/1/1990") }; SNSEvent.SNSRecord snsRecord = new SNSEvent.SNSRecord { EventSource = "event_source", EventSubscriptionArn = "event_subscription_arn", EventVersion = "event_version", Sns = snsMessage }; List <SNSEvent.SNSRecord> recordList = new List <SNSEvent.SNSRecord>(); recordList.Add(snsRecord); SNSEvent snsEvent = new SNSEvent { Records = recordList }; var retval = function.FunctionHandler(snsEvent, context); Assert.Equal("okay", retval.Result); }
/// <summary> /// The <see cref="ProcessMessageStreamAsync(Stream)"/> method is overridden to /// provide specific behavior for this base class. /// </summary> /// <remarks> /// This method cannot be overridden. /// </remarks> /// <param name="stream">The stream with the request payload.</param> /// <returns>The task object representing the asynchronous operation.</returns> public override sealed async Task <Stream> ProcessMessageStreamAsync(Stream stream) { // read stream into memory LogInfo("reading stream body"); string snsEventBody; using (var reader = new StreamReader(stream)) { snsEventBody = reader.ReadToEnd(); } var stopwatch = Stopwatch.StartNew(); var metrics = new List <LambdaMetric>(); // process received sns record (there is only ever one) try { // sns event deserialization LogInfo("deserializing SNS event"); try { var snsEvent = LambdaSerializer.Deserialize <SNSEvent>(snsEventBody); _currentRecord = snsEvent.Records.First().Sns; // message deserialization LogInfo("deserializing message"); var message = Deserialize(CurrentRecord.Message); // process message LogInfo("processing message"); await ProcessMessageAsync(message); // record successful processing metrics stopwatch.Stop(); var now = DateTimeOffset.UtcNow; metrics.Add(("MessageSuccess.Count", 1, LambdaMetricUnit.Count)); metrics.Add(("MessageSuccess.Latency", stopwatch.Elapsed.TotalMilliseconds, LambdaMetricUnit.Milliseconds)); metrics.Add(("MessageSuccess.Lifespan", (now - CurrentRecord.GetLifespanTimestamp()).TotalSeconds, LambdaMetricUnit.Seconds)); return("Ok".ToStream()); } catch (Exception e) { LogError(e); try { // attempt to send failed message to the dead-letter queue await RecordFailedMessageAsync(LambdaLogLevel.ERROR, FailedMessageOrigin.SQS, LambdaSerializer.Serialize(snsEventBody), e); // record failed processing metrics metrics.Add(("MessageDead.Count", 1, LambdaMetricUnit.Count)); } catch { // NOTE (2020-04-22, bjorg): since the message could not be sent to the dead-letter queue, // the next best action is to let Lambda retry it; unfortunately, there is no way // of knowing how many attempts have occurred already. // unable to forward message to dead-letter queue; report failure to lambda so it can retry metrics.Add(("MessageFailed.Count", 1, LambdaMetricUnit.Count)); throw; } return($"ERROR: {e.Message}".ToStream()); } } finally { _currentRecord = null; LogMetric(metrics); } }
//--- Extension Methods --- public static DateTimeOffset GetLifespanTimestamp(this SNSEvent.SNSMessage message) { // a custom "SentTimestamp" message attribute takes precedence over the record "SentTimestamp" attribute if ( message.MessageAttributes.TryGetValue(SENT_TIME_ATTRIBUTE, out var sentTimeMessageAttribute) && (sentTimeMessageAttribute.Type == "String") && long.TryParse(sentTimeMessageAttribute.Value, out var sentTimeEpoch) ) { return(DateTimeOffset.FromUnixTimeMilliseconds(sentTimeEpoch)); } return(message.Timestamp.ToUniversalTime()); }
private async Task ProcessMessageAsync(SNSEvent.SNSMessage message) { if (message == null || message.Message == null || message.Message.Contains("Error:")) { LambdaLogger.Log($"Error: {message?.Message}"); } LambdaLogger.Log($"Processed message {message?.Message}"); EventMessage eventMessage = JsonConvert.DeserializeObject <EventMessage>(message?.Message); await RegisterUser(eventMessage); }
public async Task CompleteTask(SNSEvent.SNSMessage message) { if (message.Message == "available") { await stepFunctionsClient.SendTaskSuccessAsync(new SendTaskSuccessRequest { Output = Serialize(new { Status = message.Message }), TaskToken = message.MessageAttributes["TaskToken"].Value }); return; } await stepFunctionsClient.SendTaskFailureAsync(new SendTaskFailureRequest { Cause = message.Message, TaskToken = message.MessageAttributes["TaskToken"].Value }); }
/// <summary> /// The <see cref="ProcessMessageStreamAsync(Stream)"/> method is overridden to /// provide specific behavior for this base class. /// </summary> /// <remarks> /// This method cannot be overridden. /// </remarks> /// <param name="stream">The stream with the request payload.</param> /// <returns>The task object representing the asynchronous operation.</returns> public override sealed async Task <Stream> ProcessMessageStreamAsync(Stream stream) { // read stream into memory LogInfo("reading stream body"); string snsEventBody; using (var reader = new StreamReader(stream)) { snsEventBody = reader.ReadToEnd(); } // process received sns record (there is only ever one) try { // sns event deserialization LogInfo("deserializing SNS event"); var snsEvent = DeserializeJson <SNSEvent>(snsEventBody); CurrentRecord = snsEvent.Records.First().Sns; // message deserialization LogInfo("deserializing message"); var message = Deserialize(CurrentRecord.Message); // process message LogInfo("processing message"); await ProcessMessageAsync(message); return("Ok".ToStream()); } catch (Exception e) when(!(e is LambdaRetriableException)) { LogError(e); await RecordFailedMessageAsync(LambdaLogLevel.ERROR, FailedMessageOrigin.SNS, snsEventBody, e); return($"ERROR: {e.Message}".ToStream()); } finally { CurrentRecord = null; } }
private string GetMessageQueueName(SNSEvent.SNSMessage sns) { return(sns.Message); }
public void Setup() { // APIGatewayProxy _baseAPIGatewayProxyRequest = new APIGatewayProxyRequest { HttpMethod = "POST", Path = "/test/path", }; _baseAPIGatewayProxyResponse = new APIGatewayProxyResponse { StatusCode = (int)HttpStatusCode.OK, }; // ApplicationLoadBalancer var albRequestContext = new ApplicationLoadBalancerRequest.ALBRequestContext { Elb = new ApplicationLoadBalancerRequest.ElbInfo() }; albRequestContext.Elb.TargetGroupArn = TestArn; _baseApplicationLoadBalancerRequest = new ApplicationLoadBalancerRequest { HttpMethod = "POST", Path = "/test/path", RequestContext = albRequestContext, }; _baseApplicationLoadBalancerResponse = new ApplicationLoadBalancerResponse { StatusCode = (int)HttpStatusCode.OK, }; // SQSEvent var sqsRecord = new SQSEvent.SQSMessage { EventSourceArn = TestArn }; _baseSQSEvent = new SQSEvent { Records = new List <SQSEvent.SQSMessage> { sqsRecord }, }; // SNSEvent var snsMessaage = new SNSEvent.SNSMessage() { Message = "Test Message", }; var snsRecord = new SNSEvent.SNSRecord { EventSubscriptionArn = TestArn, Sns = snsMessaage }; _baseSNSEvent = new SNSEvent { Records = new List <SNSEvent.SNSRecord> { snsRecord }, }; // KinesisEvent var kinesisRecord = new KinesisEvent.KinesisEventRecord { EventSourceARN = TestArn }; _baseKinesisEvent = new KinesisEvent { Records = new List <KinesisEvent.KinesisEventRecord> { kinesisRecord }, }; // S3Event var s3Record = new Amazon.S3.Util.S3EventNotification.S3EventNotificationRecord { S3 = new Amazon.S3.Util.S3EventNotification.S3Entity { Bucket = new Amazon.S3.Util.S3EventNotification.S3BucketEntity { Arn = TestArn } } }; _baseS3Event = new S3Event { Records = new List <Amazon.S3.Util.S3EventNotification.S3EventNotificationRecord> { s3Record }, }; // DynamoDBEvent var dynamoDBRecord = new DynamoDBEvent.DynamodbStreamRecord { EventSourceArn = TestArn }; _baseDynamoDBEvent = new DynamoDBEvent { Records = new List <DynamoDBEvent.DynamodbStreamRecord> { dynamoDBRecord }, }; // KinesisFirehoseEvent _baseKinesisFirehoseEvent = new KinesisFirehoseEvent { DeliveryStreamArn = TestArn, }; }