Tests[RequestLogSimple]
This commit is contained in:
parent
b2df0a5a02
commit
03f60ff316
@ -77,6 +77,8 @@
|
|||||||
|
|
||||||
- Pagination for ListChannels / ListSubscriptions / ListClients / ListChannelSubscriptions / ListUserSubscriptions
|
- Pagination for ListChannels / ListSubscriptions / ListClients / ListChannelSubscriptions / ListUserSubscriptions
|
||||||
|
|
||||||
|
- Add .Insert() function to sq.DB interface (auto generate insert for an object based on struct keys)
|
||||||
|
|
||||||
- cannot open sqlite in dbbrowsr (cannot parse schema?)
|
- cannot open sqlite in dbbrowsr (cannot parse schema?)
|
||||||
-> https://github.com/sqlitebrowser/sqlitebrowser/issues/292 -> https://github.com/sqlitebrowser/sqlitebrowser/issues/29266
|
-> https://github.com/sqlitebrowser/sqlitebrowser/issues/292 -> https://github.com/sqlitebrowser/sqlitebrowser/issues/29266
|
||||||
|
|
||||||
|
@ -124,7 +124,7 @@ func createRequestLog(g *gin.Context, t0 time.Time, ctr int, resp HTTPResponse,
|
|||||||
RequestBodySize: int64(len(reqbody)),
|
RequestBodySize: int64(len(reqbody)),
|
||||||
RequestContentType: ct,
|
RequestContentType: ct,
|
||||||
RemoteIP: g.RemoteIP(),
|
RemoteIP: g.RemoteIP(),
|
||||||
TokenID: langext.ConditionalFn10(hasTok, func() *models.KeyTokenID { return langext.Ptr(permObj.(models.PermissionSet).Token.KeyTokenID) }, nil),
|
KeyID: langext.ConditionalFn10(hasTok, func() *models.KeyTokenID { return langext.Ptr(permObj.(models.PermissionSet).Token.KeyTokenID) }, nil),
|
||||||
UserID: langext.ConditionalFn10(hasTok, func() *models.UserID { return langext.Ptr(permObj.(models.PermissionSet).Token.OwnerUserID) }, nil),
|
UserID: langext.ConditionalFn10(hasTok, func() *models.UserID { return langext.Ptr(permObj.(models.PermissionSet).Token.OwnerUserID) }, nil),
|
||||||
Permissions: langext.ConditionalFn10(hasTok, func() *string { return langext.Ptr(permObj.(models.PermissionSet).Token.Permissions.String()) }, nil),
|
Permissions: langext.ConditionalFn10(hasTok, func() *string { return langext.Ptr(permObj.(models.PermissionSet).Token.Permissions.String()) }, nil),
|
||||||
ResponseStatuscode: langext.ConditionalFn10(resp != nil, func() *int64 { return langext.Ptr(int64(resp.Statuscode())) }, nil),
|
ResponseStatuscode: langext.ConditionalFn10(resp != nil, func() *int64 { return langext.Ptr(int64(resp.Statuscode())) }, nil),
|
||||||
|
@ -900,7 +900,7 @@ func (h APIHandler) ListChannelMessages(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
ChannelID: langext.Ptr([]models.ChannelID{channel.ChannelID}),
|
ChannelID: langext.Ptr([]models.ChannelID{channel.ChannelID}),
|
||||||
}
|
}
|
||||||
|
|
||||||
messages, npt, err := h.database.ListMessages(ctx, filter, pageSize, tok)
|
messages, npt, err := h.database.ListMessages(ctx, filter, &pageSize, tok)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query messages", err)
|
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query messages", err)
|
||||||
}
|
}
|
||||||
@ -1398,7 +1398,7 @@ func (h APIHandler) ListMessages(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
filter.SearchString = langext.Ptr([]string{strings.TrimSpace(*q.Filter)})
|
filter.SearchString = langext.Ptr([]string{strings.TrimSpace(*q.Filter)})
|
||||||
}
|
}
|
||||||
|
|
||||||
messages, npt, err := h.database.ListMessages(ctx, filter, pageSize, tok)
|
messages, npt, err := h.database.ListMessages(ctx, filter, &pageSize, tok)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query messages", err)
|
return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to query messages", err)
|
||||||
}
|
}
|
||||||
@ -1561,7 +1561,7 @@ func (h APIHandler) ListUserKeys(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
UserID models.UserID `uri:"uid" binding:"entityid"`
|
UserID models.UserID `uri:"uid" binding:"entityid"`
|
||||||
}
|
}
|
||||||
type response struct {
|
type response struct {
|
||||||
Tokens []models.KeyTokenJSON `json:"tokens"`
|
Keys []models.KeyTokenJSON `json:"keys"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var u uri
|
var u uri
|
||||||
@ -1582,7 +1582,7 @@ func (h APIHandler) ListUserKeys(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
|
|
||||||
res := langext.ArrMap(clients, func(v models.KeyToken) models.KeyTokenJSON { return v.JSON() })
|
res := langext.ArrMap(clients, func(v models.KeyToken) models.KeyTokenJSON { return v.JSON() })
|
||||||
|
|
||||||
return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, response{Tokens: res}))
|
return ctx.FinishSuccess(ginresp.JSON(http.StatusOK, response{Keys: res}))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUserKey swaggerdoc
|
// GetUserKey swaggerdoc
|
||||||
|
@ -527,7 +527,7 @@ func (h CompatHandler) Requery(g *gin.Context) ginresp.HTTPResponse {
|
|||||||
CompatAcknowledged: langext.Ptr(false),
|
CompatAcknowledged: langext.Ptr(false),
|
||||||
}
|
}
|
||||||
|
|
||||||
msgs, _, err := h.database.ListMessages(ctx, filter, 16, ct.Start())
|
msgs, _, err := h.database.ListMessages(ctx, filter, langext.Ptr(16), ct.Start())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ginresp.CompatAPIError(0, "Failed to query user")
|
return ginresp.CompatAPIError(0, "Failed to query user")
|
||||||
}
|
}
|
||||||
|
@ -118,7 +118,7 @@ func (db *Database) DeleteMessage(ctx TxContext, messageID models.MessageID) err
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *Database) ListMessages(ctx TxContext, filter models.MessageFilter, pageSize int, inTok ct.CursorToken) ([]models.Message, ct.CursorToken, error) {
|
func (db *Database) ListMessages(ctx TxContext, filter models.MessageFilter, pageSize *int, inTok ct.CursorToken) ([]models.Message, ct.CursorToken, error) {
|
||||||
tx, err := ctx.GetOrCreateTransaction(db)
|
tx, err := ctx.GetOrCreateTransaction(db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, ct.CursorToken{}, err
|
return nil, ct.CursorToken{}, err
|
||||||
@ -135,11 +135,16 @@ func (db *Database) ListMessages(ctx TxContext, filter models.MessageFilter, pag
|
|||||||
|
|
||||||
filterCond, filterJoin, prepParams, err := filter.SQL()
|
filterCond, filterJoin, prepParams, err := filter.SQL()
|
||||||
|
|
||||||
orderClause := "ORDER BY COALESCE(timestamp_client, timestamp_real) DESC, message_id DESC LIMIT :lim"
|
orderClause := ""
|
||||||
|
if pageSize != nil {
|
||||||
|
orderClause = "ORDER BY COALESCE(timestamp_client, timestamp_real) DESC, message_id DESC LIMIT :lim"
|
||||||
|
prepParams["lim"] = *pageSize + 1
|
||||||
|
} else {
|
||||||
|
orderClause = "ORDER BY COALESCE(timestamp_client, timestamp_real) DESC, message_id DESC"
|
||||||
|
}
|
||||||
|
|
||||||
sqlQuery := "SELECT " + "messages.*" + " FROM messages " + filterJoin + " WHERE ( " + pageCond + " ) AND ( " + filterCond + " ) " + orderClause
|
sqlQuery := "SELECT " + "messages.*" + " FROM messages " + filterJoin + " WHERE ( " + pageCond + " ) AND ( " + filterCond + " ) " + orderClause
|
||||||
|
|
||||||
prepParams["lim"] = pageSize + 1
|
|
||||||
prepParams["tokts"] = inTok.Timestamp
|
prepParams["tokts"] = inTok.Timestamp
|
||||||
prepParams["tokid"] = inTok.Id
|
prepParams["tokid"] = inTok.Id
|
||||||
|
|
||||||
@ -153,10 +158,10 @@ func (db *Database) ListMessages(ctx TxContext, filter models.MessageFilter, pag
|
|||||||
return nil, ct.CursorToken{}, err
|
return nil, ct.CursorToken{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(data) <= pageSize {
|
if pageSize == nil || len(data) <= *pageSize {
|
||||||
return data, ct.End(), nil
|
return data, ct.End(), nil
|
||||||
} else {
|
} else {
|
||||||
outToken := ct.Normal(data[pageSize-1].Timestamp(), data[pageSize-1].MessageID.String(), "DESC", filter.Hash())
|
outToken := ct.Normal(data[*pageSize-1].Timestamp(), data[*pageSize-1].MessageID.String(), "DESC", filter.Hash())
|
||||||
return data[0:pageSize], outToken, nil
|
return data[0:*pageSize], outToken, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package requests
|
package requests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
ct "blackforestbytes.com/simplecloudnotifier/db/cursortoken"
|
||||||
"blackforestbytes.com/simplecloudnotifier/models"
|
"blackforestbytes.com/simplecloudnotifier/models"
|
||||||
"context"
|
"context"
|
||||||
"gogs.mikescher.com/BlackForestBytes/goext/sq"
|
"gogs.mikescher.com/BlackForestBytes/goext/sq"
|
||||||
@ -11,7 +12,7 @@ func (db *Database) InsertRequestLog(ctx context.Context, requestid models.Reque
|
|||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
_, err := db.db.Exec(ctx, "INSERT INTO requests (request_id, method, uri, user_agent, authentication, request_body, request_body_size, request_content_type, remote_ip, userid, permissions, response_statuscode, response_body_size, response_body, response_content_type, retry_count, panicked, panic_str, processing_time, timestamp_created, timestamp_start, timestamp_finish) VALUES (:request_id, :method, :uri, :user_agent, :authentication, :request_body, :request_body_size, :request_content_type, :remote_ip, :userid, :permissions, :response_statuscode, :response_body_size, :response_body, :response_content_type, :retry_count, :panicked, :panic_str, :processing_time, :timestamp_created, :timestamp_start, :timestamp_finish)", sq.PP{
|
_, err := db.db.Exec(ctx, "INSERT INTO requests (request_id, method, uri, user_agent, authentication, request_body, request_body_size, request_content_type, remote_ip, userid, permissions, response_statuscode, response_body_size, response_body, response_content_type, retry_count, panicked, panic_str, processing_time, timestamp_created, timestamp_start, timestamp_finish, key_id) VALUES (:request_id, :method, :uri, :user_agent, :authentication, :request_body, :request_body_size, :request_content_type, :remote_ip, :userid, :permissions, :response_statuscode, :response_body_size, :response_body, :response_content_type, :retry_count, :panicked, :panic_str, :processing_time, :timestamp_created, :timestamp_start, :timestamp_finish, :kid)", sq.PP{
|
||||||
"request_id": requestid,
|
"request_id": requestid,
|
||||||
"method": data.Method,
|
"method": data.Method,
|
||||||
"uri": data.URI,
|
"uri": data.URI,
|
||||||
@ -34,6 +35,7 @@ func (db *Database) InsertRequestLog(ctx context.Context, requestid models.Reque
|
|||||||
"timestamp_created": now.UnixMilli(),
|
"timestamp_created": now.UnixMilli(),
|
||||||
"timestamp_start": data.TimestampStart,
|
"timestamp_start": data.TimestampStart,
|
||||||
"timestamp_finish": data.TimestampFinish,
|
"timestamp_finish": data.TimestampFinish,
|
||||||
|
"kid": data.KeyID,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return models.RequestLogDB{}, err
|
return models.RequestLogDB{}, err
|
||||||
@ -62,6 +64,7 @@ func (db *Database) InsertRequestLog(ctx context.Context, requestid models.Reque
|
|||||||
TimestampCreated: now.UnixMilli(),
|
TimestampCreated: now.UnixMilli(),
|
||||||
TimestampStart: data.TimestampStart,
|
TimestampStart: data.TimestampStart,
|
||||||
TimestampFinish: data.TimestampFinish,
|
TimestampFinish: data.TimestampFinish,
|
||||||
|
KeyID: data.KeyID,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,3 +93,46 @@ func (db *Database) Cleanup(ctx context.Context, count int, duration time.Durati
|
|||||||
|
|
||||||
return affected1 + affected2, nil
|
return affected1 + affected2, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (db *Database) ListRequestLogs(ctx context.Context, filter models.RequestLogFilter, pageSize *int, inTok ct.CursorToken) ([]models.RequestLog, ct.CursorToken, error) {
|
||||||
|
if inTok.Mode == ct.CTMEnd {
|
||||||
|
return make([]models.RequestLog, 0), ct.End(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
pageCond := "1=1"
|
||||||
|
if inTok.Mode == ct.CTMNormal {
|
||||||
|
pageCond = "timestamp_created < :tokts OR (timestamp_created = :tokts AND request_id < :tokid )"
|
||||||
|
}
|
||||||
|
|
||||||
|
filterCond, filterJoin, prepParams, err := filter.SQL()
|
||||||
|
|
||||||
|
orderClause := ""
|
||||||
|
if pageSize != nil {
|
||||||
|
orderClause = "ORDER BY timestamp_created DESC, request_id DESC LIMIT :lim"
|
||||||
|
prepParams["lim"] = *pageSize + 1
|
||||||
|
} else {
|
||||||
|
orderClause = "ORDER BY timestamp_created DESC, request_id DESC"
|
||||||
|
}
|
||||||
|
|
||||||
|
sqlQuery := "SELECT " + "requests.*" + " FROM requests " + filterJoin + " WHERE ( " + pageCond + " ) AND ( " + filterCond + " ) " + orderClause
|
||||||
|
|
||||||
|
prepParams["tokts"] = inTok.Timestamp
|
||||||
|
prepParams["tokid"] = inTok.Id
|
||||||
|
|
||||||
|
rows, err := db.db.Query(ctx, sqlQuery, prepParams)
|
||||||
|
if err != nil {
|
||||||
|
return nil, ct.CursorToken{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := models.DecodeRequestLogs(rows)
|
||||||
|
if err != nil {
|
||||||
|
return nil, ct.CursorToken{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if pageSize == nil || len(data) <= *pageSize {
|
||||||
|
return data, ct.End(), nil
|
||||||
|
} else {
|
||||||
|
outToken := ct.Normal(data[*pageSize-1].TimestampCreated, data[*pageSize-1].RequestID.String(), "DESC", filter.Hash())
|
||||||
|
return data[0:*pageSize], outToken, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -11,6 +11,7 @@ CREATE TABLE `requests`
|
|||||||
request_body_size INTEGER NOT NULL,
|
request_body_size INTEGER NOT NULL,
|
||||||
request_content_type TEXT NOT NULL,
|
request_content_type TEXT NOT NULL,
|
||||||
remote_ip TEXT NOT NULL,
|
remote_ip TEXT NOT NULL,
|
||||||
|
key_id TEXT NULL,
|
||||||
|
|
||||||
userid TEXT NULL,
|
userid TEXT NULL,
|
||||||
permissions TEXT NULL,
|
permissions TEXT NULL,
|
||||||
|
@ -18,7 +18,7 @@ type RequestLog struct {
|
|||||||
RequestBodySize int64
|
RequestBodySize int64
|
||||||
RequestContentType string
|
RequestContentType string
|
||||||
RemoteIP string
|
RemoteIP string
|
||||||
TokenID *KeyTokenID
|
KeyID *KeyTokenID
|
||||||
UserID *UserID
|
UserID *UserID
|
||||||
Permissions *string
|
Permissions *string
|
||||||
ResponseStatuscode *int64
|
ResponseStatuscode *int64
|
||||||
@ -45,7 +45,7 @@ func (c RequestLog) JSON() RequestLogJSON {
|
|||||||
RequestBodySize: c.RequestBodySize,
|
RequestBodySize: c.RequestBodySize,
|
||||||
RequestContentType: c.RequestContentType,
|
RequestContentType: c.RequestContentType,
|
||||||
RemoteIP: c.RemoteIP,
|
RemoteIP: c.RemoteIP,
|
||||||
TokenID: c.TokenID,
|
KeyID: c.KeyID,
|
||||||
UserID: c.UserID,
|
UserID: c.UserID,
|
||||||
Permissions: c.Permissions,
|
Permissions: c.Permissions,
|
||||||
ResponseStatuscode: c.ResponseStatuscode,
|
ResponseStatuscode: c.ResponseStatuscode,
|
||||||
@ -73,7 +73,7 @@ func (c RequestLog) DB() RequestLogDB {
|
|||||||
RequestBodySize: c.RequestBodySize,
|
RequestBodySize: c.RequestBodySize,
|
||||||
RequestContentType: c.RequestContentType,
|
RequestContentType: c.RequestContentType,
|
||||||
RemoteIP: c.RemoteIP,
|
RemoteIP: c.RemoteIP,
|
||||||
TokenID: c.TokenID,
|
KeyID: c.KeyID,
|
||||||
UserID: c.UserID,
|
UserID: c.UserID,
|
||||||
Permissions: c.Permissions,
|
Permissions: c.Permissions,
|
||||||
ResponseStatuscode: c.ResponseStatuscode,
|
ResponseStatuscode: c.ResponseStatuscode,
|
||||||
@ -100,7 +100,7 @@ type RequestLogJSON struct {
|
|||||||
RequestBodySize int64 `json:"request_body_size"`
|
RequestBodySize int64 `json:"request_body_size"`
|
||||||
RequestContentType string `json:"request_content_type"`
|
RequestContentType string `json:"request_content_type"`
|
||||||
RemoteIP string `json:"remote_ip"`
|
RemoteIP string `json:"remote_ip"`
|
||||||
TokenID *KeyTokenID `json:"token_id"`
|
KeyID *KeyTokenID `json:"key_id"`
|
||||||
UserID *UserID `json:"userid"`
|
UserID *UserID `json:"userid"`
|
||||||
Permissions *string `json:"permissions"`
|
Permissions *string `json:"permissions"`
|
||||||
ResponseStatuscode *int64 `json:"response_statuscode"`
|
ResponseStatuscode *int64 `json:"response_statuscode"`
|
||||||
@ -117,7 +117,7 @@ type RequestLogJSON struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type RequestLogDB struct {
|
type RequestLogDB struct {
|
||||||
RequestID RequestID `db:"requestLog_id"`
|
RequestID RequestID `db:"request_id"`
|
||||||
Method string `db:"method"`
|
Method string `db:"method"`
|
||||||
URI string `db:"uri"`
|
URI string `db:"uri"`
|
||||||
UserAgent *string `db:"user_agent"`
|
UserAgent *string `db:"user_agent"`
|
||||||
@ -126,13 +126,13 @@ type RequestLogDB struct {
|
|||||||
RequestBodySize int64 `db:"request_body_size"`
|
RequestBodySize int64 `db:"request_body_size"`
|
||||||
RequestContentType string `db:"request_content_type"`
|
RequestContentType string `db:"request_content_type"`
|
||||||
RemoteIP string `db:"remote_ip"`
|
RemoteIP string `db:"remote_ip"`
|
||||||
TokenID *KeyTokenID `db:"token_id"`
|
KeyID *KeyTokenID `db:"key_id"`
|
||||||
UserID *UserID `db:"userid"`
|
UserID *UserID `db:"userid"`
|
||||||
Permissions *string `db:"permissions"`
|
Permissions *string `db:"permissions"`
|
||||||
ResponseStatuscode *int64 `db:"response_statuscode"`
|
ResponseStatuscode *int64 `db:"response_statuscode"`
|
||||||
ResponseBodySize *int64 `db:"response_body_size"`
|
ResponseBodySize *int64 `db:"response_body_size"`
|
||||||
ResponseBody *string `db:"response_body"`
|
ResponseBody *string `db:"response_body"`
|
||||||
ResponseContentType string `db:"request_content_type"`
|
ResponseContentType string `db:"response_content_type"`
|
||||||
RetryCount int64 `db:"retry_count"`
|
RetryCount int64 `db:"retry_count"`
|
||||||
Panicked int64 `db:"panicked"`
|
Panicked int64 `db:"panicked"`
|
||||||
PanicStr *string `db:"panic_str"`
|
PanicStr *string `db:"panic_str"`
|
||||||
@ -153,6 +153,7 @@ func (c RequestLogDB) Model() RequestLog {
|
|||||||
RequestBodySize: c.RequestBodySize,
|
RequestBodySize: c.RequestBodySize,
|
||||||
RequestContentType: c.RequestContentType,
|
RequestContentType: c.RequestContentType,
|
||||||
RemoteIP: c.RemoteIP,
|
RemoteIP: c.RemoteIP,
|
||||||
|
KeyID: c.KeyID,
|
||||||
UserID: c.UserID,
|
UserID: c.UserID,
|
||||||
Permissions: c.Permissions,
|
Permissions: c.Permissions,
|
||||||
ResponseStatuscode: c.ResponseStatuscode,
|
ResponseStatuscode: c.ResponseStatuscode,
|
||||||
|
45
scnserver/models/requestlogfilter.go
Normal file
45
scnserver/models/requestlogfilter.go
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha512"
|
||||||
|
"encoding/hex"
|
||||||
|
"gogs.mikescher.com/BlackForestBytes/goext/dataext"
|
||||||
|
"gogs.mikescher.com/BlackForestBytes/goext/mathext"
|
||||||
|
"gogs.mikescher.com/BlackForestBytes/goext/sq"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RequestLogFilter struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f RequestLogFilter) SQL() (string, string, sq.PP, error) {
|
||||||
|
|
||||||
|
joinClause := ""
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
sqlClauses := make([]string, 0)
|
||||||
|
|
||||||
|
params := sq.PP{}
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
sqlClause := ""
|
||||||
|
if len(sqlClauses) > 0 {
|
||||||
|
sqlClause = strings.Join(sqlClauses, " AND ")
|
||||||
|
} else {
|
||||||
|
sqlClause = "1=1"
|
||||||
|
}
|
||||||
|
|
||||||
|
return sqlClause, joinClause, params, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f RequestLogFilter) Hash() string {
|
||||||
|
bh, err := dataext.StructHash(f, dataext.StructHashOptions{HashAlgo: sha512.New()})
|
||||||
|
if err != nil {
|
||||||
|
return "00000000"
|
||||||
|
}
|
||||||
|
|
||||||
|
str := hex.EncodeToString(bh)
|
||||||
|
return str[0:mathext.Min(8, len(bh))]
|
||||||
|
}
|
56
scnserver/test/keytoken_test.go
Normal file
56
scnserver/test/keytoken_test.go
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
import (
|
||||||
|
tt "blackforestbytes.com/simplecloudnotifier/test/util"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestListUserKeys(t *testing.T) {
|
||||||
|
ws, baseUrl, stop := tt.StartSimpleWebserver(t)
|
||||||
|
defer stop()
|
||||||
|
|
||||||
|
data := tt.InitSingleData(t, ws)
|
||||||
|
|
||||||
|
type keylist struct {
|
||||||
|
Tokens []struct {
|
||||||
|
AllChannels bool `json:"all_channels"`
|
||||||
|
Channels []string `json:"channels"`
|
||||||
|
KeytokenId string `json:"keytoken_id"`
|
||||||
|
MessagesSent int `json:"messages_sent"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
OwnerUserId string `json:"owner_user_id"`
|
||||||
|
Permissions string `json:"permissions"`
|
||||||
|
} `json:"tokens"`
|
||||||
|
}
|
||||||
|
|
||||||
|
klist := tt.RequestAuthGet[keylist](t, data.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s/keys", data.UserID))
|
||||||
|
|
||||||
|
tt.AssertEqual(t, "len(keys)", 1, len(klist.Tokens))
|
||||||
|
|
||||||
|
t.SkipNow() //TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateUserKey(t *testing.T) {
|
||||||
|
t.SkipNow() //TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeleteUserKey(t *testing.T) {
|
||||||
|
t.SkipNow() //TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetUserKey(t *testing.T) {
|
||||||
|
t.SkipNow() //TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUpdateUserKey(t *testing.T) {
|
||||||
|
t.SkipNow() //TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUserKeyPermissions(t *testing.T) {
|
||||||
|
t.SkipNow() //TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUsedKeyInMessage(t *testing.T) {
|
||||||
|
t.SkipNow() //TODO
|
||||||
|
}
|
@ -1,3 +1,126 @@
|
|||||||
package test
|
package test
|
||||||
|
|
||||||
//TODO test requestlog
|
import (
|
||||||
|
ct "blackforestbytes.com/simplecloudnotifier/db/cursortoken"
|
||||||
|
"blackforestbytes.com/simplecloudnotifier/models"
|
||||||
|
tt "blackforestbytes.com/simplecloudnotifier/test/util"
|
||||||
|
"fmt"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"net/url"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRequestLogSimple(t *testing.T) {
|
||||||
|
ws, baseUrl, stop := tt.StartSimpleWebserver(t)
|
||||||
|
defer stop()
|
||||||
|
|
||||||
|
ctx := ws.NewSimpleTransactionContext(5 * time.Second)
|
||||||
|
defer ctx.Cancel()
|
||||||
|
|
||||||
|
// Ping
|
||||||
|
{
|
||||||
|
tt.RequestGet[tt.Void](t, baseUrl, fmt.Sprintf("/api/ping"))
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
||||||
|
rl, _, err := ws.Database.Requests.ListRequestLogs(ctx, models.RequestLogFilter{}, nil, ct.Start())
|
||||||
|
tt.TestFailIfErr(t, err)
|
||||||
|
|
||||||
|
tt.AssertEqual(t, "requestlog.count", 1, len(rl))
|
||||||
|
|
||||||
|
tt.AssertEqual(t, "requestlog[0].Method", "GET", rl[0].Method)
|
||||||
|
tt.AssertEqual(t, "requestlog[0].KeyID", nil, rl[0].KeyID)
|
||||||
|
tt.AssertEqual(t, "requestlog[0].UserID", nil, rl[0].UserID)
|
||||||
|
tt.AssertEqual(t, "requestlog[0].Panicked", false, rl[0].Panicked)
|
||||||
|
tt.AssertEqual(t, "requestlog[0].URI", "/api/ping", rl[0].URI)
|
||||||
|
tt.AssertEqual(t, "requestlog[0].ResponseContentType", "application/json", rl[0].ResponseContentType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTMl request
|
||||||
|
{
|
||||||
|
tt.RequestRaw(t, baseUrl, fmt.Sprintf("/"))
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
||||||
|
rl, _, err := ws.Database.Requests.ListRequestLogs(ctx, models.RequestLogFilter{}, nil, ct.Start())
|
||||||
|
tt.TestFailIfErr(t, err)
|
||||||
|
|
||||||
|
tt.AssertEqual(t, "requestlog.count", 2, len(rl))
|
||||||
|
|
||||||
|
tt.AssertEqual(t, "requestlog[0].Method", "GET", rl[0].Method)
|
||||||
|
tt.AssertEqual(t, "requestlog[0].KeyID", nil, rl[0].KeyID)
|
||||||
|
tt.AssertEqual(t, "requestlog[0].UserID", nil, rl[0].UserID)
|
||||||
|
tt.AssertEqual(t, "requestlog[0].Panicked", false, rl[0].Panicked)
|
||||||
|
tt.AssertEqual(t, "requestlog[0].URI", "/", rl[0].URI)
|
||||||
|
tt.AssertEqual(t, "requestlog[0].ResponseContentType", "text/html", rl[0].ResponseContentType)
|
||||||
|
}
|
||||||
|
|
||||||
|
type R struct {
|
||||||
|
Clients []struct {
|
||||||
|
ClientId string `json:"client_id"`
|
||||||
|
UserId string `json:"user_id"`
|
||||||
|
} `json:"clients"`
|
||||||
|
ReadKey string `json:"read_key"`
|
||||||
|
SendKey string `json:"send_key"`
|
||||||
|
AdminKey string `json:"admin_key"`
|
||||||
|
UserId string `json:"user_id"`
|
||||||
|
}
|
||||||
|
usr := tt.RequestPost[R](t, baseUrl, "/api/v2/users", gin.H{
|
||||||
|
"agent_model": "DUMMY_PHONE",
|
||||||
|
"agent_version": "4X",
|
||||||
|
"client_type": "ANDROID",
|
||||||
|
"fcm_token": "DUMMY_FCM",
|
||||||
|
})
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
||||||
|
// API request
|
||||||
|
{
|
||||||
|
|
||||||
|
tt.RequestAuthGet[R](t, usr.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s", usr.UserId))
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
||||||
|
rl, _, err := ws.Database.Requests.ListRequestLogs(ctx, models.RequestLogFilter{}, nil, ct.Start())
|
||||||
|
tt.TestFailIfErr(t, err)
|
||||||
|
|
||||||
|
tt.AssertEqual(t, "requestlog.count", 4, len(rl))
|
||||||
|
|
||||||
|
tt.AssertEqual(t, "requestlog[0].Method", "GET", rl[0].Method)
|
||||||
|
tt.AssertNotEqual(t, "requestlog[0].KeyID", nil, rl[0].KeyID)
|
||||||
|
tt.AssertStrRepEqual(t, "requestlog[0].UserID", usr.UserId, rl[0].UserID)
|
||||||
|
tt.AssertEqual(t, "requestlog[0].Panicked", false, rl[0].Panicked)
|
||||||
|
tt.AssertStrRepEqual(t, "requestlog[0].Permissions", "A", rl[0].Permissions)
|
||||||
|
tt.AssertEqual(t, "requestlog[0].URI", fmt.Sprintf("/api/v2/users/%s", usr.UserId), rl[0].URI)
|
||||||
|
tt.AssertEqual(t, "requestlog[0].ResponseContentType", "application/json", rl[0].ResponseContentType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send request
|
||||||
|
{
|
||||||
|
tt.RequestPost[gin.H](t, baseUrl, fmt.Sprintf("/?user_id=%s&key=%s&title=%s", usr.UserId, usr.SendKey, url.QueryEscape("Hello World 2134")), nil)
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
||||||
|
rl, _, err := ws.Database.Requests.ListRequestLogs(ctx, models.RequestLogFilter{}, nil, ct.Start())
|
||||||
|
tt.TestFailIfErr(t, err)
|
||||||
|
|
||||||
|
tt.AssertEqual(t, "requestlog.count", 5, len(rl))
|
||||||
|
|
||||||
|
tt.AssertEqual(t, "requestlog[0].Method", "POST", rl[0].Method)
|
||||||
|
tt.AssertEqual(t, "requestlog[0].UserID", nil, rl[0].UserID)
|
||||||
|
tt.AssertEqual(t, "requestlog[0].ResponseContentType", "application/json", rl[0].ResponseContentType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Failed request
|
||||||
|
{
|
||||||
|
tt.RequestAuthGetShouldFail(t, usr.AdminKey, baseUrl, fmt.Sprintf("/api/v2/users/%s", models.NewUserID()), 0, 0)
|
||||||
|
time.Sleep(100 * time.Millisecond)
|
||||||
|
|
||||||
|
rl, _, err := ws.Database.Requests.ListRequestLogs(ctx, models.RequestLogFilter{}, nil, ct.Start())
|
||||||
|
tt.TestFailIfErr(t, err)
|
||||||
|
|
||||||
|
tt.AssertEqual(t, "requestlog.count", 6, len(rl))
|
||||||
|
|
||||||
|
tt.AssertEqual(t, "requestlog[0].Method", "GET", rl[0].Method)
|
||||||
|
tt.AssertStrRepEqual(t, "requestlog[0].UserID", usr.UserId, rl[0].UserID)
|
||||||
|
tt.AssertEqual(t, "requestlog[0].ResponseContentType", "application/json", rl[0].ResponseContentType)
|
||||||
|
tt.AssertStrRepEqual(t, "requestlog[0].ResponseStatuscode", 401, rl[0].ResponseStatuscode)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -140,6 +140,10 @@ func AssertEqual(t *testing.T, key string, expected any, actual any) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if langext.IsNil(expected) && langext.IsNil(actual) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if expected != actual {
|
if expected != actual {
|
||||||
t.Errorf("Value [%s] differs (%T <-> %T):\n", key, expected, actual)
|
t.Errorf("Value [%s] differs (%T <-> %T):\n", key, expected, actual)
|
||||||
|
|
||||||
@ -173,7 +177,7 @@ func AssertTrue(t *testing.T, key string, v bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func AssertNotEqual(t *testing.T, key string, expected any, actual any) {
|
func AssertNotEqual(t *testing.T, key string, expected any, actual any) {
|
||||||
if expected == actual {
|
if expected == actual || (langext.IsNil(expected) && langext.IsNil(actual)) {
|
||||||
t.Errorf("Value [%s] does not differ (%T <-> %T):\n", key, expected, actual)
|
t.Errorf("Value [%s] does not differ (%T <-> %T):\n", key, expected, actual)
|
||||||
|
|
||||||
str1 := fmt.Sprintf("%v", expected)
|
str1 := fmt.Sprintf("%v", expected)
|
||||||
|
@ -395,6 +395,57 @@ func InitDefaultData(t *testing.T, ws *logic.Application) DefData {
|
|||||||
return DefData{User: users}
|
return DefData{User: users}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SingleData struct {
|
||||||
|
UserID string
|
||||||
|
AdminKey string
|
||||||
|
SendKey string
|
||||||
|
ReadKey string
|
||||||
|
ClientID string
|
||||||
|
}
|
||||||
|
|
||||||
|
func InitSingleData(t *testing.T, ws *logic.Application) SingleData {
|
||||||
|
|
||||||
|
// set logger to buffer, only output if error occured
|
||||||
|
success := false
|
||||||
|
SetBufLogger()
|
||||||
|
defer func() {
|
||||||
|
ClearBufLogger(!success)
|
||||||
|
if success {
|
||||||
|
log.Info().Msgf("Succesfully initialized default data (%d messages, %d users)", len(messageExamples), len(userExamples))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
baseUrl := "http://127.0.0.1:" + ws.Port
|
||||||
|
|
||||||
|
type R struct {
|
||||||
|
Clients []struct {
|
||||||
|
ClientId string `json:"client_id"`
|
||||||
|
UserId string `json:"user_id"`
|
||||||
|
} `json:"clients"`
|
||||||
|
ReadKey string `json:"read_key"`
|
||||||
|
SendKey string `json:"send_key"`
|
||||||
|
AdminKey string `json:"admin_key"`
|
||||||
|
UserId string `json:"user_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
r0 := RequestPost[R](t, baseUrl, "/api/v2/users", gin.H{
|
||||||
|
"agent_model": "DUMMY_PHONE",
|
||||||
|
"agent_version": "4X",
|
||||||
|
"client_type": "ANDROID",
|
||||||
|
"fcm_token": "DUMMY_FCM",
|
||||||
|
})
|
||||||
|
|
||||||
|
success = true
|
||||||
|
|
||||||
|
return SingleData{
|
||||||
|
UserID: r0.UserId,
|
||||||
|
AdminKey: r0.AdminKey,
|
||||||
|
SendKey: r0.SendKey,
|
||||||
|
ReadKey: r0.ReadKey,
|
||||||
|
ClientID: r0.Clients[0].ClientId,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func doSubscribe(t *testing.T, baseUrl string, user Userdat, chanOwner Userdat, chanInternalName string) {
|
func doSubscribe(t *testing.T, baseUrl string, user Userdat, chanOwner Userdat, chanInternalName string) {
|
||||||
|
|
||||||
if user == chanOwner {
|
if user == chanOwner {
|
||||||
|
@ -14,44 +14,48 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func RequestRaw(t *testing.T, baseURL string, urlSuffix string) {
|
||||||
|
RequestAny[Void](t, "", "GET", baseURL, urlSuffix, nil, false)
|
||||||
|
}
|
||||||
|
|
||||||
func RequestGet[TResult any](t *testing.T, baseURL string, urlSuffix string) TResult {
|
func RequestGet[TResult any](t *testing.T, baseURL string, urlSuffix string) TResult {
|
||||||
return RequestAny[TResult](t, "", "GET", baseURL, urlSuffix, nil)
|
return RequestAny[TResult](t, "", "GET", baseURL, urlSuffix, nil, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RequestAuthGet[TResult any](t *testing.T, akey string, baseURL string, urlSuffix string) TResult {
|
func RequestAuthGet[TResult any](t *testing.T, akey string, baseURL string, urlSuffix string) TResult {
|
||||||
return RequestAny[TResult](t, akey, "GET", baseURL, urlSuffix, nil)
|
return RequestAny[TResult](t, akey, "GET", baseURL, urlSuffix, nil, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RequestPost[TResult any](t *testing.T, baseURL string, urlSuffix string, body any) TResult {
|
func RequestPost[TResult any](t *testing.T, baseURL string, urlSuffix string, body any) TResult {
|
||||||
return RequestAny[TResult](t, "", "POST", baseURL, urlSuffix, body)
|
return RequestAny[TResult](t, "", "POST", baseURL, urlSuffix, body, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RequestAuthPost[TResult any](t *testing.T, akey string, baseURL string, urlSuffix string, body any) TResult {
|
func RequestAuthPost[TResult any](t *testing.T, akey string, baseURL string, urlSuffix string, body any) TResult {
|
||||||
return RequestAny[TResult](t, akey, "POST", baseURL, urlSuffix, body)
|
return RequestAny[TResult](t, akey, "POST", baseURL, urlSuffix, body, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RequestPut[TResult any](t *testing.T, baseURL string, urlSuffix string, body any) TResult {
|
func RequestPut[TResult any](t *testing.T, baseURL string, urlSuffix string, body any) TResult {
|
||||||
return RequestAny[TResult](t, "", "PUT", baseURL, urlSuffix, body)
|
return RequestAny[TResult](t, "", "PUT", baseURL, urlSuffix, body, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RequestAuthPUT[TResult any](t *testing.T, akey string, baseURL string, urlSuffix string, body any) TResult {
|
func RequestAuthPUT[TResult any](t *testing.T, akey string, baseURL string, urlSuffix string, body any) TResult {
|
||||||
return RequestAny[TResult](t, akey, "PUT", baseURL, urlSuffix, body)
|
return RequestAny[TResult](t, akey, "PUT", baseURL, urlSuffix, body, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RequestPatch[TResult any](t *testing.T, baseURL string, urlSuffix string, body any) TResult {
|
func RequestPatch[TResult any](t *testing.T, baseURL string, urlSuffix string, body any) TResult {
|
||||||
return RequestAny[TResult](t, "", "PATCH", baseURL, urlSuffix, body)
|
return RequestAny[TResult](t, "", "PATCH", baseURL, urlSuffix, body, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RequestAuthPatch[TResult any](t *testing.T, akey string, baseURL string, urlSuffix string, body any) TResult {
|
func RequestAuthPatch[TResult any](t *testing.T, akey string, baseURL string, urlSuffix string, body any) TResult {
|
||||||
return RequestAny[TResult](t, akey, "PATCH", baseURL, urlSuffix, body)
|
return RequestAny[TResult](t, akey, "PATCH", baseURL, urlSuffix, body, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RequestDelete[TResult any](t *testing.T, baseURL string, urlSuffix string, body any) TResult {
|
func RequestDelete[TResult any](t *testing.T, baseURL string, urlSuffix string, body any) TResult {
|
||||||
return RequestAny[TResult](t, "", "DELETE", baseURL, urlSuffix, body)
|
return RequestAny[TResult](t, "", "DELETE", baseURL, urlSuffix, body, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RequestAuthDelete[TResult any](t *testing.T, akey string, baseURL string, urlSuffix string, body any) TResult {
|
func RequestAuthDelete[TResult any](t *testing.T, akey string, baseURL string, urlSuffix string, body any) TResult {
|
||||||
return RequestAny[TResult](t, akey, "DELETE", baseURL, urlSuffix, body)
|
return RequestAny[TResult](t, akey, "DELETE", baseURL, urlSuffix, body, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RequestGetShouldFail(t *testing.T, baseURL string, urlSuffix string, statusCode int, errcode apierr.APIError) {
|
func RequestGetShouldFail(t *testing.T, baseURL string, urlSuffix string, statusCode int, errcode apierr.APIError) {
|
||||||
@ -86,7 +90,7 @@ func RequestAuthDeleteShouldFail(t *testing.T, akey string, baseURL string, urlS
|
|||||||
RequestAuthAnyShouldFail(t, akey, "DELETE", baseURL, urlSuffix, body, statusCode, errcode)
|
RequestAuthAnyShouldFail(t, akey, "DELETE", baseURL, urlSuffix, body, statusCode, errcode)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RequestAny[TResult any](t *testing.T, akey string, method string, baseURL string, urlSuffix string, body any) TResult {
|
func RequestAny[TResult any](t *testing.T, akey string, method string, baseURL string, urlSuffix string, body any, deserialize bool) TResult {
|
||||||
client := http.Client{}
|
client := http.Client{}
|
||||||
|
|
||||||
TPrintf("[-> REQUEST] (%s) %s%s [%s] [%s]\n", method, baseURL, urlSuffix, langext.Conditional(akey == "", "NO AUTH", "AUTH"), langext.Conditional(body == nil, "NO BODY", "BODY"))
|
TPrintf("[-> REQUEST] (%s) %s%s [%s] [%s]\n", method, baseURL, urlSuffix, langext.Conditional(akey == "", "NO AUTH", "AUTH"), langext.Conditional(body == nil, "NO BODY", "BODY"))
|
||||||
@ -156,9 +160,11 @@ func RequestAny[TResult any](t *testing.T, akey string, method string, baseURL s
|
|||||||
}
|
}
|
||||||
|
|
||||||
var data TResult
|
var data TResult
|
||||||
|
if deserialize {
|
||||||
if err := json.Unmarshal(respBodyBin, &data); err != nil {
|
if err := json.Unmarshal(respBodyBin, &data); err != nil {
|
||||||
TestFailErr(t, err)
|
TestFailErr(t, err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
@ -320,3 +320,5 @@ code {
|
|||||||
background: #F9F9F9;
|
background: #F9F9F9;
|
||||||
border: .0625rem solid var(--secondary-border-color);
|
border: .0625rem solid var(--secondary-border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
code { white-space: pre; }
|
Loading…
Reference in New Issue
Block a user