Skip to content

Commit

Permalink
add aws/aws-lambda-go support
Browse files Browse the repository at this point in the history
  • Loading branch information
grokify committed Jan 29, 2018
1 parent 28d9ebe commit fb23258
Show file tree
Hide file tree
Showing 13 changed files with 265 additions and 72 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
_*
handler.so
handler.zip
server
main
main.zip
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,40 @@ Example Webhook Message from Travis CI:
$ go get github.com/grokify/chathooks
```

## Configuration

### Environment Variables

Chathooks uses two environment variables:

| Variable Name | Value |
|---------------|-------|
| `CHATHOOKS_ENGINE` | The engine to be used: `aws` for `aws/aws-lambda-go`, `nethttp` for `net/http` and `fasthttp` for `valyala/fasthttp` |
| `CHATHOOKS_TOKENS` | Comma-delimited list of verification tokens. No extra leading or trailing spaces. |

### Engines

Chathooks supports 4 server engines:

* `aws/aws-lambda-go`
* `eawsy/aws-lambda-go` (deprecated)
* `net/http`
* `valyala/fasthttp`

For `aws/aws-lambda-go`, `net/http`, `valyala/fasthttp`, you can select the engine by setting the `CHATHOOKS_ENGINE` environment variable to one of: `["aws", "nethttp", "fasthttp"]`.

#### Using the AWS Engine

##### Update Lambda Code:

You can update the Lambda funciton code using the following:

https://docs.aws.amazon.com/cli/latest/reference/lambda/update-function-code.html

`$ aws lambda update-function-code --function-name='MyFunction' --zip-file='fileb://main.zip' --publish --region='us-east-1'`

Make sure to set your AWS credentials file.

## Usage

### Starting the Service using FastHTTP
Expand Down
4 changes: 4 additions & 0 deletions aws-package.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
rm main
rm main.zip
GOOS=linux go build -o main main.go
zip main.zip main
57 changes: 37 additions & 20 deletions examples/proxy_send/proxy_send.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,19 @@ import (
"flag"
"fmt"
"io/ioutil"
"net/url"
"os"
"path"
"regexp"
"strings"

"github.com/google/go-querystring/query"
"github.com/grokify/gotilla/fmt/fmtutil"

"github.com/grokify/chathooks/src/config"
"github.com/grokify/chathooks/src/models"

"github.com/grokify/gotilla/io/ioutilmore"
"github.com/grokify/gotilla/net/httputilmore"
"github.com/grokify/gotilla/net/urlutil"
"github.com/grokify/gotilla/strings/stringsutil"
"github.com/valyala/fasthttp"
)
Expand All @@ -27,9 +30,7 @@ const (
type ExampleWebhookSender struct {
DocHandlersDir string
BaseUrl string
OutputType string
Token string
Url string
RequestParams models.RequestParams
}

func (s *ExampleWebhookSender) SendExamplesForInputType(inputType string) error {
Expand All @@ -52,20 +53,29 @@ func (s *ExampleWebhookSender) SendExamplesForInputType(inputType string) error
return nil
}

func BuildURLQueryString(baseUrl string, qry interface{}) string {
v, _ := query.Values(qry)
qryString := v.Encode()
if len(qryString) > 0 {
return baseUrl + "?" + qryString
}
return baseUrl
}

func (s *ExampleWebhookSender) SendExampleForFilepath(filepath string, inputType string) error {
bytes, err := ioutil.ReadFile(filepath)
if err != nil {
return err
}

qry := url.Values{}
qry.Add("inputType", inputType)
qry.Add("outputType", s.OutputType)
qry.Add("token", s.Token)
qry.Add("url", s.Url)

fullUrl := urlutil.BuildURL(s.BaseUrl, qry)
fmt.Println(fullUrl)
qry := models.RequestParams{
InputType: inputType,
OutputType: s.RequestParams.OutputType,
Token: s.RequestParams.Token,
URL: s.RequestParams.URL,
}
fullUrl := BuildURLQueryString(s.BaseUrl, qry)
fmt.Printf("FULL_URL: %v\n", fullUrl)

req := fasthttp.AcquireRequest()
resp := fasthttp.AcquireResponse()
Expand All @@ -79,7 +89,7 @@ func (s *ExampleWebhookSender) SendExampleForFilepath(filepath string, inputType

err = fastClient.Do(req, resp)
fmt.Printf("RES_STATUS: %v\n", resp.StatusCode())
if resp.StatusCode() > 299 {
if resp.StatusCode() >= 300 || 1 == 1 {
fmt.Printf("RES_BODY: %v\n", string(resp.Body()))
}
fasthttp.ReleaseRequest(req)
Expand All @@ -91,19 +101,26 @@ func main() {
inputTypeP := flag.String("inputType", "travisci", "Example message type")
urlP := flag.String("url", "https://hooks.glip.com/webhook/11112222-3333-4444-5555-666677778888", "Your Webhook URL")
outputTypeP := flag.String("outputType", "glip", "Adapter name")
tokenP := flag.String("token", "token", "You token")

flag.Parse()
inputTypes := strings.ToLower(strings.TrimSpace(*inputTypeP))

qry := models.RequestParams{
InputType: *inputTypeP,
OutputType: *outputTypeP,
Token: *tokenP,
URL: *urlP,
}
fmtutil.PrintJSON(qry)

sender := ExampleWebhookSender{
DocHandlersDir: config.DocsHandlersDir(),
BaseUrl: "http://localhost:8080/hooks",
OutputType: strings.ToLower(strings.TrimSpace(*outputTypeP)),
Token: "hello-world",
Url: strings.TrimSpace(*urlP),
BaseUrl: "http://localhost:8080/hook",
RequestParams: qry,
}
if len(sender.Url) == 0 {
sender.Url = os.Getenv(WebhookUrlEnvGlip)
if len(sender.RequestParams.URL) == 0 {
sender.RequestParams.URL = os.Getenv(WebhookUrlEnvGlip)
}

examples := stringsutil.SliceTrimSpace(strings.Split(inputTypes, ","))
Expand Down
48 changes: 40 additions & 8 deletions server.go → main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"fmt"
"net/http"
"os"
"strings"

"github.com/aws/aws-lambda-go/events"
Expand All @@ -14,6 +15,8 @@ import (
"github.com/grokify/gotilla/fmt/fmtutil"
fhu "github.com/grokify/gotilla/net/fasthttputil"
nhu "github.com/grokify/gotilla/net/nethttputil"
"github.com/grokify/gotilla/strings/stringsutil"
"github.com/joho/godotenv"
log "github.com/sirupsen/logrus"
"github.com/valyala/fasthttp"

Expand Down Expand Up @@ -50,21 +53,30 @@ import (
"github.com/grokify/chathooks/src/handlers/victorops"
)

/*
Use the `CHATHOOKS_TOKENS` environment variable to load secret
tokens as a comma delimited string.
*/

const (
ParamNameInput = "inputType"
ParamNameOutput = "outputType"
ParamNameURL = "url"
ParamNameToken = "token"
SecretToken = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
EnvEnvPath = "ENV_PATH"
EnvEngine = "CHATHOOKS_ENGINE" // aws, nethttp, fasthttp
EnvTokens = "CHATHOOKS_TOKENS"
)

type HandlerSet struct {
Handlers map[string]Handler
}

type Handler interface {
HandleAwsLambda(ctx context.Context, req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error)
HandleCanonical(hookData models.HookData) []models.ErrorInfo
HandleAwsLambda(ctx context.Context, req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error)
HandleEawsyLambda(event *apigatewayproxyevt.Event, ctx *runtime.Context) (events.APIGatewayProxyResponse, error)
HandleFastHTTP(ctx *fasthttp.RequestCtx)
HandleNetHTTP(res http.ResponseWriter, req *http.Request)
Expand Down Expand Up @@ -157,13 +169,19 @@ func getConfig() ServiceInfo {
"victorops": hf.InflateHandler(victorops.NewHandler()),
}}

return ServiceInfo{
svcInfo := ServiceInfo{
Config: cfgData,
AdapterSet: adapterSet,
HandlerSet: handlerSet,
RequireToken: false,
Tokens: map[string]int{SecretToken: 1},
Tokens: map[string]int{},
}
tokens := stringsutil.SplitCondenseSpace(os.Getenv(EnvTokens), ",")
for _, token := range tokens {
svcInfo.Tokens[token] = 1
}

return svcInfo
}

var serviceInfo = getConfig()
Expand Down Expand Up @@ -272,10 +290,12 @@ func (h *AnyHTTPHandler) HandleFastHTTP(ctx *fasthttp.RequestCtx) {
token := fhu.GetReqHeader(ctx, ParamNameToken)
if len(token) == 0 {
ctx.SetStatusCode(http.StatusUnauthorized)
log.Warn("E_NO_TOKEN")
return
}
if _, ok := serviceInfo.Tokens[token]; !ok {
ctx.SetStatusCode(http.StatusUnauthorized)
log.Warn("E_INCORRECT_TOKEN")
return
}
}
Expand All @@ -299,7 +319,6 @@ func serveNetHttp() {

func serveFastHttp() {
router := fasthttprouter.New()
router.GET("/", handlers.HomeHandler)
router.GET("/hook", anyHTTPHandler.HandleFastHTTP)
router.GET("/hook/", anyHTTPHandler.HandleFastHTTP)
router.POST("/hook", anyHTTPHandler.HandleFastHTTP)
Expand All @@ -313,7 +332,20 @@ func serveAwsLambda() {
}

func main() {
//serveAwsLambda()
//serveNetHttp()
serveFastHttp()
if len(strings.TrimSpace(os.Getenv(EnvEnvPath))) > 0 {
err := godotenv.Load(os.Getenv(EnvEnvPath))
if err != nil {
log.Fatal("Error loading .env file")
}
}

engine := strings.ToLower(strings.TrimSpace(os.Getenv(EnvEngine)))
switch engine {
case "aws":
serveAwsLambda()
case "nethttp":
serveNetHttp()
case "fasthttp":
serveFastHttp()
}
}
10 changes: 6 additions & 4 deletions src/adapters/adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@ func (set *AdapterSet) SendWebhooks(hookData models.HookData) []models.ErrorInfo
errs := []models.ErrorInfo{}
if len(hookData.OutputType) > 0 && len(hookData.OutputURL) > 0 {
if adapter, ok := set.Adapters[hookData.OutputType]; ok {
req, res, err := adapter.SendWebhook(hookData.OutputURL, hookData.OutputMessage)
var msg interface{}
req, res, err := adapter.SendWebhook(hookData.OutputURL, hookData.CanonicalMessage, &msg)
errs = set.procResponse(errs, req, res, err)
}
}
for _, namedAdapter := range hookData.OutputNames {
if adapter, ok := set.Adapters[namedAdapter]; ok {
req, res, err := adapter.SendMessage(hookData.OutputMessage)
var msg interface{}
req, res, err := adapter.SendMessage(hookData.CanonicalMessage, &msg)
errs = set.procResponse(errs, req, res, err)
}
}
Expand All @@ -50,8 +52,8 @@ func (set *AdapterSet) procResponse(errs []models.ErrorInfo, req *fasthttp.Reque
}

type Adapter interface {
SendWebhook(url string, message cc.Message) (*fasthttp.Request, *fasthttp.Response, error)
SendMessage(message cc.Message) (*fasthttp.Request, *fasthttp.Response, error)
SendWebhook(url string, message cc.Message, finalMsg interface{}) (*fasthttp.Request, *fasthttp.Response, error)
SendMessage(message cc.Message, finalMsg interface{}) (*fasthttp.Request, *fasthttp.Response, error)
WebhookUID(ctx *fasthttp.RequestCtx) (string, error)
}

Expand Down
7 changes: 4 additions & 3 deletions src/adapters/glip.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@ func NewGlipAdapter(webhookURLOrUID string) (*GlipAdapter, error) {
CommonConverter: converter}, err
}

func (adapter *GlipAdapter) SendWebhook(urlOrUid string, message cc.Message) (*fasthttp.Request, *fasthttp.Response, error) {
func (adapter *GlipAdapter) SendWebhook(urlOrUid string, message cc.Message, glipmsg interface{}) (*fasthttp.Request, *fasthttp.Response, error) {
glipMessage := adapter.CommonConverter.ConvertCommonMessage(message)
glipmsg = &glipMessage

glipMessageString, err := json.Marshal(glipMessage)
if err == nil {
Expand All @@ -51,8 +52,8 @@ func (adapter *GlipAdapter) SendWebhook(urlOrUid string, message cc.Message) (*f
return adapter.GlipClient.PostWebhookGUIDFast(urlOrUid, glipMessage)
}

func (adapter *GlipAdapter) SendMessage(message cc.Message) (*fasthttp.Request, *fasthttp.Response, error) {
return adapter.SendWebhook(adapter.WebhookURLOrUID, message)
func (adapter *GlipAdapter) SendMessage(message cc.Message, glipmsg interface{}) (*fasthttp.Request, *fasthttp.Response, error) {
return adapter.SendWebhook(adapter.WebhookURLOrUID, message, glipmsg)
}

func (adapter *GlipAdapter) WebhookUID(ctx *fasthttp.RequestCtx) (string, error) {
Expand Down
10 changes: 6 additions & 4 deletions src/adapters/slack.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,14 @@ func NewSlackAdapter(webhookURLOrUID string) (*SlackAdapter, error) {
WebhookURLOrUID: webhookURLOrUID}, err
}

func (adapter *SlackAdapter) SendWebhook(urlOrUid string, message commonchat.Message) (*fasthttp.Request, *fasthttp.Response, error) {
return adapter.SlackClient.PostWebhookFast(urlOrUid, slack.ConvertCommonMessage(message))
func (adapter *SlackAdapter) SendWebhook(urlOrUid string, message commonchat.Message, slackmsg interface{}) (*fasthttp.Request, *fasthttp.Response, error) {
slackMessage := slack.ConvertCommonMessage(message)
slackmsg = &slackMessage
return adapter.SlackClient.PostWebhookFast(urlOrUid, slackMessage)
}

func (adapter *SlackAdapter) SendMessage(message commonchat.Message) (*fasthttp.Request, *fasthttp.Response, error) {
return adapter.SendWebhook(adapter.WebhookURLOrUID, message)
func (adapter *SlackAdapter) SendMessage(message commonchat.Message, slackmsg interface{}) (*fasthttp.Request, *fasthttp.Response, error) {
return adapter.SendWebhook(adapter.WebhookURLOrUID, message, slackmsg)
}

func (adapter *SlackAdapter) WebhookUID(ctx *fasthttp.RequestCtx) (string, error) {
Expand Down
14 changes: 8 additions & 6 deletions src/handlers/aha/examples/decode_html/decode_aha_html.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

func main() {
data := []byte(`{"event":"audit","audit":{"id":"1011112222333344445555666"}}`)
data = []byte(`{"event":"audit","audit":{"id":"6514922586902400152","audit_action":"update","created_at":"2018-01-25T09:46:59.257Z","interesting":true,"user":{"id":"6355516420883588191","name":"John Wang","email":"[email protected]","created_at":"2016-11-21T20:09:39.022Z","updated_at":"2018-01-24T23:50:12.097Z"},"auditable_type":"note","auditable_id":"6499593214164077189","description":"updated feature RCGG-112 User feedback collection","auditable_url":"https://ringcentral.aha.io/features/RCGG-112","changes":[{"field_name":"Description","value":"\u003cp\u003eBesides Google Chrome store reviews, we should provide user way to submit their feedback from within our Google app\u003c/p\u003e\u003cp\u003e\u003cb\u003eRequirement\u003c/b\u003e\u003c/p\u003e\u003col\u003e\n\u003cli\u003eThere should be a menu item 'Feedback' on Settings page\u003c/li\u003e\n\u003cli\u003eWhen user clicks 'Feedback', user shall be navigated to a new page with following content\u003cblockquote\u003e\n\u003cp\u003e\u003cb\u003eContact Customer Support\u003c/b\u003e\u003cbr\u003e Your feedback is valuable for us. If you have problems using the app, want to request a feature, or report a bug, we’re more than happy to help. Please fill in the form below and click \u003ci\u003eSend Your Feedback\u003c/i\u003e, or directly use your mailbox and send your request to [email protected].\u003c/p\u003e\n\u003cp\u003eYour email address (so we can reply to you)\u003cbr\u003e [Input box: [email protected]]\u003c/p\u003e\n\u003cp\u003eFeedback topic\u003cbr\u003e [Dropdown: Please select an option]\u003c/p\u003e\n\u003cp\u003eSubject\u003cbr\u003e [Input box: Let us know how we can help you]\u003c/p\u003e\n\u003cp\u003eFull description\u003cbr\u003e [Input box: Please include as much information as possible]\u003c/p\u003e\n\u003cp\u003e\u003cstrike\u003eAttachment\u003c/strike\u003e \u003cbr\u003e \u003cstrike\u003e[Drag and drop area]\u003c/strike\u003e\u003c/p\u003e\n\u003cp\u003e[Button: Send Your Feedback]\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003c/li\u003e\n\u003cli\u003eFeedback topic options in the dropdown list: Please select an option (default) | Bug report | Feature request | Others\u003c/li\u003e\n\u003cli\u003eThere should be back icon on the page by clicking which user can be navigated back to Settings page.\u003c/li\u003e\n\u003cli\u003eWe should leverage an Email server and send the feedback including all the information/\u003cstrike\u003eattachment\u003c/strike\u003e user submitted to team alias \u003ci\[email protected]\u003c/i\u003e with title 'Google User Feedback'.\u003cul\u003e\u003cli\u003eEmail content example\u003cblockquote\u003e\n\u003cp\u003eHi Integration Team,\u003c/p\u003e\n\u003cp\u003eYou've got feedback from customer on RingCentral for Google extension. This customer could be contacted via email [customer's email address].\u003c/p\u003e\n\u003cp\u003e\u003cb\u003eCustomer Feedback Topic\u003c/b\u003e\u003cbr\u003e *****\u003c/p\u003e\n\u003cp\u003e\u003cb\u003eSubject\u003c/b\u003e\u003cbr\u003e ******\u003c/p\u003e\n\u003cp\u003e\u003cb\u003eDescription\u003c/b\u003e\u003cbr\u003e ********\u003c/p\u003e\n\u003cp\u003eRegards,\u003cbr\u003e RingCentral for Google Extension\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003c/li\u003e\u003c/ul\u003e\n\u003c/li\u003e\n\u003c/ol\u003e\u003cp\u003ePlease note that the feedback email should support rebranding, to avoid us using wrong words when reaching back to the customers.\u003c/p\u003e"}]}}`)

msg, err := aha.AhaOutMessageFromBytes(data)
if err != nil {
Expand All @@ -19,13 +20,14 @@ func main() {
val := msg.Audit.Changes[0].Value
fmt.Println(val)

fmt.Println("---")
val2 := html.UnescapeString(val)
fmt.Println(val2)

fmt.Println("---")
//p := bluemonday.UGCPolicy()
p := bluemonday.StrictPolicy()
val3 := p.Sanitize(val2)
fmt.Println(val3)
val = p.Sanitize(val)
fmt.Println(val)

fmt.Println("===")
val = html.UnescapeString(val)
fmt.Println(val)

}
Loading

0 comments on commit fb23258

Please sign in to comment.