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  }