github.com/masterhung0112/hk_server/v5@v5.0.0-20220302090640-ec71aef15e1c/store/sqlstore/bot_store.go (about) 1 package sqlstore 2 3 import ( 4 "database/sql" 5 "fmt" 6 "strings" 7 8 "github.com/pkg/errors" 9 10 "github.com/masterhung0112/hk_server/v5/einterfaces" 11 "github.com/masterhung0112/hk_server/v5/model" 12 "github.com/masterhung0112/hk_server/v5/store" 13 ) 14 15 // bot is a subset of the model.Bot type, omitting the model.User fields. 16 type bot struct { 17 UserId string `json:"user_id"` 18 Description string `json:"description"` 19 OwnerId string `json:"owner_id"` 20 LastIconUpdate int64 `json:"last_icon_update"` 21 CreateAt int64 `json:"create_at"` 22 UpdateAt int64 `json:"update_at"` 23 DeleteAt int64 `json:"delete_at"` 24 } 25 26 func botFromModel(b *model.Bot) *bot { 27 return &bot{ 28 UserId: b.UserId, 29 Description: b.Description, 30 OwnerId: b.OwnerId, 31 LastIconUpdate: b.LastIconUpdate, 32 CreateAt: b.CreateAt, 33 UpdateAt: b.UpdateAt, 34 DeleteAt: b.DeleteAt, 35 } 36 } 37 38 // SqlBotStore is a store for managing bots in the database. 39 // Bots are otherwise normal users with extra metadata record in the Bots table. The primary key 40 // for a bot matches the primary key value for corresponding User record. 41 type SqlBotStore struct { 42 *SqlStore 43 metrics einterfaces.MetricsInterface 44 } 45 46 // newSqlBotStore creates an instance of SqlBotStore, registering the table schema in question. 47 func newSqlBotStore(sqlStore *SqlStore, metrics einterfaces.MetricsInterface) store.BotStore { 48 us := &SqlBotStore{ 49 SqlStore: sqlStore, 50 metrics: metrics, 51 } 52 53 for _, db := range sqlStore.GetAllConns() { 54 table := db.AddTableWithName(bot{}, "Bots").SetKeys(false, "UserId") 55 table.ColMap("UserId").SetMaxSize(26) 56 table.ColMap("Description").SetMaxSize(1024) 57 table.ColMap("OwnerId").SetMaxSize(model.BOT_CREATOR_ID_MAX_RUNES) 58 } 59 60 return us 61 } 62 63 func (us SqlBotStore) createIndexesIfNotExists() { 64 } 65 66 // Get fetches the given bot in the database. 67 func (us SqlBotStore) Get(botUserId string, includeDeleted bool) (*model.Bot, error) { 68 var excludeDeletedSql = "AND b.DeleteAt = 0" 69 if includeDeleted { 70 excludeDeletedSql = "" 71 } 72 73 query := ` 74 SELECT 75 b.UserId, 76 u.Username, 77 u.FirstName AS DisplayName, 78 b.Description, 79 b.OwnerId, 80 COALESCE(b.LastIconUpdate, 0) AS LastIconUpdate, 81 b.CreateAt, 82 b.UpdateAt, 83 b.DeleteAt 84 FROM 85 Bots b 86 JOIN 87 Users u ON (u.Id = b.UserId) 88 WHERE 89 b.UserId = :user_id 90 ` + excludeDeletedSql + ` 91 ` 92 93 var bot *model.Bot 94 if err := us.GetReplica().SelectOne(&bot, query, map[string]interface{}{"user_id": botUserId}); err == sql.ErrNoRows { 95 return nil, store.NewErrNotFound("Bot", botUserId) 96 } else if err != nil { 97 return nil, errors.Wrapf(err, "selectone: user_id=%s", botUserId) 98 } 99 100 return bot, nil 101 } 102 103 // GetAll fetches from all bots in the database. 104 func (us SqlBotStore) GetAll(options *model.BotGetOptions) ([]*model.Bot, error) { 105 params := map[string]interface{}{ 106 "offset": options.Page * options.PerPage, 107 "limit": options.PerPage, 108 } 109 110 var conditions []string 111 var conditionsSql string 112 var additionalJoin string 113 114 if !options.IncludeDeleted { 115 conditions = append(conditions, "b.DeleteAt = 0") 116 } 117 if options.OwnerId != "" { 118 conditions = append(conditions, "b.OwnerId = :creator_id") 119 params["creator_id"] = options.OwnerId 120 } 121 if options.OnlyOrphaned { 122 additionalJoin = "JOIN Users o ON (o.Id = b.OwnerId)" 123 conditions = append(conditions, "o.DeleteAt != 0") 124 } 125 126 if len(conditions) > 0 { 127 conditionsSql = "WHERE " + strings.Join(conditions, " AND ") 128 } 129 130 sql := ` 131 SELECT 132 b.UserId, 133 u.Username, 134 u.FirstName AS DisplayName, 135 b.Description, 136 b.OwnerId, 137 COALESCE(b.LastIconUpdate, 0) AS LastIconUpdate, 138 b.CreateAt, 139 b.UpdateAt, 140 b.DeleteAt 141 FROM 142 Bots b 143 JOIN 144 Users u ON (u.Id = b.UserId) 145 ` + additionalJoin + ` 146 ` + conditionsSql + ` 147 ORDER BY 148 b.CreateAt ASC, 149 u.Username ASC 150 LIMIT 151 :limit 152 OFFSET 153 :offset 154 ` 155 156 var bots []*model.Bot 157 if _, err := us.GetReplica().Select(&bots, sql, params); err != nil { 158 return nil, errors.Wrap(err, "select") 159 } 160 161 return bots, nil 162 } 163 164 // Save persists a new bot to the database. 165 // It assumes the corresponding user was saved via the user store. 166 func (us SqlBotStore) Save(bot *model.Bot) (*model.Bot, error) { 167 bot = bot.Clone() 168 bot.PreSave() 169 170 if err := bot.IsValid(); err != nil { // TODO: change to return error in v6. 171 return nil, err 172 } 173 174 if err := us.GetMaster().Insert(botFromModel(bot)); err != nil { 175 return nil, errors.Wrapf(err, "insert: user_id=%s", bot.UserId) 176 } 177 178 return bot, nil 179 } 180 181 // Update persists an updated bot to the database. 182 // It assumes the corresponding user was updated via the user store. 183 func (us SqlBotStore) Update(bot *model.Bot) (*model.Bot, error) { 184 bot = bot.Clone() 185 186 bot.PreUpdate() 187 if err := bot.IsValid(); err != nil { // TODO: needs to return error in v6 188 return nil, err 189 } 190 191 oldBot, err := us.Get(bot.UserId, true) 192 if err != nil { 193 return nil, err 194 } 195 196 oldBot.Description = bot.Description 197 oldBot.OwnerId = bot.OwnerId 198 oldBot.LastIconUpdate = bot.LastIconUpdate 199 oldBot.UpdateAt = bot.UpdateAt 200 oldBot.DeleteAt = bot.DeleteAt 201 bot = oldBot 202 203 if count, err := us.GetMaster().Update(botFromModel(bot)); err != nil { 204 return nil, errors.Wrapf(err, "update: user_id=%s", bot.UserId) 205 } else if count > 1 { 206 return nil, fmt.Errorf("unexpected count while updating bot: count=%d, userId=%s", count, bot.UserId) 207 } 208 209 return bot, nil 210 } 211 212 // PermanentDelete removes the bot from the database altogether. 213 // If the corresponding user is to be deleted, it must be done via the user store. 214 func (us SqlBotStore) PermanentDelete(botUserId string) error { 215 query := "DELETE FROM Bots WHERE UserId = :user_id" 216 if _, err := us.GetMaster().Exec(query, map[string]interface{}{"user_id": botUserId}); err != nil { 217 return store.NewErrInvalidInput("Bot", "UserId", botUserId) 218 } 219 return nil 220 }