Compare commits
No commits in common. "3d3b582e82f3743b3b9476dec1df607eb0b39a7c" and "2b5a3cc8e4fd9c65bbf60155c71d3a26432eceac" have entirely different histories.
3d3b582e82
...
2b5a3cc8e4
@ -4,20 +4,20 @@ SELECT 'up SQL query';
|
|||||||
CREATE TABLE Articles (
|
CREATE TABLE Articles (
|
||||||
ID INTEGER PRIMARY KEY AUTOINCREMENT,
|
ID INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
CreatedAt DATETIME NOT NULL,
|
CreatedAt DATETIME NOT NULL,
|
||||||
UpdatedAt DATETIME NOT NULL,
|
LastUpdated DATETIME NOT NULL,
|
||||||
DeletedAt DATETIME,
|
DeletedAt DATETIME,
|
||||||
SourceId NUMBER NOT NULL,
|
SourceId NUMBER NOT NULL,
|
||||||
Tags TEXT NOT NULL,
|
Tags TEXT NOT NULL,
|
||||||
Title TEXT NOT NULL,
|
Title TEXT NOT NULL,
|
||||||
Url TEXT NOT NULL,
|
Url TEXT NOT NULL,
|
||||||
PubDate DATETIME NOT NULL,
|
PubDate DATETIME NOT NULL,
|
||||||
IsVideo TEXT NOT NULL,
|
Video TEXT,
|
||||||
--VideoHeight int NOT NULL,
|
VideoHeight int NOT NULL,
|
||||||
--VideoWidth int NOT NULL,
|
VideoWidth int NOT NULL,
|
||||||
ThumbnailUrl TEXT NOT NULL,
|
Thumbnail TEXT NOT NULL,
|
||||||
Description TEXT NOT NULL,
|
Description TEXT NOT NULL,
|
||||||
AuthorName TEXT NOT NULL,
|
AuthorName TEXT,
|
||||||
AuthorImageUrl TEXT NOT NULL
|
AuthorImageUrl TEXT
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE Table DiscordQueue (
|
CREATE Table DiscordQueue (
|
||||||
|
@ -7,14 +7,13 @@ import (
|
|||||||
type ArticleEntity struct {
|
type ArticleEntity struct {
|
||||||
ID int64
|
ID int64
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
UpdatedAt time.Time
|
LastUpdated time.Time
|
||||||
DeletedAt time.Time
|
DeletedAt time.Time
|
||||||
SourceID int64
|
SourceID int64
|
||||||
Tags string
|
Tags string
|
||||||
Title string
|
Title string
|
||||||
Url string
|
Url string
|
||||||
PubDate time.Time
|
PubDate time.Time
|
||||||
IsVideo bool
|
|
||||||
Thumbnail string
|
Thumbnail string
|
||||||
Description string
|
Description string
|
||||||
AuthorName string
|
AuthorName string
|
||||||
@ -22,12 +21,12 @@ type ArticleEntity struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type DiscordQueueEntity struct {
|
type DiscordQueueEntity struct {
|
||||||
ID int64
|
ID int64
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
UpdatedAt time.Time
|
LastUpdated time.Time
|
||||||
DeletedAt time.Time
|
DeletedAt time.Time
|
||||||
ArticleId int64
|
ArticleId int64
|
||||||
SourceId int64
|
SourceId int64
|
||||||
}
|
}
|
||||||
|
|
||||||
type DiscordWebHookEntity struct {
|
type DiscordWebHookEntity struct {
|
||||||
|
@ -1,226 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
@ -1,118 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user