GET v1/users is now actually pretty darn cool
This commit is contained in:
parent
dccec469af
commit
fefde77261
|
@ -44,5 +44,5 @@ LEFT JOIN users_stats
|
||||||
ON users.id=users_stats.id
|
ON users.id=users_stats.id
|
||||||
WHERE users.id=?
|
WHERE users.id=?
|
||||||
LIMIT 1`
|
LIMIT 1`
|
||||||
return userPuts(md, md.DB.QueryRowx(query, data.UserID))
|
return userPutsSingle(md, md.DB.QueryRowx(query, data.UserID))
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,33 +22,36 @@ type userData struct {
|
||||||
Country string `json:"country"`
|
Country string `json:"country"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// UsersGET is the API handler for GET /users
|
const userFields = `users.id, users.username, register_datetime, privileges,
|
||||||
func UsersGET(md common.MethodData) common.CodeMessager {
|
|
||||||
shouldRet, whereClause, param := whereClauseUser(md, "users")
|
|
||||||
if shouldRet != nil {
|
|
||||||
return *shouldRet
|
|
||||||
}
|
|
||||||
|
|
||||||
query := `
|
|
||||||
SELECT users.id, users.username, register_datetime, privileges,
|
|
||||||
latest_activity, users_stats.username_aka,
|
latest_activity, users_stats.username_aka,
|
||||||
users_stats.country
|
users_stats.country
|
||||||
FROM users
|
FROM users
|
||||||
INNER JOIN users_stats
|
INNER JOIN users_stats
|
||||||
ON users.id=users_stats.id
|
ON users.id=users_stats.id
|
||||||
WHERE ` + whereClause + ` AND ` + md.User.OnlyUserPublic(true) + `
|
`
|
||||||
LIMIT 1`
|
|
||||||
return userPuts(md, md.DB.QueryRowx(query, param))
|
// UsersGET is the API handler for GET /users
|
||||||
|
func UsersGET(md common.MethodData) common.CodeMessager {
|
||||||
|
shouldRet, whereClause, param := whereClauseUser(md, "users")
|
||||||
|
if shouldRet != nil {
|
||||||
|
return userPutsMulti(md)
|
||||||
}
|
}
|
||||||
|
|
||||||
type userPutsUserData struct {
|
query := `
|
||||||
|
SELECT ` + userFields + `
|
||||||
|
WHERE ` + whereClause + ` AND ` + md.User.OnlyUserPublic(true) + `
|
||||||
|
LIMIT 1`
|
||||||
|
return userPutsSingle(md, md.DB.QueryRowx(query, param))
|
||||||
|
}
|
||||||
|
|
||||||
|
type userPutsSingleUserData struct {
|
||||||
common.ResponseBase
|
common.ResponseBase
|
||||||
userData
|
userData
|
||||||
}
|
}
|
||||||
|
|
||||||
func userPuts(md common.MethodData, row *sqlx.Row) common.CodeMessager {
|
func userPutsSingle(md common.MethodData, row *sqlx.Row) common.CodeMessager {
|
||||||
var err error
|
var err error
|
||||||
var user userPutsUserData
|
var user userPutsSingleUserData
|
||||||
|
|
||||||
err = row.StructScan(&user.userData)
|
err = row.StructScan(&user.userData)
|
||||||
switch {
|
switch {
|
||||||
|
@ -63,6 +66,62 @@ func userPuts(md common.MethodData, row *sqlx.Row) common.CodeMessager {
|
||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type userPutsMultiUserData struct {
|
||||||
|
common.ResponseBase
|
||||||
|
Users []userData `json:"users"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func userPutsMulti(md common.MethodData) common.CodeMessager {
|
||||||
|
q := md.C.Request.URL.Query()
|
||||||
|
|
||||||
|
// query composition
|
||||||
|
wh := common.
|
||||||
|
Where("users.username = ?", md.Query("nname")).
|
||||||
|
Where("users.id = ?", md.Query("iid")).
|
||||||
|
Where("users.privileges = ?", md.Query("privileges")).
|
||||||
|
Where("users.privileges & ? > 0", md.Query("has_privileges")).
|
||||||
|
Where("users_stats.country = ?", md.Query("country")).
|
||||||
|
Where("users_stats.username_aka = ?", md.Query("name_aka")).
|
||||||
|
In("users.id", q["ids"]...).
|
||||||
|
In("users.username", q["names"]...).
|
||||||
|
In("users_stats.username_aka", q["names_aka"]...).
|
||||||
|
In("users_stats.country", q["countries"]...)
|
||||||
|
query := "" +
|
||||||
|
"SELECT " + userFields + wh.ClauseSafe() + " AND " + md.User.OnlyUserPublic(true) +
|
||||||
|
" " + common.Sort(md, common.SortConfiguration{
|
||||||
|
Allowed: []string{
|
||||||
|
"id",
|
||||||
|
"username",
|
||||||
|
"privileges",
|
||||||
|
"donor_expire",
|
||||||
|
"latest_activity",
|
||||||
|
"silence_end",
|
||||||
|
},
|
||||||
|
Default: "id ASC",
|
||||||
|
Table: "users",
|
||||||
|
}) +
|
||||||
|
" " + common.Paginate(md.Query("p"), md.Query("l"), 100)
|
||||||
|
|
||||||
|
// query execution
|
||||||
|
rows, err := md.DB.Queryx(query, wh.Params...)
|
||||||
|
if err != nil {
|
||||||
|
md.Err(err)
|
||||||
|
return Err500
|
||||||
|
}
|
||||||
|
var r userPutsMultiUserData
|
||||||
|
for rows.Next() {
|
||||||
|
var u userData
|
||||||
|
err := rows.StructScan(&u)
|
||||||
|
if err != nil {
|
||||||
|
md.Err(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
r.Users = append(r.Users, u)
|
||||||
|
}
|
||||||
|
r.Code = 200
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
// UserSelfGET is a shortcut for /users/id/self. (/users/self)
|
// UserSelfGET is a shortcut for /users/id/self. (/users/self)
|
||||||
func UserSelfGET(md common.MethodData) common.CodeMessager {
|
func UserSelfGET(md common.MethodData) common.CodeMessager {
|
||||||
md.C.Request.URL.RawQuery = "id=self&" + md.C.Request.URL.RawQuery
|
md.C.Request.URL.RawQuery = "id=self&" + md.C.Request.URL.RawQuery
|
||||||
|
|
|
@ -15,16 +15,57 @@ func (w *WhereClause) Where(clause, passedParam string, allowedValues ...string)
|
||||||
if len(allowedValues) != 0 && !contains(allowedValues, passedParam) {
|
if len(allowedValues) != 0 && !contains(allowedValues, passedParam) {
|
||||||
return w
|
return w
|
||||||
}
|
}
|
||||||
// checks passed, if string is empty add "WHERE"
|
w.addWhere()
|
||||||
|
w.Clause += clause
|
||||||
|
w.Params = append(w.Params, passedParam)
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *WhereClause) addWhere() {
|
||||||
|
// if string is empty add "WHERE", else add AND
|
||||||
if w.Clause == "" {
|
if w.Clause == "" {
|
||||||
w.Clause += "WHERE "
|
w.Clause += "WHERE "
|
||||||
} else {
|
} else {
|
||||||
w.Clause += " AND "
|
w.Clause += " AND "
|
||||||
}
|
}
|
||||||
w.Clause += clause
|
}
|
||||||
w.Params = append(w.Params, passedParam)
|
|
||||||
|
// In generates an IN clause.
|
||||||
|
// initial is the initial part, e.g. "users.id".
|
||||||
|
// Fields are the possible values.
|
||||||
|
// Sample output: users.id IN ('1', '2', '3')
|
||||||
|
func (w *WhereClause) In(initial string, fields ...string) *WhereClause {
|
||||||
|
if len(fields) == 0 {
|
||||||
return w
|
return w
|
||||||
}
|
}
|
||||||
|
w.addWhere()
|
||||||
|
w.Clause += initial + " IN (" + generateQuestionMarks(len(fields)) + ")"
|
||||||
|
fieldsInterfaced := make([]interface{}, 0, len(fields))
|
||||||
|
for _, i := range fields {
|
||||||
|
fieldsInterfaced = append(fieldsInterfaced, interface{}(i))
|
||||||
|
}
|
||||||
|
w.Params = append(w.Params, fieldsInterfaced...)
|
||||||
|
return w
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateQuestionMarks(x int) (qm string) {
|
||||||
|
for i := 0; i < x-1; i++ {
|
||||||
|
qm += "?, "
|
||||||
|
}
|
||||||
|
if x > 0 {
|
||||||
|
qm += "?"
|
||||||
|
}
|
||||||
|
return qm
|
||||||
|
}
|
||||||
|
|
||||||
|
// ClauseSafe returns the clause, always containing something. If w.Clause is
|
||||||
|
// empty, it returns "WHERE 1".
|
||||||
|
func (w *WhereClause) ClauseSafe() string {
|
||||||
|
if w.Clause == "" {
|
||||||
|
return "WHERE 1"
|
||||||
|
}
|
||||||
|
return w.Clause
|
||||||
|
}
|
||||||
|
|
||||||
// Where is the same as WhereClause.Where, but creates a new WhereClause.
|
// Where is the same as WhereClause.Where, but creates a new WhereClause.
|
||||||
func Where(clause, passedParam string, allowedValues ...string) *WhereClause {
|
func Where(clause, passedParam string, allowedValues ...string) *WhereClause {
|
||||||
|
|
96
common/where_test.go
Normal file
96
common/where_test.go
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_generateQuestionMarks(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
x int
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
wantQm string
|
||||||
|
}{
|
||||||
|
{"-1", args{-1}, ""},
|
||||||
|
{"0", args{0}, ""},
|
||||||
|
{"1", args{1}, "?"},
|
||||||
|
{"2", args{2}, "?, ?"},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
if gotQm := generateQuestionMarks(tt.args.x); gotQm != tt.wantQm {
|
||||||
|
t.Errorf("%q. generateQuestionMarks() = %v, want %v", tt.name, gotQm, tt.wantQm)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWhereClause_In(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
initial string
|
||||||
|
fields []interface{}
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
fields *WhereClause
|
||||||
|
args args
|
||||||
|
want *WhereClause
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"simple",
|
||||||
|
&WhereClause{},
|
||||||
|
args{"users.id", []interface{}{"1", "2", "3"}},
|
||||||
|
&WhereClause{"WHERE users.id IN (?, ?, ?)", []interface{}{"1", "2", "3"}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"withExisting",
|
||||||
|
Where("users.username = ?", "Howl").Where("users.xd > ?", "6"),
|
||||||
|
args{"users.id", []interface{}{"1"}},
|
||||||
|
&WhereClause{
|
||||||
|
"WHERE users.username = ? AND users.xd > ? AND users.id IN (?)",
|
||||||
|
[]interface{}{"Howl", "6", "1"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
w := tt.fields
|
||||||
|
if got := w.In(tt.args.initial, tt.args.fields...); !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("%q. WhereClause.In() = %v, want %v", tt.name, got, tt.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWhere(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
clause string
|
||||||
|
passedParam string
|
||||||
|
allowedValues []string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want *WhereClause
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"simple",
|
||||||
|
args{"users.id = ?", "5", nil},
|
||||||
|
&WhereClause{"WHERE users.id = ?", []interface{}{"5"}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allowed",
|
||||||
|
args{"users.id = ?", "5", []string{"1", "3", "5"}},
|
||||||
|
&WhereClause{"WHERE users.id = ?", []interface{}{"5"}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"notAllowed",
|
||||||
|
args{"users.id = ?", "5", []string{"0"}},
|
||||||
|
&WhereClause{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
if got := Where(tt.args.clause, tt.args.passedParam, tt.args.allowedValues...); !reflect.DeepEqual(got, tt.want) {
|
||||||
|
t.Errorf("%q. Where() = %#v, want %#v", tt.name, got, tt.want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user