diff --git a/events/README.md b/events/README.md index 2eb4b4c7..d1a50f92 100644 --- a/events/README.md +++ b/events/README.md @@ -14,6 +14,8 @@ This package provides input types for Lambda functions that process AWS events. [CloudFormation Events](../cfn/README.md) +[Chime Bot Events](README_Chime_Bots.md) + [Code Commit Events](README_CodeCommit.md) [Cognito Events](README_Cognito.md) diff --git a/events/README_Chime_Bots.md b/events/README_Chime_Bots.md new file mode 100644 index 00000000..d5d933d8 --- /dev/null +++ b/events/README_Chime_Bots.md @@ -0,0 +1,67 @@ +# Sample Function + +The following is a sample class and Lambda function that receives a Amazon Chime Bot event and handles the various event types accordingly. + +```go + +package main + +import ( + "fmt" + "context" + "net/http" + "bytes" + "encoding/json" + "errors" + "strconv" + + "github.com/aws/aws-lambda-go/events" +) + +func handler(_ context.Context, chimeBotEvent events.ChimeBotEvent) error { + switch chimeBotEvent.EventType { + case "Invite": + if err := message(chimeBotEvent.InboundHTTPSEndpoint.URL, "Thanks for inviting me to this room " + chimeBotEvent.Sender.SenderID); err != nil { + return fmt.Errorf("failed to send webhook message: %v", err) + } + return nil + case "Mention": + if err := message(chimeBotEvent.InboundHTTPSEndpoint.URL, "Thanks for mentioning me " + chimeBotEvent.Sender.SenderID); err != nil { + return fmt.Errorf("failed to send webhook message: %v", err) + } + return nil + case "Remove": + fmt.Printf("I have been removed from %q by %q", chimeBotEvent.Discussion.DiscussionType, chimeBotEvent.Sender.SenderID) + return nil + default: + return fmt.Errorf("event type %q is unsupported", chimeBotEvent.EventType) + } +} + +func message(url, content string) (error) { + input := &bytes.Buffer{} + if err := json.NewEncoder(input).Encode(webhookInput{Content:content}); err != nil { + return errors.New("failed to marshal request: " + err.Error()) + } + + resp, err := http.Post("POST", url, input) + if err != nil { + return errors.New("failed to execute post http request: " + err.Error()) + } + + if resp != nil && resp.Body != nil { + defer resp.Body.Close() + } + + if resp.StatusCode != http.StatusOK { + return errors.New("bad response: status code not is " + strconv.Itoa(http.StatusOK) + " not " + strconv.Itoa(resp.StatusCode)) + } + + return nil +} + +type webhookInput struct { + Content string `json:"Content"` +} + +``` diff --git a/events/chime_bot.go b/events/chime_bot.go new file mode 100644 index 00000000..fa08f003 --- /dev/null +++ b/events/chime_bot.go @@ -0,0 +1,31 @@ +// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +package events + +import ( + "time" +) + +type ChimeBotEvent struct { + Sender ChimeBotEventSender `json:"Sender"` + Discussion ChimeBotEventDiscussion `json:"Discussion"` + EventType string `json:"EventType"` + InboundHTTPSEndpoint *ChimeBotEventInboundHTTPSEndpoint `json:"InboundHttpsEndpoint,omitempty"` + EventTimestamp time.Time `json:"EventTimestamp"` + Message string `json:"Message,omitempty"` +} + +type ChimeBotEventSender struct { + SenderID string `json:"SenderId"` + SenderIDType string `json:"SenderIdType"` +} + +type ChimeBotEventDiscussion struct { + DiscussionID string `json:"DiscussionId"` + DiscussionType string `json:"DiscussionType"` +} + +type ChimeBotEventInboundHTTPSEndpoint struct { + EndpointType string `json:"EndpointType"` + URL string `json:"Url"` +} diff --git a/events/chime_bot_test.go b/events/chime_bot_test.go new file mode 100644 index 00000000..18598ba9 --- /dev/null +++ b/events/chime_bot_test.go @@ -0,0 +1,138 @@ +// Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. +package events + +import ( + "encoding/json" + "testing" + + "github.com/aws/aws-lambda-go/events/test" + "github.com/stretchr/testify/assert" + "time" +) + +func TestChimeBotEventMarshaling(t *testing.T) { + // From https://docs.aws.amazon.com/chime/latest/ag/manage-chat-bots.html + tests := map[string]struct { + inputJSON string + expectedEvent ChimeBotEvent + }{ + "Example Invite Event": { + inputJSON: ` { + "Sender": { + "SenderId": "user@example.com", + "SenderIdType": "EmailId" + }, + "Discussion": { + "DiscussionId": "abcdef12-g34h-56i7-j8kl-mn9opqr012st", + "DiscussionType": "Room" + }, + "EventType": "Invite", + "InboundHttpsEndpoint": { + "EndpointType": "Persistent", + "Url": "https://hooks.a.chime.aws/incomingwebhooks/a1b2c34d-5678-90e1-f23g-h45i67j8901k?token=ABCDefGHiJK1LMnoP2Q3RST4uvwxYZAbC56DeFghIJkLM7N8OP9QRsTuV0WXYZABcdefgHiJ" + }, + "EventTimestamp": "2019-04-04T21:27:52.736Z" +}`, + expectedEvent: ChimeBotEvent{ + Sender: ChimeBotEventSender{ + SenderID: "user@example.com", + SenderIDType: "EmailId", + }, + Discussion: ChimeBotEventDiscussion{ + DiscussionID: "abcdef12-g34h-56i7-j8kl-mn9opqr012st", + DiscussionType: "Room", + }, + EventType: "Invite", + InboundHTTPSEndpoint: &ChimeBotEventInboundHTTPSEndpoint{ + EndpointType: "Persistent", + URL: "https://hooks.a.chime.aws/incomingwebhooks/a1b2c34d-5678-90e1-f23g-h45i67j8901k?token=ABCDefGHiJK1LMnoP2Q3RST4uvwxYZAbC56DeFghIJkLM7N8OP9QRsTuV0WXYZABcdefgHiJ", + }, + EventTimestamp: time.Date(2019, 04, 04, 21, 27, 52, 736000000, time.UTC), + Message: "", + }, + }, + "Example Mention Event": { + inputJSON: `{ + "Sender": { + "SenderId": "user@example.com", + "SenderIdType": "EmailId" + }, + "Discussion": { + "DiscussionId": "abcdef12-g34h-56i7-j8kl-mn9opqr012st", + "DiscussionType": "Room" + }, + "EventType": "Mention", + "InboundHttpsEndpoint": { + "EndpointType": "ShortLived", + "Url": "https://hooks.a.chime.aws/incomingwebhooks/a1b2c34d-5678-90e1-f23g-h45i67j8901k?token=ABCDefGHiJK1LMnoP2Q3RST4uvwxYZAbC56DeFghIJkLM7N8OP9QRsTuV0WXYZABcdefgHiJ" + }, + "EventTimestamp": "2019-04-04T21:30:43.181Z", + "Message": "@botDisplayName@example.com Hello Chatbot" +}`, + expectedEvent: ChimeBotEvent{ + Sender: ChimeBotEventSender{ + SenderID: "user@example.com", + SenderIDType: "EmailId", + }, + Discussion: ChimeBotEventDiscussion{ + DiscussionID: "abcdef12-g34h-56i7-j8kl-mn9opqr012st", + DiscussionType: "Room", + }, + EventType: "Mention", + InboundHTTPSEndpoint: &ChimeBotEventInboundHTTPSEndpoint{ + EndpointType: "ShortLived", + URL: "https://hooks.a.chime.aws/incomingwebhooks/a1b2c34d-5678-90e1-f23g-h45i67j8901k?token=ABCDefGHiJK1LMnoP2Q3RST4uvwxYZAbC56DeFghIJkLM7N8OP9QRsTuV0WXYZABcdefgHiJ", + }, + EventTimestamp: time.Date(2019, 04, 04, 21, 30, 43, 181000000, time.UTC), + Message: "@botDisplayName@example.com Hello Chatbot", + }, + }, + "Example Remove Event": { + inputJSON: `{ + "Sender": { + "SenderId": "user@example.com", + "SenderIdType": "EmailId" + }, + "Discussion": { + "DiscussionId": "abcdef12-g34h-56i7-j8kl-mn9opqr012st", + "DiscussionType": "Room" + }, + "EventType": "Remove", + "EventTimestamp": "2019-04-04T21:27:29.626Z" +}`, + expectedEvent: ChimeBotEvent{ + Sender: ChimeBotEventSender{ + SenderID: "user@example.com", + SenderIDType: "EmailId", + }, + Discussion: ChimeBotEventDiscussion{ + DiscussionID: "abcdef12-g34h-56i7-j8kl-mn9opqr012st", + DiscussionType: "Room", + }, + EventType: "Remove", + EventTimestamp: time.Date(2019, 04, 04, 21, 27, 29, 626000000, time.UTC), + }, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + var testEvent ChimeBotEvent + if err := json.Unmarshal([]byte(test.inputJSON), &testEvent); err != nil { + t.Errorf("could not unmarshal event. details: %v", err) + } + + assert.Equal(t, testEvent, test.expectedEvent) + + outputJSON, err := json.Marshal(testEvent) + if err != nil { + t.Errorf("could not marshal event. details: %v", err) + } + assert.JSONEq(t, test.inputJSON, string(outputJSON)) + }) + } +} + +func TestChimeBotMarshalingMalformedJSON(t *testing.T) { + test.TestMalformedJson(t, ChimeBotEvent{}) +}