Permalink
Browse files

Add admin user list pagination

  • Loading branch information...
brycekahle committed Aug 24, 2017
1 parent 776d894 commit 27528ce82cf8b7a8a4299d3ce1bb9559248426f7
View
@@ -2,7 +2,6 @@
gorm.db
vendor/
gotrue
test.db
.DS_Store
.vscode
View
@@ -23,5 +23,4 @@ vet: # Vet the code
go vet $(CHECK_FILES)
test: ## Run tests.
rm -f api/test.db
go test -v $(CHECK_FILES)
View
@@ -63,10 +63,17 @@ func (api *API) adminUsers(w http.ResponseWriter, r *http.Request) error {
ctx := r.Context()
instanceID := getInstanceID(ctx)
aud := api.requestAud(ctx, r)
users, err := api.db.FindUsersInAudience(instanceID, aud)
pageParams, err := paginate(r)
if err != nil {
return badRequestError("Bad Pagination Parameters: %v", err)
}
users, err := api.db.FindUsersInAudience(instanceID, aud, pageParams)
if err != nil {
return internalServerError("Database error finding users").WithInternalError(err)
}
addPaginationHeaders(w, r, pageParams)
return sendJSON(w, http.StatusOK, map[string]interface{}{
"users": users,
View
@@ -6,6 +6,7 @@ import (
"fmt"
"net/http"
"net/http/httptest"
"os"
"testing"
"time"
@@ -24,6 +25,14 @@ type AdminTestSuite struct {
Config *conf.Configuration
}
func (ts *AdminTestSuite) SetupSuite() {
require.NoError(ts.T(), os.Setenv("GOTRUE_DB_DATABASE_URL", createTestDB()))
}
func (ts *AdminTestSuite) TearDownSuite() {
os.Remove(ts.API.config.DB.URL)
}
func (ts *AdminTestSuite) SetupTest() {
api, err := NewAPIFromConfigFile("test.env", "v1")
require.NoError(ts.T(), err)
@@ -76,11 +85,35 @@ func (ts *AdminTestSuite) TestAdminUsers() {
ts.makeSuperAdmin(req, "test@example.com")
ts.API.handler.ServeHTTP(w, req)
require.Equal(ts.T(), http.StatusOK, w.Code)
assert.Equal(ts.T(), "</admin/users?page=1>; rel=\"last\"", w.HeaderMap.Get("Link"))
assert.Equal(ts.T(), "2", w.HeaderMap.Get("X-Total-Count"))
data := make(map[string]interface{})
require.NoError(ts.T(), json.NewDecoder(w.Body).Decode(&data))
for _, user := range data["users"].([]interface{}) {
assert.NotEmpty(ts.T(), user)
}
}
// TestAdminUsers tests API /admin/users route
func (ts *AdminTestSuite) TestAdminUsers_Pagination() {
// Setup request
w := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, "/admin/users?per_page=1", nil)
assert.Equal(ts.T(), w.Code, http.StatusOK)
// Setup response recorder with super admin privileges
ts.makeSuperAdmin(req, "test@example.com")
ts.API.handler.ServeHTTP(w, req)
require.Equal(ts.T(), http.StatusOK, w.Code)
assert.Equal(ts.T(), "</admin/users?page=2&per_page=1>; rel=\"next\", </admin/users?page=2&per_page=1>; rel=\"last\"", w.HeaderMap.Get("Link"))
assert.Equal(ts.T(), "2", w.HeaderMap.Get("X-Total-Count"))
data := make(map[string]interface{})
require.NoError(ts.T(), json.NewDecoder(w.Body).Decode(&data))
for _, user := range data["users"].([]interface{}) {
assert.NotEmpty(ts.T(), user)
}
@@ -102,8 +135,7 @@ func (ts *AdminTestSuite) TestAdminUserCreate() {
ts.makeSuperAdmin(req, "test@example.com")
ts.API.handler.ServeHTTP(w, req)
assert.Equal(ts.T(), w.Code, http.StatusOK)
require.Equal(ts.T(), http.StatusOK, w.Code)
u, err := ts.API.db.FindUserByEmailAndAudience("", "test1@example.com", ts.Config.JWT.Aud)
require.NoError(ts.T(), err)
@@ -128,8 +160,7 @@ func (ts *AdminTestSuite) TestAdminUserGet() {
ts.makeSuperAdmin(req, "test@example.com")
ts.API.handler.ServeHTTP(w, req)
assert.Equal(ts.T(), w.Code, http.StatusOK)
require.Equal(ts.T(), http.StatusOK, w.Code)
data := make(map[string]interface{})
require.NoError(ts.T(), json.NewDecoder(w.Body).Decode(&data))
@@ -156,8 +187,7 @@ func (ts *AdminTestSuite) TestAdminUserUpdate() {
ts.makeSuperAdmin(req, "test@example.com")
ts.API.handler.ServeHTTP(w, req)
assert.Equal(ts.T(), w.Code, http.StatusOK)
require.Equal(ts.T(), http.StatusOK, w.Code)
data := make(map[string]interface{})
require.NoError(ts.T(), json.NewDecoder(w.Body).Decode(&data))
@@ -187,8 +217,7 @@ func (ts *AdminTestSuite) TestAdminUserDelete() {
ts.makeSuperAdmin(req, "test@example.com")
ts.API.handler.ServeHTTP(w, req)
assert.Equal(ts.T(), w.Code, http.StatusOK)
require.Equal(ts.T(), http.StatusOK, w.Code)
}
// TestAdminUserCreateWithManagementToken tests API /admin/user route using the management token (POST)
@@ -207,8 +236,7 @@ func (ts *AdminTestSuite) TestAdminUserCreateWithManagementToken() {
req.Header.Set("X-JWT-AUD", "op-test-aud")
ts.API.handler.ServeHTTP(w, req)
assert.Equal(ts.T(), w.Code, http.StatusOK)
require.Equal(ts.T(), http.StatusOK, w.Code)
_, err := ts.API.db.FindUserByEmailAndAudience("", "test2@example.com", ts.Config.JWT.Aud)
require.Error(ts.T(), err)
View
@@ -5,6 +5,7 @@ import (
"encoding/json"
"net/http"
"net/http/httptest"
"os"
"testing"
"github.com/pborman/uuid"
@@ -25,6 +26,14 @@ type InstanceTestSuite struct {
API *API
}
func (ts *InstanceTestSuite) SetupSuite() {
require.NoError(ts.T(), os.Setenv("GOTRUE_DB_DATABASE_URL", createTestDB()))
}
func (ts *InstanceTestSuite) TearDownSuite() {
os.Remove(ts.API.config.DB.URL)
}
func (ts *InstanceTestSuite) SetupTest() {
globalConfig, err := conf.LoadGlobal("test.env")
require.NoError(ts.T(), err)
View
@@ -5,6 +5,7 @@ import (
"encoding/json"
"net/http"
"net/http/httptest"
"os"
"testing"
"time"
@@ -21,6 +22,14 @@ type InviteTestSuite struct {
Config *conf.Configuration
}
func (ts *InviteTestSuite) SetupSuite() {
require.NoError(ts.T(), os.Setenv("GOTRUE_DB_DATABASE_URL", createTestDB()))
}
func (ts *InviteTestSuite) TearDownSuite() {
os.Remove(ts.API.config.DB.URL)
}
func (ts *InviteTestSuite) SetupTest() {
api, err := NewAPIFromConfigFile("test.env", "v1")
require.NoError(ts.T(), err)
View
@@ -0,0 +1,64 @@
package api
import (
"fmt"
"net/http"
"net/url"
"strconv"
"github.com/netlify/gotrue/models"
)
const defaultPerPage = 50
func calculateTotalPages(perPage, total uint64) uint64 {
pages := total / perPage
if total%perPage > 0 {
return pages + 1
}
return pages
}
func addPaginationHeaders(w http.ResponseWriter, r *http.Request, p *models.Pagination) {
totalPages := calculateTotalPages(p.PerPage, p.Count)
url, _ := url.ParseRequestURI(r.URL.String())
query := url.Query()
header := ""
if totalPages > p.Page {
query.Set("page", fmt.Sprintf("%v", p.Page+1))
url.RawQuery = query.Encode()
header += "<" + url.String() + ">; rel=\"next\", "
}
query.Set("page", fmt.Sprintf("%v", totalPages))
url.RawQuery = query.Encode()
header += "<" + url.String() + ">; rel=\"last\""
w.Header().Add("Link", header)
w.Header().Add("X-Total-Count", fmt.Sprintf("%v", p.Count))
}
func paginate(r *http.Request) (*models.Pagination, error) {
params := r.URL.Query()
queryPage := params.Get("page")
queryPerPage := params.Get("per_page")
var page uint64 = 1
var perPage uint64 = defaultPerPage
var err error
if queryPage != "" {
page, err = strconv.ParseUint(queryPage, 10, 64)
if err != nil {
return nil, err
}
}
if queryPerPage != "" {
perPage, err = strconv.ParseUint(queryPerPage, 10, 64)
if err != nil {
return nil, err
}
}
return &models.Pagination{
Page: page,
PerPage: perPage,
}, nil
}
View
@@ -5,6 +5,7 @@ import (
"encoding/json"
"net/http"
"net/http/httptest"
"os"
"testing"
"time"
@@ -21,6 +22,14 @@ type RecoverTestSuite struct {
Config *conf.Configuration
}
func (ts *RecoverTestSuite) SetupSuite() {
require.NoError(ts.T(), os.Setenv("GOTRUE_DB_DATABASE_URL", createTestDB()))
}
func (ts *RecoverTestSuite) TearDownSuite() {
os.Remove(ts.API.config.DB.URL)
}
func (ts *RecoverTestSuite) SetupTest() {
api, err := NewAPIFromConfigFile("test.env", "v1")
require.NoError(ts.T(), err)
View
@@ -21,6 +21,14 @@ type SignupTestSuite struct {
Config *conf.Configuration
}
func (ts *SignupTestSuite) SetupSuite() {
require.NoError(ts.T(), os.Setenv("GOTRUE_DB_DATABASE_URL", createTestDB()))
}
func (ts *SignupTestSuite) TearDownSuite() {
os.Remove(ts.API.config.DB.URL)
}
func (ts *SignupTestSuite) SetupTest() {
api, err := NewAPIFromConfigFile("test.env", "v1")
require.NoError(ts.T(), err)
View
@@ -3,7 +3,6 @@ GOTRUE_JWT_EXP=3600
GOTRUE_JWT_AUD=api.netlify.com
GOTRUE_DB_DRIVER=sqlite3
GOTRUE_DB_AUTOMIGRATE=true
GOTRUE_DB_DATABASE_URL=test.db
GOTRUE_API_HOST=localhost
PORT=9999
GOTRUE_LOG_LEVEL=error
View
@@ -6,6 +6,7 @@ import (
"fmt"
"net/http"
"net/http/httptest"
"os"
"testing"
"time"
@@ -22,6 +23,14 @@ type UserTestSuite struct {
Config *conf.Configuration
}
func (ts *UserTestSuite) SetupSuite() {
require.NoError(ts.T(), os.Setenv("GOTRUE_DB_DATABASE_URL", createTestDB()))
}
func (ts *UserTestSuite) TearDownSuite() {
os.Remove(ts.API.config.DB.URL)
}
func (ts *UserTestSuite) SetupTest() {
api, err := NewAPIFromConfigFile("test.env", "v1")
require.NoError(ts.T(), err)
View
@@ -0,0 +1,11 @@
package api
import "io/ioutil"
func createTestDB() string {
f, err := ioutil.TempFile("", "test-db")
if err != nil {
panic(err)
}
return f.Name()
}
View
@@ -15,3 +15,13 @@ func tableName(modelName string) string {
}
return modelName
}
type Pagination struct {
Page uint64
PerPage uint64
Count uint64
}
func (p *Pagination) Offset() uint64 {
return (p.Page - 1) * p.PerPage
}
View
@@ -73,11 +73,24 @@ func (conn *Connection) DeleteUser(user *models.User) error {
}
// FindUsersInAudience finds users that belong to the provided audience.
func (conn *Connection) FindUsersInAudience(instanceID string, aud string) ([]*models.User, error) {
func (conn *Connection) FindUsersInAudience(instanceID string, aud string, pageParams *models.Pagination) ([]*models.User, error) {
user := &models.User{}
users := []*models.User{}
c := conn.db.C(user.TableName())
err := c.Find(bson.M{"instance_id": instanceID, "aud": aud}).All(&users)
q := c.Find(bson.M{"instance_id": instanceID, "aud": aud})
var err error
if pageParams != nil {
count, err := q.Count()
if err != nil {
return nil, err
}
pageParams.Count = uint64(count)
err = q.Skip(int(pageParams.Offset())).Limit(int(pageParams.PerPage)).All(&users)
} else {
err = q.All(&users)
}
return users, err
}
Oops, something went wrong.

0 comments on commit 27528ce

Please sign in to comment.