Commit cc437bd2 authored by MaxGranzow's avatar MaxGranzow

Add endpoint to search for users

parent 723b3145
Pipeline #2215 failed with stage
in 5 minutes and 24 seconds
......@@ -87,9 +87,9 @@ paths:
get:
summary: Search for users by their username.
description: >-
Returns an array of objects representing the app users
Returns an array of objects representing the app users,
whose usernames start with the search term.
Casing is ignored.
Caseing is ignored.
The user objects are stripped down to what is necessary
to know as an app user.
parameters:
......
......@@ -39,6 +39,13 @@ func SetupUserRoutes(group *gin.RouterGroup, repository UserQuery, titleReposito
})
//endpoint used to fetch data of all users
group.GET("/all", getAllUsers(repository))
group.OPTIONS("/search", func(ctx *gin.Context) {
ctx.Header("Allow", "GET")
ctx.Status(200)
})
// endpoint used to search for users by username
group.GET("/search", searchForUsers(repository))
}
/*
......@@ -328,6 +335,29 @@ func getAllUsers(repository UserQuery) gin.HandlerFunc {
}
}
func searchForUsers(repository UserQuery) gin.HandlerFunc {
return func(ctx *gin.Context) {
searchParam := ctx.Query("s")
// Only accept searches with minimum 2 characters
if len(searchParam) < 2 {
ctx.String(http.StatusBadRequest, "Search failed. Specify at least two characters.")
return
}
// fetch matching users from database
users, err := repository.SelectByUsernameLike(searchParam)
if err != nil {
eventID := raven.CaptureError(err, nil)
ctx.String(http.StatusInternalServerError, "Failed fetching data from DB. "+eventID)
return
}
ctx.JSON(http.StatusOK, users)
}
}
func ForgotPassword(repository UserQuery, tokenFactory otp.TokenFactory, templatemail *sendmail.TemplateMail) gin.HandlerFunc {
return func(ctx *gin.Context) {
var req ForgotPasswordRequest
......
......@@ -31,6 +31,7 @@ type UserQuery interface {
Insert(user *schemas.FullUserSchema, password string) (id uint32, err error)
Select(id uint32) ([]schemas.FullUserSchema, error)
SelectByUsername(username string) ([]schemas.InfoUserSchema, error)
SelectByUsernameLike(username string) ([]schemas.InfoUserSchema, error)
SelectCredentialsByField(field UserField, value interface{}) (*schemas.CredentialsSchema, error)
Authenticate(name string, password string) (uint32, error)
Update(userID uint32, params PatchUserRequest, titleRepository title.TitleQuery) error
......@@ -332,6 +333,109 @@ func (MySQLUserQuery) SelectByUsername(username string) ([]schemas.InfoUserSchem
return user, nil
}
// SelectByUsernameLike fetches and returns a list of userdata of all users with matching usernames
func (MySQLUserQuery) SelectByUsernameLike(username string) ([]schemas.InfoUserSchema, error) {
db, connectionError := dbhandler.GetDBConnection()
if connectionError != nil {
return nil, errors.New("Unable to connect to database. " + connectionError.Error())
}
stmt, prepareError := db.Prepare(`
SELECT iduser, username, idavatar, avatar.image, avatar.level, title.idtitle,
title.name, subject.idsubject, subject.name, subject.shortform,
subject.department, subject.description, title.unlock_score, title.unlock_win
FROM user
LEFT JOIN title ON (title.idtitle=selected_title)
LEFT JOIN subject ON (idsubject=title.subject)
LEFT JOIN avatar ON (idavatar=selected_avatar)
WHERE username LIKE ?`)
if prepareError != nil {
return nil, errors.New(
"An error occurred while preparing database access. " + prepareError.Error())
}
defer stmt.Close()
// execute sql query
rows, queryError := stmt.Query(username + "%")
if queryError != nil {
return nil, errors.New("Cannot execute database query. " + queryError.Error())
}
defer rows.Close()
users := []schemas.InfoUserSchema{}
for rows.Next() {
var tmpUser schemas.InfoUserSchema
scanError := scanUser(rows.Scan, &tmpUser)
if scanError != nil {
return nil, errors.New("Cannot scan db values into users list. " + scanError.Error())
}
users = append(users, tmpUser)
}
return users, nil
}
func scanUser(scanFn func(dest ...interface{}) error, tmpUser *schemas.InfoUserSchema) error {
// Temporary subject
var tmpSubjectID sql.NullInt32
var tmpSubjectName sql.NullString
var tmpSubjectShortForm sql.NullString
var tmpSubjectDepartment sql.NullString
var tmpSubjectDescription sql.NullString
var tmpUnlockScore sql.NullInt32
var tmpUnlockWin sql.NullInt32
err := scanFn(
&tmpUser.ID,
&tmpUser.Username,
&tmpUser.SelectedAvatar.ID,
&tmpUser.SelectedAvatar.Image,
&tmpUser.SelectedAvatar.Level,
&tmpUser.SelectedTitle.ID,
&tmpUser.SelectedTitle.Name,
&(tmpSubjectID),
&(tmpSubjectName),
&(tmpSubjectShortForm),
&(tmpSubjectDepartment),
&(tmpSubjectDescription),
&tmpUnlockScore,
&tmpUnlockWin,
)
if err != nil {
return err
}
if tmpSubjectName.Valid {
tmpUser.SelectedTitle.Subject = &schemas.Subject{
ID: uint32(tmpSubjectID.Int32),
Name: tmpSubjectName.String,
ShortForm: tmpSubjectShortForm.String,
Department: tmpSubjectDepartment.String,
Description: tmpSubjectDescription.String,
}
}
if tmpUnlockScore.Valid {
tmpUser.SelectedTitle.UnlockScore = tmpUnlockScore.Int32
} else {
tmpUser.SelectedTitle.UnlockScore = -1
}
if tmpUnlockWin.Valid {
tmpUser.SelectedTitle.UnlockWin = tmpUnlockWin.Int32
} else {
tmpUser.SelectedTitle.UnlockWin = -1
}
return nil
}
func (MySQLUserQuery) Authenticate(name string, password string) (uint32, error) {
db, dberr := dbhandler.GetDBConnection()
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment