mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
Add support for Function and Trigger domain objects (#1060)
Vast commit, includes: * Introduces the Trigger domain entity. * Introduces the Fns domain entity. * V2 of the API for interacting with the new entities in swaggerv2.yml * Adds v2 end points for Apps to support PUT updates. * Rewrites the datastore level tests into a new pattern. * V2 routes use entity ID over name as the path parameter.
This commit is contained in:
@@ -5,15 +5,20 @@ import (
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"time"
|
||||
|
||||
"github.com/fnproject/fn/api/common"
|
||||
"github.com/fnproject/fn/api/datastore/internal/datastoreutil"
|
||||
"github.com/fnproject/fn/api/id"
|
||||
"github.com/fnproject/fn/api/logs"
|
||||
"github.com/fnproject/fn/api/models"
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
type mock struct {
|
||||
Apps []*models.App
|
||||
Routes []*models.Route
|
||||
Apps []*models.App
|
||||
Routes []*models.Route
|
||||
Fns []*models.Fn
|
||||
Triggers []*models.Trigger
|
||||
|
||||
models.LogStore
|
||||
}
|
||||
@@ -31,6 +36,11 @@ func NewMockInit(args ...interface{}) models.Datastore {
|
||||
mocker.Apps = x
|
||||
case []*models.Route:
|
||||
mocker.Routes = x
|
||||
case []*models.Fn:
|
||||
mocker.Fns = x
|
||||
case []*models.Trigger:
|
||||
mocker.Triggers = x
|
||||
|
||||
default:
|
||||
panic("not accounted for data type sent to mock init. add it")
|
||||
}
|
||||
@@ -52,7 +62,7 @@ func (m *mock) GetAppID(ctx context.Context, appName string) (string, error) {
|
||||
func (m *mock) GetAppByID(ctx context.Context, appID string) (*models.App, error) {
|
||||
for _, a := range m.Apps {
|
||||
if a.ID == appID {
|
||||
return a, nil
|
||||
return a.Clone(), nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,47 +84,101 @@ func (m *mock) GetApps(ctx context.Context, appFilter *models.AppFilter) ([]*mod
|
||||
if len(apps) == appFilter.PerPage {
|
||||
break
|
||||
}
|
||||
if len(appFilter.NameIn) > 0 {
|
||||
var found bool
|
||||
for _, fn := range appFilter.NameIn {
|
||||
if fn == a.Name {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if strings.Compare(appFilter.Cursor, a.Name) < 0 {
|
||||
apps = append(apps, a)
|
||||
apps = append(apps, a.Clone())
|
||||
}
|
||||
}
|
||||
|
||||
return apps, nil
|
||||
}
|
||||
|
||||
func (m *mock) InsertApp(ctx context.Context, app *models.App) (*models.App, error) {
|
||||
if a, _ := m.GetAppByID(ctx, app.ID); a != nil {
|
||||
return nil, models.ErrAppsAlreadyExists
|
||||
func (m *mock) InsertApp(ctx context.Context, newApp *models.App) (*models.App, error) {
|
||||
for _, a := range m.Apps {
|
||||
if newApp.Name == a.Name {
|
||||
return nil, models.ErrAppsAlreadyExists
|
||||
}
|
||||
}
|
||||
|
||||
app := newApp.Clone()
|
||||
app.CreatedAt = common.DateTime(time.Now())
|
||||
app.UpdatedAt = app.CreatedAt
|
||||
app.ID = id.New().String()
|
||||
|
||||
m.Apps = append(m.Apps, app)
|
||||
return app, nil
|
||||
return app.Clone(), nil
|
||||
}
|
||||
|
||||
func (m *mock) UpdateApp(ctx context.Context, app *models.App) (*models.App, error) {
|
||||
a, err := m.GetAppByID(ctx, app.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
a.Update(app)
|
||||
|
||||
return a.Clone(), nil
|
||||
appID := app.ID
|
||||
for idx, a := range m.Apps {
|
||||
if a.ID == appID {
|
||||
if app.Name != "" && app.Name != a.Name {
|
||||
return nil, models.ErrAppsNameImmutable
|
||||
}
|
||||
c := a.Clone()
|
||||
c.Update(app)
|
||||
err := c.Validate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m.Apps[idx] = c
|
||||
return c.Clone(), nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, models.ErrAppsNotFound
|
||||
|
||||
}
|
||||
|
||||
func (m *mock) RemoveApp(ctx context.Context, appID string) error {
|
||||
m.batchDeleteRoutes(ctx, appID)
|
||||
|
||||
for i, a := range m.Apps {
|
||||
if a.ID == appID {
|
||||
m.Apps = append(m.Apps[:i], m.Apps[i+1:]...)
|
||||
var newFns []*models.Fn
|
||||
var newTriggers []*models.Trigger
|
||||
newApps := append(m.Apps[0:i], m.Apps[i+1:]...)
|
||||
|
||||
for _, fn := range m.Fns {
|
||||
if fn.AppID != appID {
|
||||
newFns = append(newFns, fn)
|
||||
}
|
||||
}
|
||||
|
||||
for _, t := range m.Triggers {
|
||||
if t.AppID != appID {
|
||||
newTriggers = append(newTriggers, t)
|
||||
}
|
||||
}
|
||||
|
||||
m.Apps = newApps
|
||||
m.Triggers = newTriggers
|
||||
m.Fns = newFns
|
||||
return nil
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return models.ErrAppsNotFound
|
||||
}
|
||||
|
||||
func (m *mock) GetRoute(ctx context.Context, appID, routePath string) (*models.Route, error) {
|
||||
for _, r := range m.Routes {
|
||||
if r.AppID == appID && r.Path == routePath {
|
||||
return r, nil
|
||||
return r.Clone(), nil
|
||||
}
|
||||
}
|
||||
return nil, models.ErrRoutesNotFound
|
||||
@@ -140,13 +204,19 @@ func (m *mock) GetRoutesByApp(ctx context.Context, appID string, routeFilter *mo
|
||||
(routeFilter.Image == "" || routeFilter.Image == r.Image) &&
|
||||
strings.Compare(routeFilter.Cursor, r.Path) < 0 {
|
||||
|
||||
routes = append(routes, r)
|
||||
routes = append(routes, r.Clone())
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (m *mock) InsertRoute(ctx context.Context, route *models.Route) (*models.Route, error) {
|
||||
|
||||
c := route.Clone()
|
||||
c.SetDefaults()
|
||||
c.CreatedAt = common.DateTime(time.Now())
|
||||
c.UpdatedAt = c.CreatedAt
|
||||
|
||||
if _, err := m.GetAppByID(ctx, route.AppID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -154,8 +224,8 @@ func (m *mock) InsertRoute(ctx context.Context, route *models.Route) (*models.Ro
|
||||
if r, _ := m.GetRoute(ctx, route.AppID, route.Path); r != nil {
|
||||
return nil, models.ErrRoutesAlreadyExists
|
||||
}
|
||||
m.Routes = append(m.Routes, route)
|
||||
return route, nil
|
||||
m.Routes = append(m.Routes, c)
|
||||
return c.Clone(), nil
|
||||
}
|
||||
|
||||
func (m *mock) UpdateRoute(ctx context.Context, route *models.Route) (*models.Route, error) {
|
||||
@@ -184,7 +254,7 @@ func (m *mock) RemoveRoute(ctx context.Context, appID, routePath string) error {
|
||||
}
|
||||
|
||||
func (m *mock) batchDeleteRoutes(ctx context.Context, appID string) error {
|
||||
newRoutes := []*models.Route{}
|
||||
var newRoutes []*models.Route
|
||||
for _, c := range m.Routes {
|
||||
if c.AppID != appID {
|
||||
newRoutes = append(newRoutes, c)
|
||||
@@ -194,9 +264,223 @@ func (m *mock) batchDeleteRoutes(ctx context.Context, appID string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetDatabase returns nil here since shouldn't really be used
|
||||
func (m *mock) GetDatabase() *sqlx.DB {
|
||||
return nil
|
||||
func (m *mock) InsertFn(ctx context.Context, fn *models.Fn) (*models.Fn, error) {
|
||||
_, err := m.GetAppByID(ctx, fn.AppID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, f := range m.Fns {
|
||||
if f.ID == fn.ID ||
|
||||
(f.AppID == fn.AppID &&
|
||||
f.Name == fn.Name) {
|
||||
return nil, models.ErrFnsExists
|
||||
}
|
||||
}
|
||||
cl := fn.Clone()
|
||||
cl.ID = id.New().String()
|
||||
cl.CreatedAt = common.DateTime(time.Now())
|
||||
cl.UpdatedAt = cl.CreatedAt
|
||||
err = fn.Validate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
m.Fns = append(m.Fns, cl)
|
||||
|
||||
return cl.Clone(), nil
|
||||
}
|
||||
|
||||
func (m *mock) UpdateFn(ctx context.Context, fn *models.Fn) (*models.Fn, error) {
|
||||
// update if exists
|
||||
for _, f := range m.Fns {
|
||||
if f.ID == fn.ID {
|
||||
clone := f.Clone()
|
||||
clone.Update(fn)
|
||||
err := clone.Validate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
*f = *clone
|
||||
return f, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, models.ErrFnsNotFound
|
||||
}
|
||||
|
||||
type sortF []*models.Fn
|
||||
|
||||
func (s sortF) Len() int { return len(s) }
|
||||
func (s sortF) Less(i, j int) bool { return strings.Compare(s[i].Name, s[j].Name) < 0 }
|
||||
func (s sortF) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
|
||||
func (m *mock) GetFns(ctx context.Context, filter *models.FnFilter) ([]*models.Fn, error) {
|
||||
// sort them all first for cursoring (this is for testing, n is small & mock is not concurrent..)
|
||||
sort.Sort(sortF(m.Fns))
|
||||
|
||||
funcs := []*models.Fn{}
|
||||
|
||||
for _, f := range m.Fns {
|
||||
if filter.PerPage > 0 && len(funcs) == filter.PerPage {
|
||||
break
|
||||
}
|
||||
|
||||
if strings.Compare(filter.Cursor, f.Name) < 0 &&
|
||||
(filter.AppID == "" || filter.AppID == f.AppID) &&
|
||||
(filter.Name == "" || filter.Name == f.Name) {
|
||||
funcs = append(funcs, f)
|
||||
}
|
||||
|
||||
}
|
||||
return funcs, nil
|
||||
}
|
||||
|
||||
func (m *mock) GetFnByID(ctx context.Context, fnID string) (*models.Fn, error) {
|
||||
for _, f := range m.Fns {
|
||||
if f.ID == fnID {
|
||||
return f, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, models.ErrFnsNotFound
|
||||
}
|
||||
|
||||
func (m *mock) RemoveFn(ctx context.Context, fnID string) error {
|
||||
for i, f := range m.Fns {
|
||||
if f.ID == fnID {
|
||||
m.Fns = append(m.Fns[:i], m.Fns[i+1:]...)
|
||||
var newTriggers []*models.Trigger
|
||||
for _, t := range m.Triggers {
|
||||
if t.FnID != f.ID {
|
||||
newTriggers = append(newTriggers, t)
|
||||
}
|
||||
}
|
||||
|
||||
m.Triggers = newTriggers
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return models.ErrFnsNotFound
|
||||
}
|
||||
|
||||
func (m *mock) InsertTrigger(ctx context.Context, trigger *models.Trigger) (*models.Trigger, error) {
|
||||
_, err := m.GetAppByID(ctx, trigger.AppID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fn, err := m.GetFnByID(ctx, trigger.FnID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if fn.AppID != trigger.AppID {
|
||||
return nil, models.ErrTriggerFnIDNotSameApp
|
||||
}
|
||||
|
||||
for _, t := range m.Triggers {
|
||||
if t.ID == trigger.ID ||
|
||||
(t.AppID == trigger.AppID &&
|
||||
t.FnID == trigger.FnID &&
|
||||
t.Name == trigger.Name) {
|
||||
return nil, models.ErrTriggerExists
|
||||
}
|
||||
}
|
||||
|
||||
cl := trigger.Clone()
|
||||
cl.CreatedAt = common.DateTime(time.Now())
|
||||
cl.UpdatedAt = cl.CreatedAt
|
||||
cl.ID = id.New().String()
|
||||
|
||||
err = trigger.Validate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m.Triggers = append(m.Triggers, cl)
|
||||
return cl.Clone(), nil
|
||||
}
|
||||
|
||||
func (m *mock) UpdateTrigger(ctx context.Context, trigger *models.Trigger) (*models.Trigger, error) {
|
||||
for _, t := range m.Triggers {
|
||||
if t.ID == trigger.ID {
|
||||
cl := t.Clone()
|
||||
cl.Update(trigger)
|
||||
err := cl.Validate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
*t = *cl
|
||||
return cl.Clone(), nil
|
||||
}
|
||||
}
|
||||
return nil, models.ErrTriggerNotFound
|
||||
}
|
||||
|
||||
func (m *mock) GetTrigger(ctx context.Context, appId, fnId, triggerName string) (*models.Trigger, error) {
|
||||
for _, t := range m.Triggers {
|
||||
if t.AppID == appId && t.FnID == fnId && t.Name == triggerName {
|
||||
return t.Clone(), nil
|
||||
}
|
||||
}
|
||||
return nil, models.ErrTriggerNotFound
|
||||
}
|
||||
|
||||
func (m *mock) GetTriggerByID(ctx context.Context, triggerId string) (*models.Trigger, error) {
|
||||
for _, t := range m.Triggers {
|
||||
if t.ID == triggerId {
|
||||
return t.Clone(), nil
|
||||
}
|
||||
}
|
||||
return nil, models.ErrTriggerNotFound
|
||||
}
|
||||
|
||||
type sortT []*models.Trigger
|
||||
|
||||
func (s sortT) Len() int { return len(s) }
|
||||
func (s sortT) Less(i, j int) bool { return strings.Compare(s[i].ID, s[j].ID) < 0 }
|
||||
func (s sortT) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
|
||||
func (m *mock) GetTriggers(ctx context.Context, filter *models.TriggerFilter) ([]*models.Trigger, error) {
|
||||
sort.Sort(sortT(m.Triggers))
|
||||
|
||||
res := []*models.Trigger{}
|
||||
for _, t := range m.Triggers {
|
||||
if filter.PerPage > 0 && len(res) == filter.PerPage {
|
||||
break
|
||||
}
|
||||
|
||||
matched := true
|
||||
if filter.Cursor != "" && t.ID <= filter.Cursor {
|
||||
matched = false
|
||||
}
|
||||
|
||||
if t.AppID != filter.AppID {
|
||||
matched = false
|
||||
}
|
||||
if filter.FnID != "" && filter.FnID != t.FnID {
|
||||
matched = false
|
||||
}
|
||||
|
||||
if filter.Name != "" && filter.Name != t.Name {
|
||||
matched = false
|
||||
}
|
||||
|
||||
if matched {
|
||||
res = append(res, t)
|
||||
}
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (m *mock) RemoveTrigger(ctx context.Context, triggerID string) error {
|
||||
for i, t := range m.Triggers {
|
||||
if t.ID == triggerID {
|
||||
m.Triggers = append(m.Triggers[:i], m.Triggers[i+1:]...)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return models.ErrTriggerNotFound
|
||||
}
|
||||
|
||||
func (m *mock) Close() error {
|
||||
|
||||
Reference in New Issue
Block a user