github.com/argoproj/argo-events@v1.9.1/eventsources/sources/stripe/start.go (about)

     1  /*
     2  Copyright 2018 BlackRock, Inc.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  	http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package stripe
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"fmt"
    23  	"io"
    24  	"net/http"
    25  	"time"
    26  
    27  	"github.com/stripe/stripe-go"
    28  	"github.com/stripe/stripe-go/webhookendpoint"
    29  	"go.uber.org/zap"
    30  
    31  	"github.com/argoproj/argo-events/common"
    32  	"github.com/argoproj/argo-events/common/logging"
    33  	eventsourcecommon "github.com/argoproj/argo-events/eventsources/common"
    34  	"github.com/argoproj/argo-events/eventsources/common/webhook"
    35  	"github.com/argoproj/argo-events/eventsources/sources"
    36  	apicommon "github.com/argoproj/argo-events/pkg/apis/common"
    37  	"github.com/argoproj/argo-events/pkg/apis/events"
    38  )
    39  
    40  // controller controls the webhook operations
    41  var (
    42  	controller = webhook.NewController()
    43  )
    44  
    45  // set up the activation and inactivation channels to control the state of routes.
    46  func init() {
    47  	go webhook.ProcessRouteStatus(controller)
    48  }
    49  
    50  // GetEventSourceName returns name of event source
    51  func (el *EventListener) GetEventSourceName() string {
    52  	return el.EventSourceName
    53  }
    54  
    55  // GetEventName returns name of event
    56  func (el *EventListener) GetEventName() string {
    57  	return el.EventName
    58  }
    59  
    60  // GetEventSourceType return type of event server
    61  func (el *EventListener) GetEventSourceType() apicommon.EventSourceType {
    62  	return apicommon.StripeEvent
    63  }
    64  
    65  // Implement Router
    66  // 1. GetRoute
    67  // 2. HandleRoute
    68  // 3. PostActivate
    69  // 4. PostDeactivate
    70  
    71  // GetRoute returns the route
    72  func (rc *Router) GetRoute() *webhook.Route {
    73  	return rc.route
    74  }
    75  
    76  // HandleRoute handles incoming requests on the route
    77  func (rc *Router) HandleRoute(writer http.ResponseWriter, request *http.Request) {
    78  	route := rc.route
    79  
    80  	logger := route.Logger.With(
    81  		logging.LabelEndpoint, route.Context.Endpoint,
    82  		logging.LabelPort, route.Context.Port,
    83  		logging.LabelHTTPMethod, route.Context.Method,
    84  	)
    85  
    86  	logger.Info("request a received, processing it...")
    87  
    88  	if !route.Active {
    89  		logger.Warn("endpoint is not active, won't process it")
    90  		common.SendErrorResponse(writer, "endpoint is inactive")
    91  		return
    92  	}
    93  
    94  	defer func(start time.Time) {
    95  		route.Metrics.EventProcessingDuration(route.EventSourceName, route.EventName, float64(time.Since(start)/time.Millisecond))
    96  	}(time.Now())
    97  
    98  	request.Body = http.MaxBytesReader(writer, request.Body, route.Context.GetMaxPayloadSize())
    99  	payload, err := io.ReadAll(request.Body)
   100  	if err != nil {
   101  		logger.Errorw("error reading request body", zap.Error(err))
   102  		writer.WriteHeader(http.StatusServiceUnavailable)
   103  		route.Metrics.EventProcessingFailed(route.EventSourceName, route.EventName)
   104  		return
   105  	}
   106  
   107  	var event *stripe.Event
   108  	if err := json.Unmarshal(payload, &event); err != nil {
   109  		logger.Errorw("failed to parse request body", zap.Error(err))
   110  		common.SendErrorResponse(writer, "failed to parse the event")
   111  		route.Metrics.EventProcessingFailed(route.EventSourceName, route.EventName)
   112  		return
   113  	}
   114  
   115  	if ok := filterEvent(event, rc.stripeEventSource.EventFilter); !ok {
   116  		logger.Errorw("failed to pass the filters", zap.Any("event-type", event.Type), zap.Error(err))
   117  		common.SendErrorResponse(writer, "invalid event")
   118  		route.Metrics.EventProcessingFailed(route.EventSourceName, route.EventName)
   119  		return
   120  	}
   121  
   122  	eventData := &events.StripeEventData{
   123  		Event:    event,
   124  		Metadata: rc.stripeEventSource.Metadata,
   125  	}
   126  
   127  	data, err := json.Marshal(eventData)
   128  	if err != nil {
   129  		logger.Errorw("failed to marshal event data", zap.Any("event-id", event.ID), zap.Error(err))
   130  		common.SendErrorResponse(writer, "invalid event")
   131  		route.Metrics.EventProcessingFailed(route.EventSourceName, route.EventName)
   132  		return
   133  	}
   134  
   135  	logger.Info("dispatching event on route's data channel...")
   136  	route.DataCh <- data
   137  	logger.Info("request successfully processed")
   138  	common.SendSuccessResponse(writer, "success")
   139  }
   140  
   141  // PostActivate performs operations once the route is activated and ready to consume requests
   142  func (rc *Router) PostActivate() error {
   143  	if rc.stripeEventSource.CreateWebhook {
   144  		route := rc.route
   145  		stripeEventSource := rc.stripeEventSource
   146  		logger := route.Logger.With(
   147  			logging.LabelEndpoint, route.Context.Endpoint,
   148  			logging.LabelHTTPMethod, route.Context.Method,
   149  		)
   150  		logger.Info("registering a new webhook")
   151  
   152  		apiKey, err := common.GetSecretFromVolume(stripeEventSource.APIKey)
   153  		if err != nil {
   154  			return fmt.Errorf("APIKey not found, %w", err)
   155  		}
   156  
   157  		stripe.Key = apiKey
   158  
   159  		params := &stripe.WebhookEndpointParams{
   160  			URL: stripe.String(common.FormattedURL(stripeEventSource.Webhook.URL, stripeEventSource.Webhook.Endpoint)),
   161  		}
   162  		if stripeEventSource.EventFilter != nil {
   163  			params.EnabledEvents = stripe.StringSlice(stripeEventSource.EventFilter)
   164  		}
   165  
   166  		endpoint, err := webhookendpoint.New(params)
   167  		if err != nil {
   168  			return err
   169  		}
   170  		logger.With("endpoint-id", endpoint.ID).Info("new stripe webhook endpoint created")
   171  	}
   172  	return nil
   173  }
   174  
   175  // PostInactivate performs operations after the route is inactivated
   176  func (rc *Router) PostInactivate() error {
   177  	return nil
   178  }
   179  
   180  func filterEvent(event *stripe.Event, filters []string) bool {
   181  	if filters == nil {
   182  		return true
   183  	}
   184  	for _, filter := range filters {
   185  		if event.Type == filter {
   186  			return true
   187  		}
   188  	}
   189  	return false
   190  }
   191  
   192  // StartListening starts an event source
   193  func (el *EventListener) StartListening(ctx context.Context, dispatch func([]byte, ...eventsourcecommon.Option) error) error {
   194  	log := logging.FromContext(ctx).
   195  		With(logging.LabelEventSourceType, el.GetEventSourceType(), logging.LabelEventName, el.GetEventName())
   196  	log.Info("started processing the Stripe event source...")
   197  	defer sources.Recover(el.GetEventName())
   198  
   199  	stripeEventSource := &el.StripeEventSource
   200  	route := webhook.NewRoute(stripeEventSource.Webhook, log, el.GetEventSourceName(), el.GetEventName(), el.Metrics)
   201  
   202  	return webhook.ManageRoute(ctx, &Router{
   203  		route:             route,
   204  		stripeEventSource: stripeEventSource,
   205  	}, controller, dispatch)
   206  }