diff --git a/api/ifaces/app.go b/api/ifaces/app.go deleted file mode 100644 index f2a9bffb0..000000000 --- a/api/ifaces/app.go +++ /dev/null @@ -1,15 +0,0 @@ -package ifaces - -import "net/http" - -type App interface { - Name() string - Routes() Route - Validate() error -} - -type Route interface { - Path() string - Image() string - Headers() http.Header -} diff --git a/api/ifaces/listeners.go b/api/ifaces/listeners.go deleted file mode 100644 index dfd27d188..000000000 --- a/api/ifaces/listeners.go +++ /dev/null @@ -1,28 +0,0 @@ -package ifaces - -import ( - "context" - - "github.com/iron-io/functions/api/models" -) - -type AppCreateListener interface { - // BeforeAppCreate called right before creating App in the database - BeforeAppCreate(ctx context.Context, app *models.App) error - // AfterAppCreate called after creating App in the database - AfterAppCreate(ctx context.Context, app *models.App) error -} - -type AppUpdateListener interface { - // BeforeAppUpdate called right before updating App in the database - BeforeAppUpdate(ctx context.Context, app *models.App) error - // AfterAppUpdate called after updating App in the database - AfterAppUpdate(ctx context.Context, app *models.App) error -} - -type AppDeleteListener interface { - // BeforeAppDelete called right before deleting App in the database - BeforeAppDelete(ctx context.Context, appName string) error - // AfterAppDelete called after deleting App in the database - AfterAppDelete(ctx context.Context, appName string) error -} diff --git a/api/server/app_listeners.go b/api/server/app_listeners.go new file mode 100644 index 000000000..bdd89920b --- /dev/null +++ b/api/server/app_listeners.go @@ -0,0 +1,102 @@ +package server + +import ( + "context" + "github.com/iron-io/functions/api/models" +) + +type AppCreateListener interface { + // BeforeAppCreate called right before creating App in the database + BeforeAppCreate(ctx context.Context, app *models.App) error + // AfterAppCreate called after creating App in the database + AfterAppCreate(ctx context.Context, app *models.App) error +} + +type AppUpdateListener interface { + // BeforeAppUpdate called right before updating App in the database + BeforeAppUpdate(ctx context.Context, app *models.App) error + // AfterAppUpdate called after updating App in the database + AfterAppUpdate(ctx context.Context, app *models.App) error +} + +type AppDeleteListener interface { + // BeforeAppDelete called right before deleting App in the database + BeforeAppDelete(ctx context.Context, app *models.App) error + // AfterAppDelete called after deleting App in the database + AfterAppDelete(ctx context.Context, app *models.App) error +} + +// AddAppCreateListener adds a listener that will be notified on App created. +func (s *Server) AddAppCreateListener(listener AppCreateListener) { + s.appCreateListeners = append(s.appCreateListeners, listener) +} + +// AddAppUpdateListener adds a listener that will be notified on App updated. +func (s *Server) AddAppUpdateListener(listener AppUpdateListener) { + s.appUpdateListeners = append(s.appUpdateListeners, listener) +} + +// AddAppDeleteListener adds a listener that will be notified on App deleted. +func (s *Server) AddAppDeleteListener(listener AppDeleteListener) { + s.appDeleteListeners = append(s.appDeleteListeners, listener) +} + +func (s *Server) FireBeforeAppCreate(ctx context.Context, app *models.App) error { + for _, l := range s.appCreateListeners { + err := l.BeforeAppCreate(ctx, app) + if err != nil { + return err + } + } + return nil +} + +func (s *Server) FireAfterAppCreate(ctx context.Context, app *models.App) error { + for _, l := range s.appCreateListeners { + err := l.AfterAppCreate(ctx, app) + if err != nil { + return err + } + } + return nil +} + +func (s *Server) FireBeforeAppUpdate(ctx context.Context, app *models.App) error { + for _, l := range s.appUpdateListeners { + err := l.BeforeAppUpdate(ctx, app) + if err != nil { + return err + } + } + return nil +} + +func (s *Server) FireAfterAppUpdate(ctx context.Context, app *models.App) error { + for _, l := range s.appUpdateListeners { + err := l.AfterAppUpdate(ctx, app) + if err != nil { + return err + } + } + return nil +} + +func (s *Server) FireBeforeAppDelete(ctx context.Context, app *models.App) error { + for _, l := range s.appDeleteListeners { + err := l.BeforeAppDelete(ctx, app) + if err != nil { + return err + } + } + return nil +} + +func (s *Server) FireAfterAppDelete(ctx context.Context, app *models.App) error { + for _, l := range s.appDeleteListeners { + err := l.AfterAppDelete(ctx, app) + if err != nil { + return err + } + } + return nil +} diff --git a/api/server/apps_delete.go b/api/server/apps_delete.go index a063b2d89..c25d033dc 100644 --- a/api/server/apps_delete.go +++ b/api/server/apps_delete.go @@ -13,9 +13,9 @@ func (s *Server) handleAppDelete(c *gin.Context) { ctx := c.MustGet("ctx").(context.Context) log := common.Logger(ctx) - appName := ctx.Value("appName").(string) + app := &models.App{Name: ctx.Value("appName").(string)} - routes, err := s.Datastore.GetRoutesByApp(ctx, appName, &models.RouteFilter{}) + routes, err := s.Datastore.GetRoutesByApp(ctx, app.Name, &models.RouteFilter{}) if err != nil { log.WithError(err).Debug(models.ErrAppsRemoving) c.JSON(http.StatusInternalServerError, simpleError(models.ErrAppsRemoving)) @@ -28,14 +28,14 @@ func (s *Server) handleAppDelete(c *gin.Context) { return } - err = s.FireBeforeAppDelete(ctx, appName) + err = s.FireBeforeAppDelete(ctx, app) if err != nil { log.WithError(err).Errorln(models.ErrAppsRemoving) c.JSON(http.StatusInternalServerError, simpleError(err)) return } - if err = s.Datastore.RemoveApp(ctx, appName); err != nil { + if err = s.Datastore.RemoveApp(ctx, app.Name); err != nil { log.WithError(err).Debug(models.ErrAppsRemoving) if err == models.ErrAppsNotFound { c.JSON(http.StatusNotFound, simpleError(models.ErrAppsNotFound)) @@ -45,7 +45,7 @@ func (s *Server) handleAppDelete(c *gin.Context) { return } - err = s.FireAfterAppDelete(ctx, appName) + err = s.FireAfterAppDelete(ctx, app) if err != nil { log.WithError(err).Errorln(models.ErrAppsRemoving) c.JSON(http.StatusInternalServerError, simpleError(err)) diff --git a/api/server/listeners.go b/api/server/listeners.go deleted file mode 100644 index d63a1ce1d..000000000 --- a/api/server/listeners.go +++ /dev/null @@ -1,82 +0,0 @@ -package server - -import ( - "context" - "github.com/iron-io/functions/api/ifaces" - "github.com/iron-io/functions/api/models" -) - -// AddAppCreateListener adds a listener that will be notified on App created. -func (s *Server) AddAppCreateListener(listener ifaces.AppCreateListener) { - s.AppCreateListeners = append(s.AppCreateListeners, listener) -} - -// AddAppUpdateListener adds a listener that will be notified on App updated. -func (s *Server) AddAppUpdateListener(listener ifaces.AppUpdateListener) { - s.AppUpdateListeners = append(s.AppUpdateListeners, listener) -} - -// AddAppDeleteListener adds a listener that will be notified on App deleted. -func (s *Server) AddAppDeleteListener(listener ifaces.AppDeleteListener) { - s.AppDeleteListeners = append(s.AppDeleteListeners, listener) -} - -func (s *Server) FireBeforeAppCreate(ctx context.Context, app *models.App) error { - for _, l := range s.AppCreateListeners { - err := l.BeforeAppCreate(ctx, app) - if err != nil { - return err - } - } - return nil -} - -func (s *Server) FireAfterAppCreate(ctx context.Context, app *models.App) error { - for _, l := range s.AppCreateListeners { - err := l.AfterAppCreate(ctx, app) - if err != nil { - return err - } - } - return nil -} - -func (s *Server) FireBeforeAppUpdate(ctx context.Context, app *models.App) error { - for _, l := range s.AppUpdateListeners { - err := l.BeforeAppUpdate(ctx, app) - if err != nil { - return err - } - } - return nil -} - -func (s *Server) FireAfterAppUpdate(ctx context.Context, app *models.App) error { - for _, l := range s.AppUpdateListeners { - err := l.AfterAppUpdate(ctx, app) - if err != nil { - return err - } - } - return nil -} - -func (s *Server) FireBeforeAppDelete(ctx context.Context, appName string) error { - for _, l := range s.AppDeleteListeners { - err := l.BeforeAppDelete(ctx, appName) - if err != nil { - return err - } - } - return nil -} - -func (s *Server) FireAfterAppDelete(ctx context.Context, appName string) error { - for _, l := range s.AppDeleteListeners { - err := l.AfterAppDelete(ctx, appName) - if err != nil { - return err - } - } - return nil -} diff --git a/api/server/runner.go b/api/server/runner.go index c1a7c0f2b..14b366ceb 100644 --- a/api/server/runner.go +++ b/api/server/runner.go @@ -78,8 +78,15 @@ func (s *Server) handleRequest(c *gin.Context, enqueue models.Enqueue) { payload = strings.NewReader(reqPayload) } - appName := ctx.Value("appName").(string) - path := path.Clean(ctx.Value("routePath").(string)) + reqRoute := &models.Route{ + AppName: ctx.Value("appName").(string), + Path: path.Clean(ctx.Value("routePath").(string)), + } + + s.FireBeforeDispatch(ctx, reqRoute) + + appName := reqRoute.AppName + path := reqRoute.Path app, err := s.Datastore.GetApp(ctx, appName) if err != nil || app == nil { @@ -107,6 +114,7 @@ func (s *Server) handleRequest(c *gin.Context, enqueue models.Enqueue) { log = log.WithFields(logrus.Fields{"app": appName, "path": route.Path, "image": route.Image}) if s.serve(ctx, c, appName, route, app, path, reqID, payload, enqueue) { + s.FireAfterDispatch(ctx, reqRoute) return } diff --git a/api/server/runner_listeners.go b/api/server/runner_listeners.go new file mode 100644 index 000000000..ae0241ee2 --- /dev/null +++ b/api/server/runner_listeners.go @@ -0,0 +1,38 @@ +package server + +import ( + "context" + "github.com/iron-io/functions/api/models" +) + +type RunnerListener interface { + // BeforeDispatch called before a function run + BeforeDispatch(ctx context.Context, route *models.Route) error + // AfterDispatch called after a function run + AfterDispatch(ctx context.Context, route *models.Route) error +} + +// AddRunListeners adds a listener that will be fired before and after a function run. +func (s *Server) AddRunnerListener(listener RunnerListener) { + s.runnerListeners = append(s.runnerListeners, listener) +} + +func (s *Server) FireBeforeDispatch(ctx context.Context, route *models.Route) error { + for _, l := range s.runnerListeners { + err := l.BeforeDispatch(ctx, route) + if err != nil { + return err + } + } + return nil +} + +func (s *Server) FireAfterDispatch(ctx context.Context, route *models.Route) error { + for _, l := range s.runnerListeners { + err := l.AfterDispatch(ctx, route) + if err != nil { + return err + } + } + return nil +} diff --git a/api/server/server.go b/api/server/server.go index dbac404eb..495e842ad 100644 --- a/api/server/server.go +++ b/api/server/server.go @@ -10,7 +10,6 @@ import ( "github.com/Sirupsen/logrus" "github.com/gin-gonic/gin" - "github.com/iron-io/functions/api/ifaces" "github.com/iron-io/functions/api/models" "github.com/iron-io/functions/api/runner" "github.com/iron-io/functions/api/runner/task" @@ -18,19 +17,20 @@ import ( ) type Server struct { - Runner *runner.Runner - Router *gin.Engine - MQ models.MessageQueue - AppCreateListeners []ifaces.AppCreateListener - AppUpdateListeners []ifaces.AppUpdateListener - AppDeleteListeners []ifaces.AppDeleteListener - SpecialHandlers []ifaces.SpecialHandler - Enqueue models.Enqueue + Datastore models.Datastore + Runner *runner.Runner + Router *gin.Engine + MQ models.MessageQueue + Enqueue models.Enqueue - tasks chan task.Request + specialHandlers []SpecialHandler + appCreateListeners []AppCreateListener + appUpdateListeners []AppUpdateListener + appDeleteListeners []AppDeleteListener + runnerListeners []RunnerListener + tasks chan task.Request singleflight singleflight // singleflight assists Datastore - Datastore models.Datastore } func New(ctx context.Context, ds models.Datastore, mq models.MessageQueue, r *runner.Runner, tasks chan task.Request, enqueue models.Enqueue) *Server { @@ -65,24 +65,6 @@ func prepareMiddleware(ctx context.Context) gin.HandlerFunc { } } -func (s *Server) AddSpecialHandler(handler ifaces.SpecialHandler) { - s.SpecialHandlers = append(s.SpecialHandlers, handler) -} - -func (s *Server) UseSpecialHandlers(ginC *gin.Context) error { - c := &SpecialHandlerContext{ - server: s, - ginContext: ginC, - } - for _, l := range s.SpecialHandlers { - err := l.Handle(c) - if err != nil { - return err - } - } - return nil -} - func DefaultEnqueue(ctx context.Context, mq models.MessageQueue, task *models.Task) (*models.Task, error) { ctx, _ = common.LoggerWithFields(ctx, logrus.Fields{"call_id": task.ID}) return mq.Push(ctx, task) diff --git a/api/ifaces/handlers.go b/api/server/special_handler.go similarity index 68% rename from api/ifaces/handlers.go rename to api/server/special_handler.go index aaf1dccc9..9a3da827b 100644 --- a/api/ifaces/handlers.go +++ b/api/server/special_handler.go @@ -1,9 +1,10 @@ -package ifaces +package server import ( "net/http" "context" + "github.com/gin-gonic/gin" "github.com/iron-io/functions/api/models" ) @@ -27,3 +28,21 @@ type HandlerContext interface { Set(key string, value interface{}) Get(key string) (value interface{}, exists bool) } + +func (s *Server) AddSpecialHandler(handler SpecialHandler) { + s.specialHandlers = append(s.specialHandlers, handler) +} + +func (s *Server) UseSpecialHandlers(ginC *gin.Context) error { + c := &SpecialHandlerContext{ + server: s, + ginContext: ginC, + } + for _, l := range s.specialHandlers { + err := l.Handle(c) + if err != nil { + return err + } + } + return nil +} diff --git a/docs/operating/extending.md b/docs/operating/extending.md index c46fd92b1..cdc18fc3b 100644 --- a/docs/operating/extending.md +++ b/docs/operating/extending.md @@ -28,8 +28,8 @@ func (c *myCustomListener) AfterAppCreate(ctx context.Context, app *models.App) func (c *myCustomListener) BeforeAppUpdate(ctx context.Context, app *models.App) error { return nil } func (c *myCustomListener) AfterAppUpdate(ctx context.Context, app *models.App) error { return nil } -func (c *myCustomListener) BeforeAppDelete(ctx context.Context, appName string) error { return nil } -func (c *myCustomListener) BeforeAppDelete(ctx context.Context, appName string) error { return nil } +func (c *myCustomListener) BeforeAppDelete(ctx context.Context, app *models.App) error { return nil } +func (c *myCustomListener) BeforeAppDelete(ctx context.Context, app *models.App) error { return nil } function main () { srv := server.New(/* Here all required parameters to initialize the server */) @@ -42,12 +42,14 @@ function main () { } ``` -#### Listener Requirements - -To be a valid listener your struct should respect interfaces combined or alone found in the file [listeners.go](/iron-io/functions/blob/master/api/ifaces/listeners.go) +### Creating a Listener These are all available listeners: +#### App Listeners + +To be a valid listener your struct should respect interfaces combined or alone found [in this file](/iron-io/functions/blob/master/api/server/apps_listeners.go) + ##### AppCreateListener _Triggers before and after every app creation that happens in the API_ @@ -71,7 +73,20 @@ _Triggers before and after every app deletion that happens in the API_ Triggered during requests to the following routes: -- DELETE /v1/apps/app +- DELETE /v1/apps/:app + +#### Runner Listeners + +To be a valid listener your struct should respect interfaces combined or alone found [in this file](/iron-io/functions/blob/master/api/server/runner_listeners.go). + +##### RunnerListener + +_Triggers before and after every function run_ + +Triggered during requests to the following routes: + +- GET /r/:app/:route +- POST /r/:app/:route ## Special Handlers