From eb9a3757a6636e1af7a4dd573a26e9111a39dc6d Mon Sep 17 00:00:00 2001 From: Tigor Hutasuhut Date: Mon, 15 Jul 2024 17:51:40 +0700 Subject: [PATCH] blacklist: added migration and api to add images to blacklist --- api/images_blacklist_add.go | 83 +++++++++++++++++++ .../20240715121153_create_blacklist_table.sql | 24 ++++++ 2 files changed, 107 insertions(+) create mode 100644 api/images_blacklist_add.go create mode 100644 db/migrations/20240715121153_create_blacklist_table.sql diff --git a/api/images_blacklist_add.go b/api/images_blacklist_add.go new file mode 100644 index 0000000..3d40440 --- /dev/null +++ b/api/images_blacklist_add.go @@ -0,0 +1,83 @@ +package api + +import ( + "context" + "errors" + "net/http" + "strings" + "time" + + "github.com/aarondl/opt/omit" + "github.com/mattn/go-sqlite3" + "github.com/tigorlazuardi/redmage/models" + "github.com/tigorlazuardi/redmage/pkg/errs" +) + +type ImagesBlacklistAddParams struct { + Subreddit string + PostName string + Device string +} + +func (iabp *ImagesBlacklistAddParams) FillFromQuery(query Queryable) { + iabp.Subreddit = query.Get("subreddit") + iabp.PostName = query.Get("post_name") + iabp.Device = query.Get("device") +} + +func (iabp *ImagesBlacklistAddParams) FillFromCSV(values string) { + v := strings.Split(values, ",") + for len(v) < 3 { + v = append(v, "") + } + iabp.Device = strings.TrimSpace(v[0]) + iabp.Subreddit = strings.TrimSpace(v[1]) + iabp.PostName = strings.TrimSpace(v[2]) +} + +func (iabp *ImagesBlacklistAddParams) Validate() error { + var e []error + if iabp.Subreddit == "" { + e = append(e, errors.New("subreddit is required")) + } + if iabp.PostName == "" { + e = append(e, errors.New("post_name is required")) + } + if err := errors.Join(e...); err != nil { + return errs.Wrap(err).Code(http.StatusBadRequest).Details("params", iabp) + } + return nil +} + +func (api *API) ImagesBlacklistAdd(ctx context.Context, params []ImagesBlacklistAddParams) (blacklists models.BlacklistSlice, err error) { + ctx, span := tracer.Start(ctx, "*API.ImageAddToBlacklist") + defer span.End() + + now := time.Now() + + sets := make([]*models.BlacklistSetter, 0, len(params)) + for _, param := range params { + sets = append(sets, &models.BlacklistSetter{ + Device: omit.From(param.Device), + Subreddit: omit.From(param.Subreddit), + PostName: omit.From(param.PostName), + CreatedAt: omit.From(now.Unix()), + }) + } + + api.lockf(func() { + blacklists, err = models.Blacklists.InsertMany(ctx, api.db, sets...) + }) + + if err != nil { + var sqliteErr sqlite3.Error + if errors.As(err, &sqliteErr) { + if sqliteErr.Code == sqlite3.ErrConstraint { + return blacklists, errs.Wrapw(err, "blacklist already exists", "params", params).Code(http.StatusConflict) + } + } + return blacklists, errs.Wrapw(err, "failed to insert blacklist", "params", params) + } + + return blacklists, err +} diff --git a/db/migrations/20240715121153_create_blacklist_table.sql b/db/migrations/20240715121153_create_blacklist_table.sql new file mode 100644 index 0000000..44e0490 --- /dev/null +++ b/db/migrations/20240715121153_create_blacklist_table.sql @@ -0,0 +1,24 @@ +-- +goose Up +-- +goose StatementBegin +CREATE TABLE blacklists( + id INTEGER PRIMARY KEY AUTOINCREMENT, + device VARCHAR(255) NOT NULL COLLATE NOCASE DEFAULT '', + subreddit VARCHAR(255) NOT NULL COLLATE NOCASE, + post_name VARCHAR(255) NOT NULL, + created_at BIGINT DEFAULT 0 NOT NULL, + CONSTRAINT fk_blacklist_subreddit_post_name + FOREIGN KEY (subreddit, post_name) + REFERENCES images(subreddit, post_name) + ON DELETE CASCADE +); + +CREATE INDEX idx_blacklist_device ON blacklists(device); +CREATE INDEX idx_blacklist_subreddit_post_name ON blacklists(subreddit, post_name); +CREATE INDEX idx_blacklist_created_at ON blacklists(created_at DESC); +CREATE UNIQUE INDEX idx_unique_blacklist ON blacklists(device, subreddit, post_name); +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +DROP TABLE IF EXISTS blacklists; +-- +goose StatementEnd