/handler.go

https://bitbucket.org/mstenius/lambda-api · Go · 179 lines · 148 code · 26 blank · 5 comment · 27 complexity · b8622ff1c983e6caacaf2310cb32cbba MD5 · raw file

  1. package api
  2. import (
  3. "errors"
  4. "fmt"
  5. "runtime/debug"
  6. "strings"
  7. "regexp"
  8. "bitbucket.org/mstenius/logger"
  9. )
  10. const (
  11. eventSourceDynamoDB = "aws:dynamodb"
  12. eventSourceS3 = "aws:s3"
  13. eventSourceSNS = "aws:sns"
  14. )
  15. // Handler ...
  16. type Handler struct {
  17. config *HandlerConfig
  18. event map[string]interface{}
  19. }
  20. // HandlerConfig for event handlers
  21. type HandlerConfig struct {
  22. HTTP map[string]map[string]func(i *Input) *Response
  23. Scheduled map[string]func() *Response
  24. Stream map[string]func(i *StreamInput) *Response
  25. S3 map[string]func(i *S3Input) *Response
  26. SNS map[string]func(i *SNSInput) *Response
  27. }
  28. // NewHandler initialization for Handler
  29. func NewHandler(config *HandlerConfig) *Handler {
  30. return &Handler{config: config}
  31. }
  32. // Invoke correct handler based in mapped handler config and incoming event
  33. func (h *Handler) Invoke(event interface{}) (*Response, error) {
  34. h.event = event.(map[string]interface{})
  35. logger.WithFields(logger.Fields{
  36. "event": event,
  37. }).Info("Incoming event")
  38. defer h.logPanic()
  39. var response *Response
  40. var err error
  41. switch true {
  42. case h.isHTTPEvent():
  43. response, err = h.handleHTTPEvent()
  44. break
  45. case h.isScheduledEvent():
  46. response, err = h.handleScheduledEvent()
  47. break
  48. case h.isStreamEvent():
  49. response, err = h.handleStreamEvent()
  50. break
  51. case h.isS3Event():
  52. response, err = h.handleS3Event()
  53. break
  54. case h.isSNSEvent():
  55. response, err = h.handleSNSEvent()
  56. break
  57. default:
  58. response, err = nil, errors.New("unknown event")
  59. }
  60. return response, err
  61. }
  62. func (h *Handler) isHTTPEvent() bool {
  63. if _, ok := h.event["httpMethod"]; ok {
  64. return true
  65. }
  66. return false
  67. }
  68. func (h *Handler) isScheduledEvent() bool {
  69. return h.event["type"] == "schedule"
  70. }
  71. func (h *Handler) isStreamEvent() bool {
  72. if v, ok := h.event["Records"].([]interface{}); ok && len(v) > 0 {
  73. return v[0].(map[string]interface{})["eventSource"] == eventSourceDynamoDB
  74. }
  75. return false
  76. }
  77. func (h *Handler) isS3Event() bool {
  78. if v, ok := h.event["Records"].([]interface{}); ok && len(v) > 0 {
  79. return v[0].(map[string]interface{})["eventSource"] == eventSourceS3
  80. }
  81. return false
  82. }
  83. func (h *Handler) isSNSEvent() bool {
  84. if v, ok := h.event["Records"].([]interface{}); ok && len(v) > 0 {
  85. return v[0].(map[string]interface{})["EventSource"] == eventSourceSNS
  86. }
  87. return false
  88. }
  89. func (h *Handler) handleHTTPEvent() (*Response, error) {
  90. pathParams, ok := h.event["pathParameters"]
  91. resource := h.event["resource"].(string)
  92. method := h.event["httpMethod"].(string)
  93. if ok && pathParams != nil {
  94. for k, v := range pathParams.(map[string]interface{}) {
  95. resource = strings.Replace(resource, v.(string), fmt.Sprintf("{%s}", k), 1)
  96. }
  97. }
  98. handler, found := h.config.HTTP[resource][method]
  99. if !found {
  100. return nil, errors.New("handler func missing")
  101. }
  102. return &*handler(&Input{event: h.event}), nil
  103. }
  104. func (h *Handler) handleScheduledEvent() (*Response, error) {
  105. resource := h.event["resource"].(string)
  106. handler, found := h.config.Scheduled[resource]
  107. if !found {
  108. return nil, errors.New("handler func missing")
  109. }
  110. return &*handler(), nil
  111. }
  112. func (h *Handler) handleStreamEvent() (*Response, error) {
  113. record := h.event["Records"].([]interface{})[0]
  114. streamArn := record.(map[string]interface{})["eventSourceARN"].(string)
  115. handler, ok := h.config.Stream[streamArn]
  116. if !ok {
  117. return nil, errors.New("handler func missing")
  118. }
  119. return &*handler(&StreamInput{event: h.event}), nil
  120. }
  121. func (h *Handler) handleS3Event() (*Response, error) {
  122. record := h.event["Records"].([]interface{})[0].(map[string]interface{})
  123. key := record["s3"].(map[string]interface{})["object"].(map[string]interface{})["key"].(string)
  124. re := regexp.MustCompile("[^/]+$")
  125. folder := re.ReplaceAllString(key, "")
  126. if folder == "" {
  127. // If object is in root we want to look to /
  128. folder = "/"
  129. } else {
  130. folder = strings.TrimSuffix(folder, "/")
  131. }
  132. handler, ok := h.config.S3[folder]
  133. if !ok {
  134. return nil, errors.New("handler func missing")
  135. }
  136. return &*handler(&S3Input{event: h.event}), nil
  137. }
  138. func (h *Handler) handleSNSEvent() (*Response, error) {
  139. record := h.event["Records"].([]interface{})[0].(map[string]interface{})
  140. handler, ok := h.config.SNS[record["Sns"].(map[string]interface{})["TopicArn"].(string)]
  141. if !ok {
  142. return nil, errors.New("handler func missing")
  143. }
  144. return &*handler(&SNSInput{event: h.event}), nil
  145. }
  146. func (h *Handler) logPanic() {
  147. if r := recover(); r != nil {
  148. logger.WithFields(logger.Fields{
  149. "error": r,
  150. "stack": string(debug.Stack()),
  151. }).Error("Unexpected panic")
  152. }
  153. }