I deployed REST API into AWS lambda function. I am trying to use API Gateway GET method with lambda function to get a single value from MySQL database. I want to pass id in the URL. Here is my handler function
package main
import (
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
)
type User struct{
Id int `json:"id"`
Name string `json:"name"`
}
type Ids struct{
Id int `json:"id"`
}
//func handler(id Ids) (*User, error)
//id1 := id.Id
func handler( id events.APIGatewayProxyRequest) (*User, error) {
id1 := id.Body
user := &User{
Id: id1,
Name : "abc"
}
return user, nil
}
func main(){
lambda.Start(handler)
}
I am getting result while I create test event in lambda function with the test for handler(events.APIGatewayProxyRequest)
{
"body":123
}
or for handler(id Ids)
{
"id":123
}
When I use API Gateway to create GET method for the above lambda function, I am getting "internal server error" as it is giving null value to read database. I use the URL https://xxxx.xxxxx.amazonaws.com/test/user/123, but it is not reading the value of id. What am I doing wrong here? Should I need to pass the JSON for the handler function? If yes, how to pass JSON in the URL? or is there any other method where I can pass the value to handler?
Related
I am trying to use a workflow as aws appsync-> lambda1-> athena-> SNS-> lambda2
I want to understand how can I use two different lambda resolvers in the appsync api. So while calling a get task function I am passing customer_id as an input to lambda1 and it is supposed to invoke athena. Once the query is successful SNS will publish a message and lambda 2 is invoked to display the result. Here is how my schema looks like:
type Query {
getTask(cust_id: String!): String
}
type Task {
cust_id: String!
description: String!
}
schema {
query: Query
}
I have created REST API using Go Iris framework. Now I want to deploy these API's on AWS with lambda function. I am using MySQL as database. Is it possible to deploy my Go executable file on AWS lambda or should I need to modify my code according to AWS lambda specifications? I am trying to find the solution, but not getting much information.
Here is one of my API end point.
package main
import (
"database/sql"
"github.com/kataras/iris"
"github.com/kataras/iris/middleware/logger"
"github.com/kataras/iris/middleware/recover"
)
type Reward struct {
Id int `json:"reward_id"`
LotteryID int `json:"lottery_id"`
RewardName string `json:"reward_name"`
Description string `json:"reward_description"`
Asset int `json:"reward_asset"`
AssetName string `json:"reward_asset_name"`
}
func dbConn() (db *sql.DB) {
dbDriver := "mysql"
dbUser := "xxx"
dbPass := "xxx"
dbName := "xxx"
db, err := sql.Open(xxxxxxxxx)
if err != nil {
panic(err.Error())
}
return db
}
func newApp() *iris.Application {
app := iris.New()
app.Logger().SetLevel("debug")
app.Use(recover.New())
app.Use(logger.New())
db := dbConn()
app.Get("/reward/{reward_id:int}", func(ctx iris.Context) {
id1 := ctx.Params().GetIntDefault("reward_id", 0)
stmtOut, err := db.Prepare("select id, lottery_id,reward_name,reward_description,reward_asset, reward_asset_name from rewards_table where id =?")
if err != nil {
panic(err.Error())
}
defer stmtOut.Close()
var id, lotteryId, rewardAsset int
var rewardName, rewardDescription, rewardAssetName string
err1 := stmtOut.QueryRow(id1).Scan(&id, &lotteryId, &rewardName, &rewardDescription, &rewardAsset, &rewardAssetName)
if err1 != nil {
panic(err.Error())
}
reward := Reward{
Id: id,
LotteryID: lotteryId,
RewardName: rewardName,
Description: rewardDescription,
Asset: rewardAsset,
AssetName: rewardAssetName,
}
ctx.JSON(&reward)
})
return app
}
func main() {
app := newApp()
app.Run(iris.Addr(":8080"), iris.WithoutServerError(iris.ErrServerClosed), iris.WithOptimizations)
}
I have few more API endpoints which do basic CRUD operations. I am thinking about using AWS lambda and AWS API Gateway.
should I need to modify my code according to AWS lambda specifications?
Yes. Your code for lambda will require to have a handler:
AWS Lambda function handler in Go
This is the entry point to your function.
Also it seems that your go program is a web server build on iris. If this is the case, you won't be able to use it anyway, as you can't invoke lambda from internet as you would a regular server.
Also lambda runs for max 15 minutes, thus its use as a server would be very limited.
I am trying to achieve the following:
Cloudwatch alarm details are received as JSON to a Lambda
The Lambda looks at the JSON to determine if the 'NewStateValue' == "ALARM"
If it does == "ALARM" forward the whole JSON received from the SNS out via another SNS.
I am most of the way towards achieving this and I have the following code:
package main
import (
"context"
"fmt"
"encoding/json"
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/sns"
)
func handler(ctx context.Context, snsEvent events.SNSEvent) {
for _, record := range snsEvent.Records {
snsRecord := record.SNS
//To add in additional fields to publish to the logs add "snsRecord.'fieldname'"
fmt.Printf("Message = %s \n", snsRecord.Message)
var event CloudWatchAlarm
err := json.Unmarshal([]byte(snsRecord.Message), &event)
if err != nil {
fmt.Println("There is an error: " + err.Error())
}
fmt.Printf("Test Message = %s \n", event.NewStateValue)
if ( event.NewStateValue == "ALARM") {
svc := sns.New(session.New())
// params will be sent to the publish call included here is the bare minimum params to send a message.
params := &sns.PublishInput{
Message: Message: aws.String("message"), // This is the message itself (can be XML / JSON / Text - anything you want)
TopicArn: aws.String("my arn"), //Get this from the Topic in the AWS console.
}
resp, err := svc.Publish(params) //Call to puclish the message
if err != nil { //Check for errors
// Print the error, cast err to awserr.Error to get the Code and
// Message from an error.
fmt.Println(err.Error())
return
}
// Pretty-print the response data.
fmt.Println(resp)
}
}
}
func main() {
lambda.Start(handler)
}
Currently this sends an email to the address set-up in the SNS linked to the ARN above. However I would like the email to include the full, ideally formatted, JSON received by the first SNS. I have the Cloudwatch JSON structure defined in another file, this is being called by var event CloudWatchAlarm
From AWS SDK for Go docs:
type PublishInput struct {
// The message you want to send.
//
// If you are publishing to a topic and you want to send the same message to
// all transport protocols, include the text of the message as a String value.
// If you want to send different messages for each transport protocol, set the
// value of the MessageStructure parameter to json and use a JSON object for
// the Message parameter.
So, params would be:
params := &sns.PublishInput{
Message: myjson, // json data you want to send
TopicArn: aws.String("my arn"),
MessageStructure: aws.String("json")
}
WARNING
Notice the help paragraph says "use a JSON object for the parameter message", such JSON object must have a keys that correspond to supported transport protocol That means
{
'default': json_message,
'email': json_message
}
It will send json_message using both the default and email transports.
When using a Lambda Function to trigger a Step Function, how do you get the output of the function that triggered it?
Ok, so if you want to pass an input to a Step Function execution (or, more exactly, your 'State Machine''s execution), you just need to set said input the input property when calling StartExecution (see AWS Documentation: Start Execution)
In your case, it would most likely be your lambda's last step before calling it's callback.
If it's a node js lambda, that's what it would look like
const AWS = require("aws-sdk");
const stepfunctions = new AWS.StepFunctions();
exports.myHandler = function(event, context, callback) {
... your function's code
const params = {
stateMachineArn: 'YOUR_STATE_MACHINE_ARN', /* required */
input: 'STRINGIFIED INPUT',
name: 'AN EXECUTION NAME (such as an uuid or whatever)'
};
stepfunctions.startExecution(params, function(err, data) {
if (err) callback(err); // an error occurred
else callback(null, "some success message"); // successful response
});
}
Alternatively, if your payload is too big, you could store the data in S3 or DynamoDB and pass the reference to it as your State Machine's execution's input.
I am building an AWS Lambda function in Golang that copy the content from n to m S3 buckets. There is a requirement to support for S3 trigger as well as fetching the data from an SQS where all source S3 bucket change is stored. The code can be found here: https://github.com/maknahar/s3copy
I tried following:
func main() {
lambda.Start(ProcessIncomingS3Events)
lambda.Start(ProcessIncomingEvents)
}
func ProcessIncomingS3Events(event events.S3Event) error {
...
log.Println("Got S3 Event")
return processS3Trigger(config, event)
}
func ProcessIncomingEvents() error {
...
log.Println("Defaulting to SQS")
return processSQSMessage(config)
}
In this case, the first event ProcessIncomingS3Events is triggered every time.
I tried following as well
func main() {
lambda.Start(ProcessIncomingEvents)
}
func ProcessIncomingEvents(event interface{}) error {
...
switch request := event.(type) {
case events.S3Event:
log.Println("Got S3 Event")
return processS3Trigger(config, request)
case types.Nil:
log.Println("Defaulting to SQS")
return processSQSMessage(config)
default:
log.Println("Could not find the event type")
}
return nil
}
In this case, Lambda could not detect the type and Could not find the event type is logged in every trigger.
Is there a way to support multiple triggers via AWS SDK at all for the function?
I achieved to listen to multiple events by implementing the aws Handler interface, it defines one method
Invoke(ctx context.Context, payload []byte) ([]byte, error)
I implemented a multievent Handler as follows
type Handler struct {
//add global variables or context information that your handler may need
}
func (h Handler) Invoke(ctx context.Context, data []byte) ([]byte, error) {
//for demonstration purposes, not the best way to handle
apiGatewayEvent := new(events.APIGatewayProxyRequest)
if err := json.Unmarshal(data, apiGatewayEvent); err != nil {
log.Println("Not a api gateway event")
}
snsEvent := new(events.SNSEvent)
if err := json.Unmarshal(data, snsEvent); err != nil {
log.Println("Not a sns event")
}
return nil, nil
}
func main() {
lambda.StartHandler(Handler{})
}
As you can see, you could get the raw bytes of any event and handle them as you need giving you the possibility to listen to any aws event with the same lambda. However, think carefully before using this aproach, because, as noted above, lambdas are best used handling just one type of event
Hope this helps.
You can configure multiple event sources to trigger one or more Lambda functions.
However, in Go the lambda.Start call is blocking so it's not very easy to write a single function that handles multiple event types. You are strongly encouraged to make a separate Lambda function for every event source.
The idiomatic Go solution is to have your function logic defined once in the main package, and write multiple programs that take the source event and call your function. So the project layout would be:
s3copy/main.go
s3copy/handlers/s3/main.go
s3copy/handlers/sqs/main.go
See my boilerplate Lambda and Go app for an example project layout and Makefile.
I'm not a GoLang guy. Just guessing on normal programming thinking.
In approach one, you are directly calling ProcessIncomingS3Events in first statement, so every time this is called.
Read this - Lambda Function Handler (Go)
In above link, the author is parsing event's name field. Similarly, you can check for any field which is always present in S3 event e.g. "eventSource":"aws:s3" (S3 event structure see here)
If present then S3 event else other. Or you can also check for SQS event's field.
HIH
You can use embedding in Go to solve this
import (
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambda"
"reflect"
)
type Event struct {
events.SQSEvent
events.APIGatewayProxyRequest
//other event type
}
type Response struct {
events.SQSEventResponse `json:",omitempty"`
events.APIGatewayProxyResponse `json:",omitempty"`
//other response type
}
func main() {
lambda.Start(eventRouter)
}
func eventRouter(event Event) (Response, error) {
var response Response
switch {
case reflect.DeepEqual(event.APIGatewayProxyRequest, events.APIGatewayProxyRequest{}):
response.SQSEventResponse = sqsEventHandler(event.SQSEvent)
case reflect.DeepEqual(event.SQSEvent, events.SQSEvent{}):
response.APIGatewayProxyResponse = apiGatewayEventHandler(event.APIGatewayProxyRequest)
//another case for a event handler
}
return response, nil
}
func sqsEventHandler(sqsEvent events.SQSEvent) events.SQSEventResponse {
//do something with the SQS event
}
func apiGatewayEventHandler(apiEvent events.APIGatewayProxyRequest) events.APIGatewayProxyResponse {
//do something with the API Gateway event
}
Note: if the basic events have some same field names, you will need to look for another the compare method instance of DeepEqual.
Note2: sorry for my english, greetings from Argentina