2020-10-28 14:35:41 +01:00
package sqlstore
import (
2021-06-04 15:23:15 +02:00
"database/sql"
2022-06-29 14:35:24 +02:00
"errors"
2021-07-09 03:09:02 +02:00
"fmt"
2022-03-22 15:24:34 +01:00
2022-08-29 09:25:12 +02:00
mmModel "github.com/mattermost/mattermost-server/v6/model"
"github.com/mattermost/mattermost-server/v6/store"
2022-03-22 15:24:34 +01:00
sq "github.com/Masterminds/squirrel"
2020-10-28 14:35:41 +01:00
2021-01-26 23:13:46 +01:00
"github.com/mattermost/focalboard/server/model"
2021-10-07 13:51:01 +02:00
"github.com/mattermost/focalboard/server/utils"
2020-10-28 14:35:41 +01:00
2022-03-22 15:24:34 +01:00
"github.com/mattermost/mattermost-server/v6/shared/mlog"
2020-10-28 14:35:41 +01:00
)
2022-06-29 14:35:24 +02:00
var (
errUnsupportedOperation = errors . New ( "unsupported operation" )
)
2021-07-09 03:09:02 +02:00
type UserNotFoundError struct {
id string
}
func ( unf UserNotFoundError ) Error ( ) string {
return fmt . Sprintf ( "user not found (%s)" , unf . id )
}
2021-10-22 12:48:53 +02:00
func ( s * SQLStore ) getRegisteredUserCount ( db sq . BaseRunner ) ( int , error ) {
query := s . getQueryBuilder ( db ) .
2021-01-14 01:56:01 +01:00
Select ( "count(*)" ) .
2021-04-17 09:06:57 +02:00
From ( s . tablePrefix + "users" ) .
2021-01-14 01:56:01 +01:00
Where ( sq . Eq { "delete_at" : 0 } )
row := query . QueryRow ( )
var count int
err := row . Scan ( & count )
if err != nil {
return 0 , err
}
return count , nil
}
2021-10-22 12:48:53 +02:00
func ( s * SQLStore ) getUserByCondition ( db sq . BaseRunner , condition sq . Eq ) ( * model . User , error ) {
2022-03-22 15:24:34 +01:00
users , err := s . getUsersByCondition ( db , condition , 0 )
2021-06-04 15:23:15 +02:00
if err != nil {
return nil , err
}
if len ( users ) == 0 {
2022-09-13 12:18:40 +02:00
return nil , model . NewErrNotFound ( "user" )
2021-06-04 15:23:15 +02:00
}
return users [ 0 ] , nil
}
2022-03-22 15:24:34 +01:00
func ( s * SQLStore ) getUsersByCondition ( db sq . BaseRunner , condition interface { } , limit uint64 ) ( [ ] * model . User , error ) {
2021-10-22 12:48:53 +02:00
query := s . getQueryBuilder ( db ) .
2021-06-04 15:23:15 +02:00
Select (
"id" ,
"username" ,
"email" ,
"password" ,
"mfa_secret" ,
"auth_service" ,
"auth_data" ,
"create_at" ,
"update_at" ,
"delete_at" ,
) .
2021-04-17 09:06:57 +02:00
From ( s . tablePrefix + "users" ) .
2020-12-07 20:40:16 +01:00
Where ( sq . Eq { "delete_at" : 0 } ) .
2020-10-28 14:35:41 +01:00
Where ( condition )
2022-03-22 15:24:34 +01:00
if limit != 0 {
query = query . Limit ( limit )
}
2021-06-04 15:23:15 +02:00
rows , err := query . Query ( )
2020-11-06 16:46:35 +01:00
if err != nil {
2022-03-22 15:24:34 +01:00
s . logger . Error ( ` getUsersByCondition ERROR ` , mlog . Err ( err ) )
2020-11-06 16:46:35 +01:00
return nil , err
}
2021-07-06 22:44:11 +02:00
defer s . CloseRows ( rows )
2020-11-06 16:46:35 +01:00
2021-06-04 15:23:15 +02:00
users , err := s . usersFromRows ( rows )
2020-10-28 14:35:41 +01:00
if err != nil {
return nil , err
}
2021-06-04 15:23:15 +02:00
if len ( users ) == 0 {
2022-09-13 12:18:40 +02:00
return nil , model . NewErrNotFound ( "user" )
2021-06-04 15:23:15 +02:00
}
return users , nil
2020-10-28 14:35:41 +01:00
}
2021-10-22 12:48:53 +02:00
func ( s * SQLStore ) getUserByID ( db sq . BaseRunner , userID string ) ( * model . User , error ) {
return s . getUserByCondition ( db , sq . Eq { "id" : userID } )
2020-10-28 14:35:41 +01:00
}
2022-11-02 21:07:10 +01:00
func ( s * SQLStore ) getUsersList ( db sq . BaseRunner , userIDs [ ] string , _ , _ bool ) ( [ ] * model . User , error ) {
2022-09-13 12:18:40 +02:00
users , err := s . getUsersByCondition ( db , sq . Eq { "id" : userIDs } , 0 )
if err != nil {
return nil , err
}
if len ( users ) != len ( userIDs ) {
return users , model . NewErrNotAllFound ( "user" , userIDs )
}
return users , nil
2022-07-29 22:28:00 +02:00
}
2021-10-22 12:48:53 +02:00
func ( s * SQLStore ) getUserByEmail ( db sq . BaseRunner , email string ) ( * model . User , error ) {
return s . getUserByCondition ( db , sq . Eq { "email" : email } )
2020-10-28 14:35:41 +01:00
}
2021-10-22 12:48:53 +02:00
func ( s * SQLStore ) getUserByUsername ( db sq . BaseRunner , username string ) ( * model . User , error ) {
return s . getUserByCondition ( db , sq . Eq { "username" : username } )
2020-10-28 14:35:41 +01:00
}
2022-09-15 13:58:20 +02:00
func ( s * SQLStore ) createUser ( db sq . BaseRunner , user * model . User ) ( * model . User , error ) {
2021-10-07 13:51:01 +02:00
now := utils . GetMillis ( )
2022-09-15 13:58:20 +02:00
user . CreateAt = now
user . UpdateAt = now
user . DeleteAt = 0
2020-11-06 16:46:35 +01:00
2021-10-22 12:48:53 +02:00
query := s . getQueryBuilder ( db ) . Insert ( s . tablePrefix + "users" ) .
2022-09-15 13:58:20 +02:00
Columns ( "id" , "username" , "email" , "password" , "mfa_secret" , "auth_service" , "auth_data" , "create_at" , "update_at" , "delete_at" ) .
Values ( user . ID , user . Username , user . Email , user . Password , user . MfaSecret , user . AuthService , user . AuthData , user . CreateAt , user . UpdateAt , user . DeleteAt )
2020-11-06 16:46:35 +01:00
2022-09-15 13:58:20 +02:00
_ , err := query . Exec ( )
return user , err
2020-10-28 14:35:41 +01:00
}
2022-09-15 13:58:20 +02:00
func ( s * SQLStore ) updateUser ( db sq . BaseRunner , user * model . User ) ( * model . User , error ) {
2021-10-07 13:51:01 +02:00
now := utils . GetMillis ( )
2022-09-15 13:58:20 +02:00
user . UpdateAt = now
2020-11-06 16:46:35 +01:00
2021-10-22 12:48:53 +02:00
query := s . getQueryBuilder ( db ) . Update ( s . tablePrefix + "users" ) .
2020-11-06 16:46:35 +01:00
Set ( "username" , user . Username ) .
Set ( "email" , user . Email ) .
2022-09-15 13:58:20 +02:00
Set ( "update_at" , user . UpdateAt ) .
2021-01-20 22:52:25 +01:00
Where ( sq . Eq { "id" : user . ID } )
2020-11-06 16:46:35 +01:00
2021-02-01 19:49:57 +01:00
result , err := query . Exec ( )
if err != nil {
2022-09-15 13:58:20 +02:00
return nil , err
2021-02-01 19:49:57 +01:00
}
rowCount , err := result . RowsAffected ( )
if err != nil {
2022-09-15 13:58:20 +02:00
return nil , err
2021-02-01 19:49:57 +01:00
}
if rowCount < 1 {
2022-09-15 13:58:20 +02:00
return nil , UserNotFoundError { user . ID }
2021-02-01 19:49:57 +01:00
}
2022-09-15 13:58:20 +02:00
return user , nil
2020-10-28 14:35:41 +01:00
}
2021-01-20 22:52:25 +01:00
2021-10-22 12:48:53 +02:00
func ( s * SQLStore ) updateUserPassword ( db sq . BaseRunner , username , password string ) error {
2021-10-07 13:51:01 +02:00
now := utils . GetMillis ( )
2021-01-20 22:52:25 +01:00
2021-10-22 12:48:53 +02:00
query := s . getQueryBuilder ( db ) . Update ( s . tablePrefix + "users" ) .
2021-01-20 22:52:25 +01:00
Set ( "password" , password ) .
Set ( "update_at" , now ) .
Where ( sq . Eq { "username" : username } )
2021-02-01 19:49:57 +01:00
result , err := query . Exec ( )
if err != nil {
return err
}
rowCount , err := result . RowsAffected ( )
if err != nil {
return err
}
if rowCount < 1 {
2021-07-09 03:09:02 +02:00
return UserNotFoundError { username }
2021-02-01 19:49:57 +01:00
}
return nil
2021-01-20 22:52:25 +01:00
}
2021-01-22 20:28:45 +01:00
2021-10-22 12:48:53 +02:00
func ( s * SQLStore ) updateUserPasswordByID ( db sq . BaseRunner , userID , password string ) error {
2021-10-07 13:51:01 +02:00
now := utils . GetMillis ( )
2021-01-21 19:16:40 +01:00
2021-10-22 12:48:53 +02:00
query := s . getQueryBuilder ( db ) . Update ( s . tablePrefix + "users" ) .
2021-01-21 19:16:40 +01:00
Set ( "password" , password ) .
Set ( "update_at" , now ) .
Where ( sq . Eq { "id" : userID } )
2021-02-01 19:49:57 +01:00
result , err := query . Exec ( )
if err != nil {
return err
}
rowCount , err := result . RowsAffected ( )
if err != nil {
return err
}
if rowCount < 1 {
2021-07-09 03:09:02 +02:00
return UserNotFoundError { userID }
2021-02-01 19:49:57 +01:00
}
return nil
2021-01-21 19:16:40 +01:00
}
2021-06-04 15:23:15 +02:00
2022-11-02 21:07:10 +01:00
func ( s * SQLStore ) getUsersByTeam ( db sq . BaseRunner , _ string , _ string , _ , _ bool ) ( [ ] * model . User , error ) {
2022-09-13 12:18:40 +02:00
users , err := s . getUsersByCondition ( db , nil , 0 )
if model . IsErrNotFound ( err ) {
return [ ] * model . User { } , nil
}
return users , err
2022-03-22 15:24:34 +01:00
}
2022-11-02 21:07:10 +01:00
func ( s * SQLStore ) searchUsersByTeam ( db sq . BaseRunner , _ string , searchQuery string , _ string , _ , _ , _ bool ) ( [ ] * model . User , error ) {
2022-09-13 12:18:40 +02:00
users , err := s . getUsersByCondition ( db , & sq . Like { "username" : "%" + searchQuery + "%" } , 10 )
if model . IsErrNotFound ( err ) {
return [ ] * model . User { } , nil
}
return users , err
2021-06-04 15:23:15 +02:00
}
func ( s * SQLStore ) usersFromRows ( rows * sql . Rows ) ( [ ] * model . User , error ) {
users := [ ] * model . User { }
for rows . Next ( ) {
var user model . User
err := rows . Scan (
& user . ID ,
& user . Username ,
& user . Email ,
& user . Password ,
& user . MfaSecret ,
& user . AuthService ,
& user . AuthData ,
& user . CreateAt ,
& user . UpdateAt ,
& user . DeleteAt ,
)
if err != nil {
return nil , err
}
users = append ( users , & user )
}
return users , nil
}
2022-02-28 12:28:16 +01:00
2022-09-15 13:58:20 +02:00
func ( s * SQLStore ) patchUserPreferences ( db sq . BaseRunner , userID string , patch model . UserPreferencesPatch ) ( mmModel . Preferences , error ) {
preferences , err := s . getUserPreferences ( db , userID )
if err != nil {
return nil , err
}
2022-08-29 09:25:12 +02:00
if len ( patch . UpdatedFields ) > 0 {
for key , value := range patch . UpdatedFields {
preference := mmModel . Preference {
UserId : userID ,
Category : model . PreferencesCategoryFocalboard ,
Name : key ,
Value : value ,
}
2022-09-15 13:58:20 +02:00
if err := s . updateUserPreference ( db , preference ) ; err != nil {
return nil , err
}
newPreferences := mmModel . Preferences { }
for _ , existingPreference := range preferences {
if preference . Name != existingPreference . Name {
newPreferences = append ( newPreferences , existingPreference )
}
2022-08-29 09:25:12 +02:00
}
2022-09-15 13:58:20 +02:00
newPreferences = append ( newPreferences , preference )
preferences = newPreferences
2022-08-29 09:25:12 +02:00
}
2022-02-28 12:28:16 +01:00
}
2022-08-29 09:25:12 +02:00
if len ( patch . DeletedFields ) > 0 {
for _ , key := range patch . DeletedFields {
preference := mmModel . Preference {
UserId : userID ,
Category : model . PreferencesCategoryFocalboard ,
Name : key ,
}
2022-09-15 13:58:20 +02:00
if err := s . deleteUserPreference ( db , preference ) ; err != nil {
return nil , err
2022-08-29 09:25:12 +02:00
}
2022-09-15 13:58:20 +02:00
newPreferences := mmModel . Preferences { }
for _ , existingPreference := range preferences {
if preference . Name != existingPreference . Name {
newPreferences = append ( newPreferences , existingPreference )
}
}
preferences = newPreferences
2022-08-29 09:25:12 +02:00
}
2022-02-28 12:28:16 +01:00
}
2022-09-15 13:58:20 +02:00
return preferences , nil
2022-08-29 09:25:12 +02:00
}
2022-09-15 13:58:20 +02:00
func ( s * SQLStore ) updateUserPreference ( db sq . BaseRunner , preference mmModel . Preference ) error {
2022-08-29 09:25:12 +02:00
query := s . getQueryBuilder ( db ) .
Insert ( s . tablePrefix + "preferences" ) .
Columns ( "UserId" , "Category" , "Name" , "Value" ) .
Values ( preference . UserId , preference . Category , preference . Name , preference . Value )
switch s . dbType {
case model . MysqlDBType :
query = query . SuffixExpr ( sq . Expr ( "ON DUPLICATE KEY UPDATE Value = ?" , preference . Value ) )
case model . PostgresDBType :
query = query . SuffixExpr ( sq . Expr ( "ON CONFLICT (userid, category, name) DO UPDATE SET Value = ?" , preference . Value ) )
case model . SqliteDBType :
query = query . SuffixExpr ( sq . Expr ( " on conflict(userid, category, name) do update set value = excluded.value" ) )
default :
return store . NewErrNotImplemented ( "failed to update preference because of missing driver" )
2022-02-28 12:28:16 +01:00
}
2022-08-29 09:25:12 +02:00
if _ , err := query . Exec ( ) ; err != nil {
return fmt . Errorf ( "failed to upsert user preference in database: userID: %s name: %s value: %s error: %w" , preference . UserId , preference . Name , preference . Value , err )
2022-02-28 12:28:16 +01:00
}
2022-08-29 09:25:12 +02:00
return nil
}
2022-09-15 13:58:20 +02:00
func ( s * SQLStore ) deleteUserPreference ( db sq . BaseRunner , preference mmModel . Preference ) error {
2022-08-29 09:25:12 +02:00
query := s . getQueryBuilder ( db ) .
Delete ( s . tablePrefix + "preferences" ) .
Where ( sq . Eq { "UserId" : preference . UserId } ) .
Where ( sq . Eq { "Category" : preference . Category } ) .
Where ( sq . Eq { "Name" : preference . Name } )
if _ , err := query . Exec ( ) ; err != nil {
return fmt . Errorf ( "failed to delete user preference from database: %w" , err )
}
return nil
2022-02-28 12:28:16 +01:00
}
2022-06-29 14:35:24 +02:00
2022-08-25 00:08:58 +02:00
func ( s * SQLStore ) canSeeUser ( db sq . BaseRunner , seerID string , seenID string ) ( bool , error ) {
return true , nil
}
2022-06-29 14:35:24 +02:00
func ( s * SQLStore ) sendMessage ( db sq . BaseRunner , message , postType string , receipts [ ] string ) error {
return errUnsupportedOperation
}
[MM-43781] Feature: boards insights (#3005)
* Add boilerplate functions and handlers for boards insights
* Fix function signatures to add 'duration' parameter, fix where clauses in db queries
* Fix where clause to include boards of which userId in parameter is a member
* Modify queries to work with sqlite, postgres, mysql
* Integration tests, and results of make generate
* Lint Fixes
* Add icons to board insights
* Lint fixes
* Format insights queries without squirrel to fix parameterization issues
* Add tests for sqlstore utility functions
* Improve team insights tests by creating 2 boards
* Refactor endpoints/app to adhere to developments in 7.0 release
* Refactor queries to use squirrel
* Lint fixes
* Fix client, integration tests
* Remove old integration tests
* Add storetests, refactor functions to handle authorized board_ids
* Make queries compatible with mysql, sqlite
* Add app tests
* Fix lint errors
* Revert makefile changes, fix docstring in api
* Lint fixes and doc correction suggested by @wiggin77
* Fix mock store call count error
* adding client code
* Make the following changes
- use serviceAPI to get user.Timezone
- rename licenseAndGuestUserCheck to insightPermissionGate, and handle returned error better
- validate page, perPage parameters aren't < 0
* Lint fix
Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
Co-authored-by: Benjamin Cooke <benjamincooke@Benjamins-MacBook-Pro.local>
2022-08-08 08:12:02 +02:00
2022-08-23 16:48:41 +02:00
func ( s * SQLStore ) postMessage ( db sq . BaseRunner , message , postType string , channel string ) error {
return errUnsupportedOperation
}
[MM-43781] Feature: boards insights (#3005)
* Add boilerplate functions and handlers for boards insights
* Fix function signatures to add 'duration' parameter, fix where clauses in db queries
* Fix where clause to include boards of which userId in parameter is a member
* Modify queries to work with sqlite, postgres, mysql
* Integration tests, and results of make generate
* Lint Fixes
* Add icons to board insights
* Lint fixes
* Format insights queries without squirrel to fix parameterization issues
* Add tests for sqlstore utility functions
* Improve team insights tests by creating 2 boards
* Refactor endpoints/app to adhere to developments in 7.0 release
* Refactor queries to use squirrel
* Lint fixes
* Fix client, integration tests
* Remove old integration tests
* Add storetests, refactor functions to handle authorized board_ids
* Make queries compatible with mysql, sqlite
* Add app tests
* Fix lint errors
* Revert makefile changes, fix docstring in api
* Lint fixes and doc correction suggested by @wiggin77
* Fix mock store call count error
* adding client code
* Make the following changes
- use serviceAPI to get user.Timezone
- rename licenseAndGuestUserCheck to insightPermissionGate, and handle returned error better
- validate page, perPage parameters aren't < 0
* Lint fix
Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
Co-authored-by: Benjamin Cooke <benjamincooke@Benjamins-MacBook-Pro.local>
2022-08-08 08:12:02 +02:00
func ( s * SQLStore ) getUserTimezone ( _ sq . BaseRunner , _ string ) ( string , error ) {
return "" , errUnsupportedOperation
}
2022-08-29 09:25:12 +02:00
func ( s * SQLStore ) getUserPreferences ( db sq . BaseRunner , userID string ) ( mmModel . Preferences , error ) {
query := s . getQueryBuilder ( db ) .
Select ( "userid" , "category" , "name" , "value" ) .
From ( s . tablePrefix + "preferences" ) .
Where ( sq . Eq {
"userid" : userID ,
"category" : model . PreferencesCategoryFocalboard ,
} )
rows , err := query . Query ( )
if err != nil {
s . logger . Error ( "failed to fetch user preferences" , mlog . String ( "user_id" , userID ) , mlog . Err ( err ) )
return nil , err
}
defer rows . Close ( )
preferences , err := s . preferencesFromRows ( rows )
if err != nil {
return nil , err
}
return preferences , nil
}
func ( s * SQLStore ) preferencesFromRows ( rows * sql . Rows ) ( [ ] mmModel . Preference , error ) {
preferences := [ ] mmModel . Preference { }
for rows . Next ( ) {
var preference mmModel . Preference
err := rows . Scan (
& preference . UserId ,
& preference . Category ,
& preference . Name ,
& preference . Value ,
)
if err != nil {
s . logger . Error ( "failed to scan row for user preference" , mlog . Err ( err ) )
return nil , err
}
preferences = append ( preferences , preference )
}
return preferences , nil
}