From 15681d9d3779ad0a23088a83ae9be7a11e011bea Mon Sep 17 00:00:00 2001 From: James Tombleson Date: Sat, 27 Apr 2024 13:11:03 -0700 Subject: [PATCH] Almost done with DiscordWebHooks repo --- internal/domain/entity.go | 4 +- internal/repository/article.go | 25 +- internal/repository/article_test.go | 60 +++- internal/repository/discordWebHooks.go | 256 +++++++++++++++++ internal/repository/discordWebHooks_test.go | 287 ++++++++++++++++++++ internal/repository/users_test.go | 1 - 6 files changed, 609 insertions(+), 24 deletions(-) create mode 100644 internal/repository/discordWebHooks.go create mode 100644 internal/repository/discordWebHooks_test.go diff --git a/internal/domain/entity.go b/internal/domain/entity.go index 80ce64b..c8e2304 100644 --- a/internal/domain/entity.go +++ b/internal/domain/entity.go @@ -35,8 +35,8 @@ type DiscordWebHookEntity struct { CreatedAt time.Time UpdatedAt time.Time DeletedAt time.Time - Name string - Key string + //Name string + //Key string Url string Server string Channel string diff --git a/internal/repository/article.go b/internal/repository/article.go index 5b0b2b8..4bc164f 100644 --- a/internal/repository/article.go +++ b/internal/repository/article.go @@ -10,6 +10,11 @@ import ( "github.com/huandu/go-sqlbuilder" ) +const ( + ArticleOrderByPublishDateDesc = "pubDate desc" + ArticleOrderByPublishDatAsc = "pubDate asc" +) + type ArticleRepository struct { conn *sql.DB defaultLimit int @@ -68,13 +73,11 @@ func (ar ArticleRepository) GetByUrl(url string) (domain.ArticleEntity, error) { return data[0], nil } -func (ar ArticleRepository) List(limit int) ([]domain.ArticleEntity, error) { +func (ar ArticleRepository) ListTop(limit int) ([]domain.ArticleEntity, error) { builder := sqlbuilder.NewSelectBuilder() builder.Select("*") builder.From("articles") - //builder.OrderBy("pubdate") builder.Limit(limit) - //builder.Offset(50) query, args := builder.Build() rows, err := ar.conn.Query(query, args...) @@ -112,13 +115,15 @@ func (ar ArticleRepository) ListByPage(page, limit int) ([]domain.ArticleEntity, return data, nil } -func (ar ArticleRepository) ListByPublishDate(limit int) ([]domain.ArticleEntity, error) { +func (ar ArticleRepository) ListByPublishDate(page, limit int, orderBy string) ([]domain.ArticleEntity, error) { builder := sqlbuilder.NewSelectBuilder() builder.Select("*") builder.From("articles") - //builder.OrderBy("pubdate") + if orderBy != "" { + builder.OrderBy(orderBy) + } + builder.Offset(page * limit) builder.Limit(limit) - //builder.Offset(50) query, args := builder.Build() rows, err := ar.conn.Query(query, args...) @@ -133,14 +138,16 @@ func (ar ArticleRepository) ListByPublishDate(limit int) ([]domain.ArticleEntity return data, nil } -func (ar ArticleRepository) ListBySource(sourceName string, limit int) ([]domain.ArticleEntity, error) { +func (ar ArticleRepository) ListBySource(page, limit int, orderBy string) ([]domain.ArticleEntity, error) { builder := sqlbuilder.NewSelectBuilder() builder.Select("*") builder.From("articles") builder.JoinWithOption("InnerJoin", "sources", "articles.sourceId=sources.Id") - builder.OrderBy("pubdate") - builder.Limit(limit) + if orderBy != "" { + builder.OrderBy(orderBy) + } builder.Offset(50) + builder.Limit(page * limit) query, args := builder.Build() rows, err := ar.conn.Query(query, args...) diff --git a/internal/repository/article_test.go b/internal/repository/article_test.go index b4d127d..e5c37c8 100644 --- a/internal/repository/article_test.go +++ b/internal/repository/article_test.go @@ -41,7 +41,7 @@ func TestArticleByUrl(t *testing.T) { defer db.Close() r := repository.NewArticleRepository(db) - err = insertFakeArticles(r, "u1") + err = insertFakeArticles(r, "u1", 0) if err != nil { t.Log(err) t.FailNow() @@ -67,12 +67,12 @@ func TestPullingMultipleArticlesWithLimit(t *testing.T) { } defer db.Close() r := repository.NewArticleRepository(db) - insertFakeArticles(r, "u1") - insertFakeArticles(r, "u2") - insertFakeArticles(r, "u3") - insertFakeArticles(r, "u4") + insertFakeArticles(r, "u1", 0) + insertFakeArticles(r, "u2", 0) + insertFakeArticles(r, "u3", 0) + insertFakeArticles(r, "u4", 0) - items, err := r.List(3) + items, err := r.ListTop(3) if err != nil { t.Log(err) t.FailNow() @@ -92,10 +92,10 @@ func TestPullingMultipleArticlesWithPaging(t *testing.T) { } defer db.Close() r := repository.NewArticleRepository(db) - insertFakeArticles(r, "u1") - insertFakeArticles(r, "u2") - insertFakeArticles(r, "u3") - insertFakeArticles(r, "u4") + insertFakeArticles(r, "u1", 0) + insertFakeArticles(r, "u2", 0) + insertFakeArticles(r, "u3", 0) + insertFakeArticles(r, "u4", 0) items, err := r.ListByPage(2, 1) if err != nil { @@ -109,8 +109,44 @@ func TestPullingMultipleArticlesWithPaging(t *testing.T) { } } -func insertFakeArticles(r repository.ArticleRepository, title string) error { - _, err := r.Create(1, "", title, articleFakeDotCom, "", "testing", "", "", time.Now(), false) +func TestPullingByPublishDate(t *testing.T) { + db, err := setupInMemoryDb() + if err != nil { + t.Log(err) + t.FailNow() + } + defer db.Close() + r := repository.NewArticleRepository(db) + + today := time.Now() + + insertFakeArticles(r, "u1", 0) + insertFakeArticles(r, "u1", -1) + insertFakeArticles(r, "u1", -2) + + items, err := r.ListByPublishDate(0, 2, repository.ArticleOrderByPublishDateDesc) + if err != nil { + t.Log(err) + t.FailNow() + } + + if len(items) != 2 { + t.Log("expected two items back") + t.FailNow() + } + + if items[0].PubDate.Day() != (today.Day() - 2) { + t.Log("expected the record that was 2 days old") + t.FailNow() + } +} + +//func TestArticleBySource + +func insertFakeArticles(r repository.ArticleRepository, title string, daysOld int) error { + + pubDate := time.Now().AddDate(0,0, daysOld) + _, err := r.Create(1, "", title, articleFakeDotCom, "", "testing", "", "", pubDate, false) if err != nil { return err } diff --git a/internal/repository/discordWebHooks.go b/internal/repository/discordWebHooks.go new file mode 100644 index 0000000..37dca24 --- /dev/null +++ b/internal/repository/discordWebHooks.go @@ -0,0 +1,256 @@ +package repository + +import ( + "context" + "database/sql" + "time" + + "git.jamestombleson.com/jtom38/newsbot-api/internal/domain" + "github.com/huandu/go-sqlbuilder" +) + +type discordWebHookRepository struct { + conn *sql.DB +} + +func NewDiscordWebHookRepository(conn *sql.DB) discordWebHookRepository { + return discordWebHookRepository{ + conn: conn, + } +} + +func (r discordWebHookRepository) Create(ctx context.Context, url, server, channel string, enabled bool) (int64, error) { + dt := time.Now() + queryBuilder := sqlbuilder.NewInsertBuilder() + queryBuilder.InsertInto("DiscordWebHooks") + queryBuilder.Cols("UpdatedAt", "CreatedAt", "Url", "Server", "Channel", "Enabled") + queryBuilder.Values(dt, dt, url, server, channel, enabled) + query, args := queryBuilder.Build() + + _, err := r.conn.ExecContext(ctx, query, args...) + if err != nil { + return 0, err + } + + return 1, nil +} + +func (r discordWebHookRepository) Enable(ctx context.Context, id int64) (int64, error) { + b := sqlbuilder.NewUpdateBuilder() + b.Update("DiscordWebHooks") + b.Set( + b.Assign("Enabled", true), + b.Assign("UpdatedAt", time.Now()), + ) + query, args := b.Build() + + _, err := r.conn.ExecContext(ctx, query, args...) + if err != nil { + return 0, err + } + + return 1, nil +} + +func (r discordWebHookRepository) Disable(ctx context.Context, id int64) (int64, error) { + b := sqlbuilder.NewUpdateBuilder() + b.Update("DiscordWebHooks") + b.Set( + b.Assign("Enabled", false), + b.Assign("UpdatedAt", time.Now()), + ) + query, args := b.Build() + + _, err := r.conn.ExecContext(ctx, query, args...) + if err != nil { + return 0, err + } + + return 1, nil +} + +func (r discordWebHookRepository) SoftDelete(ctx context.Context, id int64) (int64, error) { + now := time.Now() + b := sqlbuilder.NewUpdateBuilder() + b.Update("DiscordWebHooks") + b.Set( + b.Assign("UpdatedAt", now), + b.Assign("DeletedAt", now), + ) + b.Where( + b.Equal("Id", id), + ) + query, args := b.Build() + + _, err := r.conn.ExecContext(ctx, query, args...) + if err != nil { + return 0, err + } + + return 1, nil +} + +func (r discordWebHookRepository) Restore(ctx context.Context, id int64) (int64, error) { + timeZero := time.Time{} + b := sqlbuilder.NewUpdateBuilder() + b.Update("DiscordWebHooks") + b.Set( + b.Assign("UpdatedAt", time.Now()), + b.Assign("DeletedAt", timeZero), + ) + b.Where( + b.Equal("Id", id), + ) + query, args := b.Build() + + _, err := r.conn.ExecContext(ctx, query, args...) + if err != nil { + return 0, err + } + + return 1, nil +} + +func (r discordWebHookRepository) Delete(ctx context.Context, id int64) (int64, error) { + b := sqlbuilder.NewDeleteBuilder() + b.DeleteFrom("DiscordWebHooks") + b.Where( + b.Equal("Id", id), + ) + query, args := b.Build() + + _, err := r.conn.ExecContext(ctx, query, args...) + if err != nil { + return 0, err + } + + return 1, nil +} + +func (r discordWebHookRepository) GetById(ctx context.Context, id int64) (domain.DiscordWebHookEntity, error) { + builder := sqlbuilder.NewSelectBuilder() + builder.Select("*") + builder.From("DiscordWebHooks").Where( + builder.E("id", id), + ) + builder.Limit(1) + + query, args := builder.Build() + rows, err := r.conn.QueryContext(ctx, query, args...) + if err != nil { + return domain.DiscordWebHookEntity{}, err + } + + data, err := r.processRows(rows) + if len(data) == 0 { + return domain.DiscordWebHookEntity{}, err + } + + return data[0], nil +} + +func (r discordWebHookRepository) GetByUrl(ctx context.Context, url string) (domain.DiscordWebHookEntity, error) { + builder := sqlbuilder.NewSelectBuilder() + builder.Select("*") + builder.From("DiscordWebHooks").Where( + builder.E("Url", url), + ) + builder.Limit(1) + + query, args := builder.Build() + rows, err := r.conn.QueryContext(ctx, query, args...) + if err != nil { + return domain.DiscordWebHookEntity{}, err + } + + data, err := r.processRows(rows) + if len(data) == 0 { + return domain.DiscordWebHookEntity{}, err + } + + return data[0], nil +} + +func (r discordWebHookRepository) ListByServerName(ctx context.Context, name string) ([]domain.DiscordWebHookEntity, error) { + builder := sqlbuilder.NewSelectBuilder() + builder.Select("*") + builder.From("DiscordWebHooks").Where( + builder.E("Server", name), + ) + + query, args := builder.Build() + rows, err := r.conn.QueryContext(ctx, query, args...) + if err != nil { + return []domain.DiscordWebHookEntity{}, err + } + + data, err := r.processRows(rows) + if len(data) == 0 { + return []domain.DiscordWebHookEntity{}, err + } + + return data, nil +} + +func (r discordWebHookRepository) ListByServerAndChannel(ctx context.Context, server, channel string) ([]domain.DiscordWebHookEntity, error) { + builder := sqlbuilder.NewSelectBuilder() + builder.Select("*") + builder.From("DiscordWebHooks").Where( + builder.Equal("Server", server), + builder.Equal("Channel", channel), + ) + + query, args := builder.Build() + rows, err := r.conn.QueryContext(ctx, query, args...) + if err != nil { + return []domain.DiscordWebHookEntity{}, err + } + + data, err := r.processRows(rows) + if len(data) == 0 { + return []domain.DiscordWebHookEntity{}, err + } + + return data, nil +} + +func (r discordWebHookRepository) processRows(rows *sql.Rows) ([]domain.DiscordWebHookEntity, error) { + items := []domain.DiscordWebHookEntity{} + + for rows.Next() { + var id int64 + var createdAt time.Time + var updatedAt time.Time + var deletedAt sql.NullTime + var url string + var server string + var channel string + var enabled bool + err := rows.Scan( + &id, &createdAt, &updatedAt, + &deletedAt, &url, &server, + &channel, &enabled, + ) + if err != nil { + return items, err + } + + item := domain.DiscordWebHookEntity{ + ID: id, + CreatedAt: createdAt, + UpdatedAt: updatedAt, + Url: url, + Server: server, + Channel: channel, + Enabled: enabled, + } + + if deletedAt.Valid { + item.DeletedAt = deletedAt.Time + } + + items = append(items, item) + } + + return items, nil +} diff --git a/internal/repository/discordWebHooks_test.go b/internal/repository/discordWebHooks_test.go new file mode 100644 index 0000000..122299d --- /dev/null +++ b/internal/repository/discordWebHooks_test.go @@ -0,0 +1,287 @@ +package repository_test + +import ( + "context" + "testing" + "time" + + "git.jamestombleson.com/jtom38/newsbot-api/internal/repository" +) + +func TestCreateDiscordWebHookRecord(t *testing.T) { + db, err := setupInMemoryDb() + if err != nil { + t.Log(err) + t.FailNow() + } + defer db.Close() + + r := repository.NewDiscordWebHookRepository(db) + created, err := r.Create(context.Background(), "www.discord.com/bad/webhook", "Unit Testing", "memes", true) + if err != nil { + t.Log(err) + t.FailNow() + } + + if created != 1 { + t.Log("failed to create the record") + t.FailNow() + } +} + +func TestDiscordWebHookGetById(t *testing.T) { + db, err := setupInMemoryDb() + if err != nil { + t.Log(err) + t.FailNow() + } + defer db.Close() + ctx := context.Background() + r := repository.NewDiscordWebHookRepository(db) + created, err := r.Create(ctx, "www.discord.com/bad/webhook", "Unit Testing", "memes", true) + if err != nil { + t.Log(err) + t.FailNow() + } + + if created != 1 { + t.Log("failed to create the record") + t.FailNow() + } + + item, err := r.GetById(ctx, 1) + if err != nil { + t.Log(err) + t.FailNow() + } + + if item.ID != 1 { + t.Log("got the wrong record back") + t.FailNow() + } +} + +func TestDiscordWebHookGetByUrl(t *testing.T) { + db, err := setupInMemoryDb() + if err != nil { + t.Log(err) + t.FailNow() + } + defer db.Close() + + ctx := context.Background() + r := repository.NewDiscordWebHookRepository(db) + _, _ = r.Create(ctx, "www.discord.com/bad/webhook", "Unit Testing", "memes", true) + item, err := r.GetByUrl(ctx, "www.discord.com/bad/webhook") + if err != nil { + t.Log(err) + t.FailNow() + } + + if item.Url != "www.discord.com/bad/webhook" { + t.Log("got the wrong record back") + t.FailNow() + } +} + +func TestDiscordWebHookListByServerName(t *testing.T) { + db, err := setupInMemoryDb() + if err != nil { + t.Log(err) + t.FailNow() + } + defer db.Close() + + ctx := context.Background() + serverName := "Unit Testing" + r := repository.NewDiscordWebHookRepository(db) + _, _ = r.Create(ctx, "www.discord.com/bad/webhook", serverName, "memes", true) + + item, err := r.ListByServerName(ctx, serverName) + if err != nil { + t.Log(err) + t.FailNow() + } + + if item[0].Server != serverName { + t.Log("got the wrong record back") + t.FailNow() + } +} + +func TestDiscordWebHookListByServerAndChannel(t *testing.T) { + db, err := setupInMemoryDb() + if err != nil { + t.Log(err) + t.FailNow() + } + defer db.Close() + + ctx := context.Background() + serverName := "Unit Testing" + channel := "memes" + r := repository.NewDiscordWebHookRepository(db) + _, _ = r.Create(ctx, "www.discord.com/bad/webhook", serverName, channel, true) + + item, err := r.ListByServerAndChannel(ctx, serverName, channel) + if err != nil { + t.Log(err) + t.FailNow() + } + + if item[0].Server != serverName { + t.Log("got the wrong wrong server back") + t.FailNow() + } + + if item[0].Channel != channel { + t.Log("got the wrong channel back") + t.FailNow() + } +} + +func TestDiscordWebHookEnableRecord(t *testing.T) { + db, err := setupInMemoryDb() + if err != nil { + t.Log(err) + t.FailNow() + } + defer db.Close() + + ctx := context.Background() + serverName := "Unit Testing" + channel := "memes" + r := repository.NewDiscordWebHookRepository(db) + _, _ = r.Create(ctx, "www.discord.com/bad/webhook", serverName, channel, false) + + item, err := r.GetById(ctx, 1) + if err != nil { + t.Log(err) + t.FailNow() + } + + if item.Enabled != false { + t.Log("the initial record was created wrong") + t.FailNow() + } + + _, err = r.Enable(ctx, 1) + if err != nil { + t.Log(err) + t.FailNow() + } + + updated, err := r.GetById(ctx, 1) + if err != nil { + t.Log(err) + t.FailNow() + } + + if item.Enabled == updated.Enabled { + t.Log("failed to update the enabled value") + t.FailNow() + } +} + +func TestDiscordWebHookDisableRecord(t *testing.T) { + db, err := setupInMemoryDb() + if err != nil { + t.Log(err) + t.FailNow() + } + defer db.Close() + + ctx := context.Background() + serverName := "Unit Testing" + channel := "memes" + r := repository.NewDiscordWebHookRepository(db) + _, _ = r.Create(ctx, "www.discord.com/bad/webhook", serverName, channel, true) + + item, err := r.GetById(ctx, 1) + if err != nil { + t.Log(err) + t.FailNow() + } + + if item.Enabled != true { + t.Log("the initial record was created wrong") + t.FailNow() + } + + _, err = r.Disable(ctx, 1) + if err != nil { + t.Log(err) + t.FailNow() + } + + updated, err := r.GetById(ctx, 1) + if err != nil { + t.Log(err) + t.FailNow() + } + + if item.Enabled == updated.Enabled { + t.Log("failed to update the enabled value") + t.FailNow() + } +} + +func TestDiscordWebHookSoftDelete(t *testing.T) { + db, err := setupInMemoryDb() + if err != nil { + t.Log(err) + t.FailNow() + } + defer db.Close() + + ctx := context.Background() + serverName := "Unit Testing" + channel := "memes" + r := repository.NewDiscordWebHookRepository(db) + _, _ = r.Create(ctx, "www.discord.com/bad/webhook", serverName, channel, true) + _, err = r.SoftDelete(ctx, 1) + if err != nil { + t.Log(err) + t.FailNow() + } + + updated, _ := r.GetById(ctx, 1) + t.Log(updated.DeletedAt) +} + +func TestDiscordWebHookRestore(t *testing.T) { + db, err := setupInMemoryDb() + if err != nil { + t.Log(err) + t.FailNow() + } + defer db.Close() + + ctx := context.Background() + serverName := "Unit Testing" + channel := "memes" + timeZero := time.Time{} + + r := repository.NewDiscordWebHookRepository(db) + _, _ = r.Create(ctx, "www.discord.com/bad/webhook", serverName, channel, true) + item, _ := r.GetById(ctx, 1) + if item.DeletedAt != timeZero { + t.Log("DeletedAt was not zero") + t.FailNow() + } + _, _ = r.SoftDelete(ctx, 1) + softDeleted, _ := r.GetById(ctx, 1) + if softDeleted.ID != 1 { + t.Log("record went boom") + t.FailNow() + } + + _, err = r.Restore(ctx, 1) + if err != nil { + t.Log(err) + t.FailNow() + } + + updated, _ := r.GetById(ctx, 1) + t.Log(updated.DeletedAt) +} diff --git a/internal/repository/users_test.go b/internal/repository/users_test.go index c5d18db..849338e 100644 --- a/internal/repository/users_test.go +++ b/internal/repository/users_test.go @@ -12,7 +12,6 @@ import ( ) func TestCanCreateNewUser(t *testing.T) { - //t.Log(time.Now().String()) db, err := setupInMemoryDb() if err != nil { t.Log(err)