features/repo-updates #4

Merged
jtom38 merged 13 commits from features/repo-updates into main 2024-04-28 11:42:58 -07:00
2 changed files with 344 additions and 0 deletions
Showing only changes of commit 3d3b582e82 - Show all commits

View File

@ -0,0 +1,226 @@
package repository
import (
"database/sql"
"errors"
"fmt"
"time"
"git.jamestombleson.com/jtom38/newsbot-api/internal/domain"
"github.com/huandu/go-sqlbuilder"
)
type ArticleRepository struct {
conn *sql.DB
defaultLimit int
defaultOffset int
}
func NewArticleRepository(conn *sql.DB) ArticleRepository {
return ArticleRepository{
conn: conn,
defaultLimit: 50,
defaultOffset: 50,
}
}
func (ar ArticleRepository) GetById(id int64) (domain.ArticleEntity, error) {
builder := sqlbuilder.NewSelectBuilder()
builder.Select("*")
builder.From("articles").Where(
builder.E("id", id),
)
builder.Limit(1)
query, args := builder.Build()
rows, err := ar.conn.Query(query, args...)
if err != nil {
return domain.ArticleEntity{}, err
}
data := ar.processRows(rows)
if len(data) == 0 {
return domain.ArticleEntity{}, errors.New(ErrUserNotFound)
}
return data[0], nil
}
func (ar ArticleRepository) GetByUrl(url string) (domain.ArticleEntity, error) {
builder := sqlbuilder.NewSelectBuilder()
builder.Select("*")
builder.From("articles").Where(
builder.E("url", url),
)
builder.Limit(1)
query, args := builder.Build()
rows, err := ar.conn.Query(query, args...)
if err != nil {
return domain.ArticleEntity{}, err
}
data := ar.processRows(rows)
if len(data) == 0 {
return domain.ArticleEntity{}, errors.New(ErrUserNotFound)
}
return data[0], nil
}
func (ar ArticleRepository) List(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...)
if err != nil {
return []domain.ArticleEntity{}, err
}
data := ar.processRows(rows)
if len(data) == 0 {
return []domain.ArticleEntity{}, errors.New(ErrUserNotFound)
}
return data, nil
}
func (ar ArticleRepository) ListByPage(page, limit int) ([]domain.ArticleEntity, error) {
builder := sqlbuilder.NewSelectBuilder()
builder.Select("*")
builder.From("articles")
builder.OrderBy("pubdate desc")
builder.Offset(page * limit)
builder.Limit(limit)
query, args := builder.Build()
rows, err := ar.conn.Query(query, args...)
if err != nil {
return []domain.ArticleEntity{}, err
}
data := ar.processRows(rows)
if len(data) == 0 {
return []domain.ArticleEntity{}, errors.New(ErrUserNotFound)
}
return data, nil
}
func (ar ArticleRepository) ListByPublishDate(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...)
if err != nil {
return []domain.ArticleEntity{}, err
}
data := ar.processRows(rows)
if len(data) == 0 {
return []domain.ArticleEntity{}, errors.New(ErrUserNotFound)
}
return data, nil
}
func (ar ArticleRepository) ListBySource(sourceName string, limit int) ([]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)
builder.Offset(50)
query, args := builder.Build()
rows, err := ar.conn.Query(query, args...)
if err != nil {
return []domain.ArticleEntity{}, err
}
data := ar.processRows(rows)
if len(data) == 0 {
return []domain.ArticleEntity{}, errors.New(ErrUserNotFound)
}
return data, nil
}
func (ar ArticleRepository) Create(sourceId int64, tags, title, url, thumbnailUrl, description, authorName, authorImageUrl string, pubDate time.Time, isVideo bool) (int64, error) {
dt := time.Now()
queryBuilder := sqlbuilder.NewInsertBuilder()
queryBuilder.InsertInto("articles")
queryBuilder.Cols("UpdatedAt", "CreatedAt", "SourceId", "Tags", "Title", "Url", "PubDate", "IsVideo", "ThumbnailUrl", "Description", "AuthorName", "AuthorImageUrl")
queryBuilder.Values(dt, dt, sourceId, tags, title, url, pubDate, isVideo, thumbnailUrl, description, authorName, authorImageUrl)
query, args := queryBuilder.Build()
_, err := ar.conn.Exec(query, args...)
if err != nil {
return 0, err
}
return 1, nil
}
func (ur ArticleRepository) processRows(rows *sql.Rows) []domain.ArticleEntity {
items := []domain.ArticleEntity{}
for rows.Next() {
var id int64
var createdAt time.Time
var updatedAt time.Time
var deletedAt sql.NullTime
var sourceId int64
var tags string
var title string
var url string
var pubDate time.Time
var isVideo bool
var thumbnail string
var description string
var authorName string
var authorImageUrl string
err := rows.Scan(
&id, &createdAt, &updatedAt,
&deletedAt, &sourceId, &tags,
&title, &url, &pubDate,
&isVideo, &thumbnail, &description,
&authorName, &authorImageUrl)
if err != nil {
fmt.Println(err)
}
item := domain.ArticleEntity{
ID: id,
CreatedAt: createdAt,
UpdatedAt: updatedAt,
SourceID: sourceId,
Tags: tags,
Title: title,
Url: url,
PubDate: pubDate,
IsVideo: isVideo,
Thumbnail: thumbnail,
Description: description,
AuthorName: authorName,
AuthorImageUrl: authorImageUrl,
}
if deletedAt.Valid {
item.DeletedAt = deletedAt.Time
}
items = append(items, item)
}
return items
}

View File

@ -0,0 +1,118 @@
package repository_test
import (
"testing"
"time"
"git.jamestombleson.com/jtom38/newsbot-api/internal/repository"
)
const (
articleFakeDotCom = "www.fake.com"
)
func TestCreateArticle(t *testing.T) {
db, err := setupInMemoryDb()
if err != nil {
t.Log(err)
t.FailNow()
}
defer db.Close()
r := repository.NewArticleRepository(db)
created, err := r.Create(1, "", "unit test", articleFakeDotCom, "", "testing", "", "", time.Now(), false)
if err != nil {
t.Log(err)
t.FailNow()
}
if created != 1 {
t.Log("failed to create the record")
t.FailNow()
}
}
func TestArticleByUrl(t *testing.T) {
db, err := setupInMemoryDb()
if err != nil {
t.Log(err)
t.FailNow()
}
defer db.Close()
r := repository.NewArticleRepository(db)
err = insertFakeArticles(r, "u1")
if err != nil {
t.Log(err)
t.FailNow()
}
article, err := r.GetByUrl(articleFakeDotCom)
if err != nil {
t.Log(err)
t.FailNow()
}
if article.Url != "www.fake.com" {
t.Log("failed to find the requested record")
t.FailNow()
}
}
func TestPullingMultipleArticlesWithLimit(t *testing.T) {
db, err := setupInMemoryDb()
if err != nil {
t.Log(err)
t.FailNow()
}
defer db.Close()
r := repository.NewArticleRepository(db)
insertFakeArticles(r, "u1")
insertFakeArticles(r, "u2")
insertFakeArticles(r, "u3")
insertFakeArticles(r, "u4")
items, err := r.List(3)
if err != nil {
t.Log(err)
t.FailNow()
}
if len(items) != 3 {
t.Log("expected 3 rows back")
t.FailNow()
}
}
func TestPullingMultipleArticlesWithPaging(t *testing.T) {
db, err := setupInMemoryDb()
if err != nil {
t.Log(err)
t.FailNow()
}
defer db.Close()
r := repository.NewArticleRepository(db)
insertFakeArticles(r, "u1")
insertFakeArticles(r, "u2")
insertFakeArticles(r, "u3")
insertFakeArticles(r, "u4")
items, err := r.ListByPage(2, 1)
if err != nil {
t.Log(err)
t.FailNow()
}
if items[0].Title != "u2" {
t.Log("pulled the wrong record with paging")
t.FailNow()
}
}
func insertFakeArticles(r repository.ArticleRepository, title string) error {
_, err := r.Create(1, "", title, articleFakeDotCom, "", "testing", "", "", time.Now(), false)
if err != nil {
return err
}
return nil
}