mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
List Cursor management moved into datastore layer. (#1102)
* Don't try to delete an app that wasn't successfully created in the case of failure * Allow datastore implementations to inject additional annotations on objects * Allow for datastores transparently adding annotations on apps, fns and triggers. Change NameIn filter to Name for apps. * Move *List types including JSON annotations for App, Fn and Trigger into models * Change return types for GetApps, GetFns and GetTriggers on datastore to be models.*List and ove cursor generation into datastore * Trigger cursor handling fixed into db layer Also changes the name generation so that it is not in the same order as the id (well is random), this means we are now testing our name ordering. * GetFns now respects cursors * Apps now feeds cursor back * Mock fixes * Fixing up api level cursor decoding * Tidy up treatment of cursors in the db layer * Adding conditions for non nil items lists * fix mock test
This commit is contained in:
committed by
Owen Cliffe
parent
fca107c815
commit
d7139358ce
@@ -1,7 +1,6 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"net/http"
|
||||
|
||||
"github.com/fnproject/fn/api/models"
|
||||
@@ -12,11 +11,10 @@ func (s *Server) handleAppList(c *gin.Context) {
|
||||
ctx := c.Request.Context()
|
||||
|
||||
filter := &models.AppFilter{}
|
||||
filter.Cursor, filter.PerPage = pageParams(c, true)
|
||||
name := c.Query("name")
|
||||
if name != "" {
|
||||
filter.NameIn = []string{name}
|
||||
}
|
||||
|
||||
filter.Cursor, filter.PerPage = pageParamsV2(c)
|
||||
|
||||
filter.Name = c.Query("name")
|
||||
|
||||
apps, err := s.datastore.GetApps(ctx, filter)
|
||||
if err != nil {
|
||||
@@ -24,14 +22,5 @@ func (s *Server) handleAppList(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
var nextCursor string
|
||||
if len(apps) > 0 && len(apps) == filter.PerPage {
|
||||
last := []byte(apps[len(apps)-1].Name)
|
||||
nextCursor = base64.RawURLEncoding.EncodeToString(last)
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, appListResponse{
|
||||
NextCursor: nextCursor,
|
||||
Items: apps,
|
||||
})
|
||||
c.JSON(http.StatusOK, apps)
|
||||
}
|
||||
|
||||
@@ -209,7 +209,7 @@ func TestAppList(t *testing.T) {
|
||||
} else {
|
||||
// normal path
|
||||
|
||||
var resp appListResponse
|
||||
var resp models.AppList
|
||||
err := json.NewDecoder(rec.Body).Decode(&resp)
|
||||
if err != nil {
|
||||
t.Errorf("Test %d: Expected response body to be a valid json object. err: %v", i, err)
|
||||
|
||||
@@ -13,7 +13,7 @@ func (s *Server) handleV1AppList(c *gin.Context) {
|
||||
ctx := c.Request.Context()
|
||||
|
||||
filter := &models.AppFilter{}
|
||||
filter.Cursor, filter.PerPage = pageParams(c, true)
|
||||
filter.Cursor, filter.PerPage = pageParamsV2(c)
|
||||
|
||||
apps, err := s.datastore.GetApps(ctx, filter)
|
||||
if err != nil {
|
||||
@@ -22,14 +22,14 @@ func (s *Server) handleV1AppList(c *gin.Context) {
|
||||
}
|
||||
|
||||
var nextCursor string
|
||||
if len(apps) > 0 && len(apps) == filter.PerPage {
|
||||
last := []byte(apps[len(apps)-1].Name)
|
||||
if len(apps.Items) > 0 && len(apps.Items) == filter.PerPage {
|
||||
last := []byte(apps.Items[len(apps.Items)-1].Name)
|
||||
nextCursor = base64.RawURLEncoding.EncodeToString(last)
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, appsV1Response{
|
||||
Message: "Successfully listed applications",
|
||||
NextCursor: nextCursor,
|
||||
Apps: apps,
|
||||
Apps: apps.Items,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ func (s *Server) handleFnList(c *gin.Context) {
|
||||
ctx := c.Request.Context()
|
||||
|
||||
var filter models.FnFilter
|
||||
filter.Cursor, filter.PerPage = pageParams(c, false)
|
||||
filter.Cursor, filter.PerPage = pageParamsV2(c)
|
||||
filter.AppID = c.Query("app_id")
|
||||
filter.Name = c.Query("name")
|
||||
|
||||
@@ -21,13 +21,5 @@ func (s *Server) handleFnList(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
var nextCursor string
|
||||
if len(fns) > 0 && len(fns) == filter.PerPage {
|
||||
nextCursor = fns[len(fns)-1].Name
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, fnListResponse{
|
||||
NextCursor: nextCursor,
|
||||
Items: fns,
|
||||
})
|
||||
c.JSON(http.StatusOK, fns)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package server
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
@@ -233,6 +234,10 @@ func TestFnList(t *testing.T) {
|
||||
|
||||
srv := testServer(ds, &mqs.Mock{}, fnl, rnr, ServerTypeFull)
|
||||
|
||||
fn1b := base64.RawURLEncoding.EncodeToString([]byte(fn1))
|
||||
fn2b := base64.RawURLEncoding.EncodeToString([]byte(fn2))
|
||||
fn3b := base64.RawURLEncoding.EncodeToString([]byte(fn3))
|
||||
|
||||
for i, test := range []struct {
|
||||
path string
|
||||
body string
|
||||
@@ -244,11 +249,11 @@ func TestFnList(t *testing.T) {
|
||||
}{
|
||||
{"/v2/fns", "", http.StatusBadRequest, models.ErrFnsMissingAppID, 0, ""},
|
||||
{fmt.Sprintf("/v2/fns?app_id=%s", app1.ID), "", http.StatusOK, nil, 3, ""},
|
||||
{fmt.Sprintf("/v2/fns?app_id=%s&per_page=1", app1.ID), "", http.StatusOK, nil, 1, fn1},
|
||||
{fmt.Sprintf("/v2/fns?app_id=%s&per_page=1&cursor=%s", app1.ID, fn1), "", http.StatusOK, nil, 1, fn2},
|
||||
{fmt.Sprintf("/v2/fns?app_id=%s&per_page=1&cursor=%s", app1.ID, fn2), "", http.StatusOK, nil, 1, fn3},
|
||||
{fmt.Sprintf("/v2/fns?app_id=%s&per_page=100&cursor=%s", app1.ID, fn3), "", http.StatusOK, nil, 0, ""}, // cursor is empty if per_page > len(results)
|
||||
{fmt.Sprintf("/v2/fns?app_id=%s&per_page=1&cursor=%s", app1.ID, fn3), "", http.StatusOK, nil, 0, ""}, // cursor could point to empty page
|
||||
{fmt.Sprintf("/v2/fns?app_id=%s&per_page=1", app1.ID), "", http.StatusOK, nil, 1, fn1b},
|
||||
{fmt.Sprintf("/v2/fns?app_id=%s&per_page=1&cursor=%s", app1.ID, fn1b), "", http.StatusOK, nil, 1, fn2b},
|
||||
{fmt.Sprintf("/v2/fns?app_id=%s&per_page=1&cursor=%s", app1.ID, fn2b), "", http.StatusOK, nil, 1, fn3b},
|
||||
{fmt.Sprintf("/v2/fns?app_id=%s&per_page=100&cursor=%s", app1.ID, fn3b), "", http.StatusOK, nil, 0, ""}, // cursor is empty if per_page > len(results)
|
||||
{fmt.Sprintf("/v2/fns?app_id=%s&per_page=1&cursor=%s", app1.ID, fn3b), "", http.StatusOK, nil, 0, ""}, // cursor could point to empty page
|
||||
} {
|
||||
_, rec := routerRequest(t, srv.Router, "GET", test.path, nil)
|
||||
|
||||
@@ -269,7 +274,7 @@ func TestFnList(t *testing.T) {
|
||||
} else {
|
||||
// normal path
|
||||
|
||||
var resp fnListResponse
|
||||
var resp models.FnList
|
||||
err := json.NewDecoder(rec.Body).Decode(&resp)
|
||||
if err != nil {
|
||||
t.Errorf("Test %d: Expected response body to be a valid json object. err: %v", i, err)
|
||||
|
||||
@@ -1013,6 +1013,18 @@ func pageParams(c *gin.Context, base64d bool) (cursor string, perPage int) {
|
||||
return cursor, perPage
|
||||
}
|
||||
|
||||
func pageParamsV2(c *gin.Context) (cursor string, perPage int) {
|
||||
cursor = c.Query("cursor")
|
||||
|
||||
perPage, _ = strconv.Atoi(c.Query("per_page"))
|
||||
if perPage > 100 {
|
||||
perPage = 100
|
||||
} else if perPage <= 0 {
|
||||
perPage = 30
|
||||
}
|
||||
return cursor, perPage
|
||||
}
|
||||
|
||||
type appResponse struct {
|
||||
Message string `json:"message"`
|
||||
App *models.App `json:"app"`
|
||||
@@ -1046,18 +1058,3 @@ type callsResponse struct {
|
||||
NextCursor string `json:"next_cursor"`
|
||||
Calls []*models.Call `json:"calls"`
|
||||
}
|
||||
|
||||
type appListResponse struct {
|
||||
NextCursor string `json:"next_cursor"`
|
||||
Items []*models.App `json:"items"`
|
||||
}
|
||||
|
||||
type fnListResponse struct {
|
||||
NextCursor string `json:"next_cursor"`
|
||||
Items []*models.Fn `json:"items"`
|
||||
}
|
||||
|
||||
type triggerListResponse struct {
|
||||
NextCursor string `json:"next_cursor"`
|
||||
Items []*models.Trigger `json:"items"`
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"net/http"
|
||||
|
||||
"github.com/fnproject/fn/api/models"
|
||||
@@ -12,7 +11,7 @@ func (s *Server) handleTriggerList(c *gin.Context) {
|
||||
ctx := c.Request.Context()
|
||||
|
||||
filter := &models.TriggerFilter{}
|
||||
filter.Cursor, filter.PerPage = pageParams(c, true)
|
||||
filter.Cursor, filter.PerPage = pageParamsV2(c)
|
||||
|
||||
filter.AppID = c.Query("app_id")
|
||||
|
||||
@@ -29,14 +28,5 @@ func (s *Server) handleTriggerList(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
var nextCursor string
|
||||
if len(triggers) > 0 && len(triggers) == filter.PerPage {
|
||||
last := []byte(triggers[len(triggers)-1].ID)
|
||||
nextCursor = base64.RawURLEncoding.EncodeToString(last)
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, triggerListResponse{
|
||||
NextCursor: nextCursor,
|
||||
Items: triggers,
|
||||
})
|
||||
c.JSON(http.StatusOK, triggers)
|
||||
}
|
||||
|
||||
@@ -221,6 +221,7 @@ func TestTriggerList(t *testing.T) {
|
||||
}{
|
||||
{"/v2/triggers?per_page", "", http.StatusBadRequest, nil, 0, ""},
|
||||
{"/v2/triggers?app_id=app_id1", "", http.StatusOK, nil, 4, ""},
|
||||
{"/v2/triggers?app_id=app_id2", "", http.StatusOK, nil, 1, ""},
|
||||
{"/v2/triggers?app_id=app_id1&name=trigger1", "", http.StatusOK, nil, 1, ""},
|
||||
{"/v2/triggers?app_id=app_id1&fn_id=fn_id1", "", http.StatusOK, nil, 3, ""},
|
||||
{"/v2/triggers?app_id=app_id1&fn_id=fn_id1&per_page", "", http.StatusOK, nil, 3, ""},
|
||||
@@ -249,7 +250,7 @@ func TestTriggerList(t *testing.T) {
|
||||
} else {
|
||||
// normal path
|
||||
|
||||
var resp triggerListResponse
|
||||
var resp models.TriggerList
|
||||
err := json.NewDecoder(rec.Body).Decode(&resp)
|
||||
if err != nil {
|
||||
t.Errorf("Test %d: Expected response body to be a valid json object. err: %v", i, err)
|
||||
|
||||
Reference in New Issue
Block a user