Merge pull request 'features/repo-updates' (#4) from features/repo-updates into main
Reviewed-on: #4
This commit is contained in:
commit
8fc2e56ad5
4
.gitignore
vendored
4
.gitignore
vendored
@ -4,6 +4,9 @@ __debug_bin
|
||||
server
|
||||
.vscode
|
||||
|
||||
# hide the swagger files in the repo
|
||||
docs/
|
||||
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
@ -15,6 +18,7 @@ collector
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
FROM golang:1.18.4 as build
|
||||
FROM golang:1.22 as build
|
||||
|
||||
COPY . /app
|
||||
WORKDIR /app
|
||||
|
@ -4,20 +4,20 @@ SELECT 'up SQL query';
|
||||
CREATE TABLE Articles (
|
||||
ID INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
CreatedAt DATETIME NOT NULL,
|
||||
LastUpdated DATETIME NOT NULL,
|
||||
DeletedAt DATETIME,
|
||||
UpdatedAt DATETIME NOT NULL,
|
||||
DeletedAt DATETIME NOT NULL,
|
||||
SourceId NUMBER NOT NULL,
|
||||
Tags TEXT NOT NULL,
|
||||
Title TEXT NOT NULL,
|
||||
Url TEXT NOT NULL,
|
||||
PubDate DATETIME NOT NULL,
|
||||
Video TEXT,
|
||||
VideoHeight int NOT NULL,
|
||||
VideoWidth int NOT NULL,
|
||||
Thumbnail TEXT NOT NULL,
|
||||
IsVideo TEXT NOT NULL,
|
||||
--VideoHeight int NOT NULL,
|
||||
--VideoWidth int NOT NULL,
|
||||
ThumbnailUrl TEXT NOT NULL,
|
||||
Description TEXT NOT NULL,
|
||||
AuthorName TEXT,
|
||||
AuthorImageUrl TEXT
|
||||
AuthorName TEXT NOT NULL,
|
||||
AuthorImageUrl TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE Table DiscordQueue (
|
||||
@ -33,12 +33,12 @@ CREATE Table DiscordWebHooks (
|
||||
ID INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
CreatedAt DATETIME NOT NULL,
|
||||
UpdatedAt DATETIME NOT NULL,
|
||||
DeletedAt DATETIME,
|
||||
DeletedAt DATETIME NOT NULL,
|
||||
--Name TEXT NOT NULL, -- Defines webhook purpose
|
||||
--Key TEXT,
|
||||
Url TEXT NOT NULL, -- Webhook Url
|
||||
Server TEXT NOT NULL, -- Defines the server its bound it. Used for refrence
|
||||
Channel TEXT NOT NULL, -- Defines the channel its bound to. Used for refrence
|
||||
Server TEXT NOT NULL, -- Defines the server its bound it. Used for reference
|
||||
Channel TEXT NOT NULL, -- Defines the channel its bound to. Used for reference
|
||||
Enabled BOOLEAN NOT NULL
|
||||
);
|
||||
|
||||
@ -65,12 +65,9 @@ CREATE Table Sources (
|
||||
ID INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
CreatedAt DATETIME NOT NULL,
|
||||
UpdatedAt DATETIME NOT NULL,
|
||||
DeletedAt DATETIME,
|
||||
Site TEXT NOT NULL, -- Vanity name
|
||||
Name TEXT NOT NULL, -- Defines the name of the source. IE: dadjokes
|
||||
Source TEXT NOT NULL, -- Defines the service that will use this reocrd. IE reddit or youtube
|
||||
Type TEXT NOT NULL, -- Defines what kind of feed this is. feed, user, tag
|
||||
Value TEXT,
|
||||
DeletedAt DATETIME NOT NULL,
|
||||
DisplayName TEXT NOT NULL, -- Vanity name
|
||||
Source TEXT NOT NULL, -- Defines the service that will use this record. IE reddit or youtube
|
||||
Enabled BOOLEAN NOT NULL,
|
||||
Url TEXT NOT NULL,
|
||||
Tags TEXT NOT NULL
|
||||
|
@ -6,34 +6,34 @@ SELECT 'up SQL query';
|
||||
--CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||
|
||||
-- Final Fantasy XIV Entries
|
||||
INSERT INTO sources (CreatedAt, UpdatedAt, Site, Name, Source, Type, Value, Enabled, Url, Tags) VALUES
|
||||
("2024-04-25 18:37:43.852367", "2024-04-25 18:37:43.852367", 'ffxiv', 'Final Fantasy XIV - NA', 'ffxiv', 'scrape', 'a', TRUE, 'https://na.finalfantasyxiv.com/lodestone/', 'ffxiv, final, fantasy, xiv, na, lodestone');
|
||||
INSERT INTO sources (CreatedAt, UpdatedAt, Site, Name, Source, Type, Value, Enabled, Url, Tags) VALUES
|
||||
("2024-04-25 18:37:43.852367", "2024-04-25 18:37:43.852367", 'ffxiv', 'Final Fantasy XIV - JP', 'ffxiv', 'scrape', 'a', FALSE, 'https://jp.finalfantasyxiv.com/lodestone/', 'ffxiv, final, fantasy, xiv, jp, lodestone');
|
||||
INSERT INTO sources (CreatedAt, UpdatedAt, Site, Name, Source, Type, Value, Enabled, Url, Tags) VALUES
|
||||
("2024-04-25 18:37:43.852367", "2024-04-25 18:37:43.852367", 'ffxiv', 'Final Fantasy XIV - EU', 'ffxiv', 'scrape', 'a', FALSE, 'https://eu.finalfantasyxiv.com/lodestone/', 'ffxiv, final, fantasy, xiv, eu, lodestone');
|
||||
INSERT INTO sources (CreatedAt, UpdatedAt, Site, Name, Source, Type, Value, Enabled, Url, Tags) VALUES
|
||||
("2024-04-25 18:37:43.852367", "2024-04-25 18:37:43.852367", 'ffxiv', 'Final Fantasy XIV - FR', 'ffxiv', 'scrape', 'a', FALSE, 'https://fr.finalfantasyxiv.com/lodestone/', 'ffxiv, final, fantasy, xiv, fr, lodestone');
|
||||
INSERT INTO sources (CreatedAt, UpdatedAt, Site, Name, Source, Type, Value, Enabled, Url, Tags) VALUES
|
||||
("2024-04-25 18:37:43.852367", "2024-04-25 18:37:43.852367", 'ffxiv', 'Final Fantasy XIV - DE', 'ffxiv', 'scrape', 'a', FALSE, 'https://de.finalfantasyxiv.com/lodestone/', 'ffxiv, final, fantasy, xiv, de, lodestone');
|
||||
INSERT INTO sources (CreatedAt, UpdatedAt, DeletedAt, DisplayName, Source, Enabled, Url, Tags) VALUES
|
||||
("2024-04-25 18:37:43.852367", "2024-04-25 18:37:43.852367", "0001-01-01 00:00:00", 'ffxiv', 'Final Fantasy XIV - NA', TRUE, 'https://na.finalfantasyxiv.com/lodestone/', 'ffxiv, final, fantasy, xiv, na, lodestone');
|
||||
INSERT INTO sources (CreatedAt, UpdatedAt, DeletedAt, DisplayName, Source, Enabled, Url, Tags) VALUES
|
||||
("2024-04-25 18:37:43.852367", "2024-04-25 18:37:43.852367", "0001-01-01 00:00:00", 'ffxiv', 'Final Fantasy XIV - JP', FALSE, 'https://jp.finalfantasyxiv.com/lodestone/', 'ffxiv, final, fantasy, xiv, jp, lodestone');
|
||||
INSERT INTO sources (CreatedAt, UpdatedAt, DeletedAt, DisplayName, Source, Enabled, Url, Tags) VALUES
|
||||
("2024-04-25 18:37:43.852367", "2024-04-25 18:37:43.852367", "0001-01-01 00:00:00", 'ffxiv', 'Final Fantasy XIV - EU', FALSE, 'https://eu.finalfantasyxiv.com/lodestone/', 'ffxiv, final, fantasy, xiv, eu, lodestone');
|
||||
INSERT INTO sources (CreatedAt, UpdatedAt, DeletedAt, DisplayName, Source, Enabled, Url, Tags) VALUES
|
||||
("2024-04-25 18:37:43.852367", "2024-04-25 18:37:43.852367", "0001-01-01 00:00:00", 'ffxiv', 'Final Fantasy XIV - FR', FALSE, 'https://fr.finalfantasyxiv.com/lodestone/', 'ffxiv, final, fantasy, xiv, fr, lodestone');
|
||||
INSERT INTO sources (CreatedAt, UpdatedAt, DeletedAt, DisplayName, Source, Enabled, Url, Tags) VALUES
|
||||
("2024-04-25 18:37:43.852367", "2024-04-25 18:37:43.852367", "0001-01-01 00:00:00", 'ffxiv', 'Final Fantasy XIV - DE', FALSE, 'https://de.finalfantasyxiv.com/lodestone/', 'ffxiv, final, fantasy, xiv, de, lodestone');
|
||||
|
||||
-- Reddit Entries
|
||||
INSERT INTO sources (CreatedAt, UpdatedAt, Site, Name, Source, Type, Value, Enabled, Url, Tags) VALUES
|
||||
("2024-04-25 18:37:43.852367", "2024-04-25 18:37:43.852367", 'reddit', 'dadjokes', 'reddit', 'feed', 'a', TRUE, 'https://reddit.com/r/dadjokes', 'reddit, dadjokes');
|
||||
INSERT INTO sources (CreatedAt, UpdatedAt, Site, Name, Source, Type, Value, Enabled, Url, Tags) VALUES
|
||||
("2024-04-25 18:37:43.852367", "2024-04-25 18:37:43.852367", 'reddit', 'steamdeck', 'reddit', 'feed', 'a', TRUE, 'https://reddit.com/r/steamdeck', 'reddit, steam deck, steam, deck');
|
||||
INSERT INTO sources (CreatedAt, UpdatedAt, DeletedAt, DisplayName, Source, Enabled, Url, Tags) VALUES
|
||||
("2024-04-25 18:37:43.852367", "2024-04-25 18:37:43.852367", "0001-01-01 00:00:00", 'reddit', 'dadjokes', TRUE, 'https://reddit.com/r/dadjokes', 'reddit, dadjokes');
|
||||
INSERT INTO sources (CreatedAt, UpdatedAt, DeletedAt, DisplayName, Source, Enabled, Url, Tags) VALUES
|
||||
("2024-04-25 18:37:43.852367", "2024-04-25 18:37:43.852367", "0001-01-01 00:00:00", 'reddit', 'steamdeck', TRUE, 'https://reddit.com/r/steamdeck', 'reddit, steam deck, steam, deck');
|
||||
|
||||
-- Youtube Entries
|
||||
INSERT INTO sources (CreatedAt, UpdatedAt, Site, Name, Source, Type, Value, Enabled, Url, Tags) VALUES
|
||||
("2024-04-25 18:37:43.852367", "2024-04-25 18:37:43.852367", 'youtube', 'Game Grumps', 'youtube', 'feed', 'a', TRUE, 'https://www.youtube.com/user/GameGrumps', 'youtube, game grumps, game, grumps');
|
||||
INSERT INTO sources (CreatedAt, UpdatedAt, DeletedAt, DisplayName, Source, Enabled, Url, Tags) VALUES
|
||||
("2024-04-25 18:37:43.852367", "2024-04-25 18:37:43.852367", "0001-01-01 00:00:00", 'youtube', 'Game Grumps', TRUE, 'https://www.youtube.com/user/GameGrumps', 'youtube, game grumps, game, grumps');
|
||||
|
||||
-- RSS Entries
|
||||
INSERT INTO sources (CreatedAt, UpdatedAt, Site, Name, Source, Type, Value, Enabled, Url, Tags) VALUES
|
||||
("2024-04-25 18:37:43.852367", "2024-04-25 18:37:43.852367", 'steampowered', 'steam deck', 'rss', 'feed', 'a', TRUE, 'https://store.steampowered.com/feeds/news/app/1675200/?cc=US&l=english&snr=1_2108_9__2107', 'rss, steampowered, steam, deck, steam deck');
|
||||
INSERT INTO sources (CreatedAt, UpdatedAt, DeletedAt, DisplayName, Source, Enabled, Url, Tags) VALUES
|
||||
("2024-04-25 18:37:43.852367", "2024-04-25 18:37:43.852367", "0001-01-01 00:00:00", 'steampowered', 'steam deck', TRUE, 'https://store.steampowered.com/feeds/news/app/1675200/?cc=US&l=english&snr=1_2108_9__2107', 'rss, steampowered, steam, deck, steam deck');
|
||||
|
||||
-- Twitch Entries
|
||||
INSERT INTO sources (CreatedAt, UpdatedAt, Site, Name, Source, Type, Value, Enabled, Url, Tags) VALUES
|
||||
("2024-04-25 18:37:43.852367", "2024-04-25 18:37:43.852367", 'twitch', 'Nintendo', 'twitch', 'api', 'a', TRUE, 'https://twitch.tv/nintendo', 'twitch, nintendo');
|
||||
INSERT INTO sources (CreatedAt, UpdatedAt, DeletedAt, DisplayName, Source, Enabled, Url, Tags) VALUES
|
||||
("2024-04-25 18:37:43.852367", "2024-04-25 18:37:43.852367", "0001-01-01 00:00:00", 'twitch', 'Nintendo', TRUE, 'https://twitch.tv/nintendo', 'twitch, nintendo');
|
||||
|
||||
-- +goose StatementEnd
|
||||
|
||||
|
9
internal/domain/const.go
Normal file
9
internal/domain/const.go
Normal file
@ -0,0 +1,9 @@
|
||||
package domain
|
||||
|
||||
const (
|
||||
SourceCollectorRss = "rss"
|
||||
SourceCollectorFfxiv = "ffxiv"
|
||||
SourceCollectorTwitch = "twitch"
|
||||
SourceCollectorYoutube = "youtube"
|
||||
SourceCollectorReddit = "reddit"
|
||||
)
|
@ -3,33 +3,26 @@ package domain
|
||||
import "time"
|
||||
|
||||
type ArticleDto struct {
|
||||
ID int64 `json:"id"`
|
||||
SourceID int64 `json:"sourceId"`
|
||||
Tags string `json:"tags"`
|
||||
Title string `json:"title"`
|
||||
Url string `json:"url"`
|
||||
PubDate time.Time `json:"pubDate"`
|
||||
Video string `json:"video"`
|
||||
VideoHeight uint16 `json:"videoHeight"`
|
||||
VideoWidth uint16 `json:"videoWidth"`
|
||||
Thumbnail string `json:"thumbnail"`
|
||||
Description string `json:"description"`
|
||||
AuthorName string `json:"authorName"`
|
||||
AuthorImage string `json:"authorImage"`
|
||||
ID int64 `json:"id"`
|
||||
SourceID int64 `json:"sourceId"`
|
||||
Tags string `json:"tags"`
|
||||
Title string `json:"title"`
|
||||
Url string `json:"url"`
|
||||
PubDate time.Time `json:"pubDate"`
|
||||
IsVideo bool `json:"isVideo"`
|
||||
Thumbnail string `json:"thumbnail"`
|
||||
Description string `json:"description"`
|
||||
AuthorName string `json:"authorName"`
|
||||
AuthorImageUrl string `json:"authorImage"`
|
||||
}
|
||||
|
||||
type DiscordQueueDto struct {
|
||||
//CreatedAt time.Time `json:"createdAt"`
|
||||
//UpdatedAt time.Time `json:"updatedAt"`
|
||||
ID int64 `json:"id"`
|
||||
ArticleId int64 `json:"articleId"`
|
||||
SourceId int64 `json:"sourceId"`
|
||||
}
|
||||
|
||||
type DiscordWebHookDto struct {
|
||||
//CreatedAt time.Time `json:"CreatedAt"`
|
||||
//UpdatedAt time.Time `json:"UpdatedAt"`
|
||||
//DeletedAt time.Time `json:"DeletedAt"`
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Key string `json:"key"`
|
||||
@ -40,18 +33,12 @@ type DiscordWebHookDto struct {
|
||||
}
|
||||
|
||||
type IconDto struct {
|
||||
//CreatedAt time.Time `json:"CreatedAt"`
|
||||
//UpdatedAt time.Time `json:"UpdatedAt"`
|
||||
//DeletedAt time.Time `json:"DeletedAt"`
|
||||
ID int64 `json:"id"`
|
||||
FileName string `json:"fileName"`
|
||||
Site string `json:"site"`
|
||||
}
|
||||
|
||||
type SettingDto struct {
|
||||
//CreatedAt time.Time `json:"CreatedAt"`
|
||||
//UpdatedAt time.Time `json:"UpdatedAt"`
|
||||
//DeletedAt time.Time `json:"DeletedAt"`
|
||||
ID int64 `json:"id"`
|
||||
Key string `json:"key"`
|
||||
Value string `json:"value"`
|
||||
@ -59,9 +46,6 @@ type SettingDto struct {
|
||||
}
|
||||
|
||||
type SubscriptionDto struct {
|
||||
//CreatedAt time.Time `json:"CreatedAt"`
|
||||
//UpdatedAt time.Time `json:"UpdatedAt"`
|
||||
//DeletedAt time.Time `json:"DeletedAt"`
|
||||
ID int64 `json:"id"`
|
||||
SourceID int64 `json:"sourceId"`
|
||||
SourceType string `json:"sourceType"`
|
||||
@ -71,16 +55,10 @@ type SubscriptionDto struct {
|
||||
}
|
||||
|
||||
type SourceDto struct {
|
||||
//CreatedAt time.Time `json:"CreatedAt"`
|
||||
//UpdatedAt time.Time `json:"UpdatedAt"`
|
||||
//DeletedAt time.Time `json:"DeletedAt"`
|
||||
ID int64 `json:"id"`
|
||||
Site string `json:"site"`
|
||||
Name string `json:"name"`
|
||||
Source string `json:"source"`
|
||||
Type string `json:"type"`
|
||||
Value string `json:"value"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Url string `json:"url"`
|
||||
Tags string `json:"tags"`
|
||||
ID int64 `json:"id"`
|
||||
Source string `json:"source"`
|
||||
DisplayName string `json:"name"`
|
||||
Url string `json:"url"`
|
||||
Tags string `json:"tags"`
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
@ -7,13 +7,14 @@ import (
|
||||
type ArticleEntity struct {
|
||||
ID int64
|
||||
CreatedAt time.Time
|
||||
LastUpdated time.Time
|
||||
UpdatedAt time.Time
|
||||
DeletedAt time.Time
|
||||
SourceID int64
|
||||
Tags string
|
||||
Title string
|
||||
Url string
|
||||
PubDate time.Time
|
||||
IsVideo bool
|
||||
Thumbnail string
|
||||
Description string
|
||||
AuthorName string
|
||||
@ -21,12 +22,12 @@ type ArticleEntity struct {
|
||||
}
|
||||
|
||||
type DiscordQueueEntity struct {
|
||||
ID int64
|
||||
CreatedAt time.Time
|
||||
LastUpdated time.Time
|
||||
DeletedAt time.Time
|
||||
ArticleId int64
|
||||
SourceId int64
|
||||
ID int64
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
DeletedAt time.Time
|
||||
ArticleId int64
|
||||
SourceId int64
|
||||
}
|
||||
|
||||
type DiscordWebHookEntity struct {
|
||||
@ -34,12 +35,12 @@ type DiscordWebHookEntity struct {
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
DeletedAt time.Time
|
||||
Name string
|
||||
Key string
|
||||
Url string
|
||||
Server string
|
||||
Channel string
|
||||
Enabled bool
|
||||
//Name string
|
||||
//Key string
|
||||
Url string
|
||||
Server string
|
||||
Channel string
|
||||
Enabled bool
|
||||
}
|
||||
|
||||
type IconEntity struct {
|
||||
@ -62,18 +63,26 @@ type SettingEntity struct {
|
||||
}
|
||||
|
||||
type SourceEntity struct {
|
||||
ID int64
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
DeletedAt time.Time
|
||||
Site string
|
||||
Name string
|
||||
Source string
|
||||
Type string
|
||||
Value string
|
||||
Enabled bool
|
||||
Url string
|
||||
Tags string
|
||||
ID int64
|
||||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
DeletedAt time.Time
|
||||
|
||||
// Who will collect from it. Used
|
||||
// domain.SourceCollector...
|
||||
Source string
|
||||
|
||||
// Human Readable value to state what is getting collected
|
||||
DisplayName string
|
||||
|
||||
// Tells the parser where to look for data
|
||||
Url string
|
||||
|
||||
// Static tags for this defined record
|
||||
Tags string
|
||||
|
||||
// If the record is disabled, then it will be skipped on processing
|
||||
Enabled bool
|
||||
}
|
||||
|
||||
type SubscriptionEntity struct {
|
||||
|
@ -1,5 +1,26 @@
|
||||
package domain
|
||||
|
||||
type ErrorResponse struct {
|
||||
|
||||
type BaseResponse struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type ArticleResponse struct {
|
||||
BaseResponse
|
||||
Payload []ArticleDto `json:"payload"`
|
||||
}
|
||||
|
||||
type ArticleAndSourceModel struct {
|
||||
Article ArticleDto `json:"article"`
|
||||
Source SourceDto `json:"source"`
|
||||
}
|
||||
|
||||
type ArticleDetailResponse struct {
|
||||
BaseResponse
|
||||
Payload ArticleAndSourceModel `json:"payload"`
|
||||
}
|
||||
|
||||
type DiscordWebhookResponse struct {
|
||||
BaseResponse
|
||||
Payload []DiscordWebHookDto `json:"payload"`
|
||||
}
|
@ -4,186 +4,148 @@ import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"git.jamestombleson.com/jtom38/newsbot-api/internal/domain/models"
|
||||
"github.com/google/uuid"
|
||||
"git.jamestombleson.com/jtom38/newsbot-api/internal/domain"
|
||||
"git.jamestombleson.com/jtom38/newsbot-api/internal/services"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
//func (s *Handler) GetArticleRouter() http.Handler {
|
||||
// r := chi.NewRouter()
|
||||
//
|
||||
// r.Get("/", s.listArticles)
|
||||
// r.Route("/{ID}", func(r chi.Router) {
|
||||
// r.Get("/", s.getArticle)
|
||||
// r.Get("/details", s.getArticleDetails)
|
||||
// })
|
||||
// r.Get("/by/sourceid", s.ListArticlesBySourceId)
|
||||
//
|
||||
// return r
|
||||
//}
|
||||
|
||||
type ArticlesListResults struct {
|
||||
ApiStatusModel
|
||||
Payload []models.ArticleDto `json:"payload"`
|
||||
}
|
||||
|
||||
type ArticleGetResults struct {
|
||||
ApiStatusModel
|
||||
Payload models.ArticleDto `json:"payload"`
|
||||
}
|
||||
|
||||
type ArticleDetailsResult struct {
|
||||
ApiStatusModel
|
||||
Payload models.ArticleDetailsDto `json:"payload"`
|
||||
}
|
||||
|
||||
// ListArticles
|
||||
// @Summary Lists the top 25 records ordering from newest to oldest.
|
||||
// @Produce application/json
|
||||
// @Param page query string false "page number"
|
||||
// @Tags Articles
|
||||
// @Router /articles [get]
|
||||
// @Success 200 {object} ArticlesListResults "OK"
|
||||
// @Success 200 {object} domain.ArticleResponse
|
||||
// @Failure 400 {object} domain.BaseResponse
|
||||
// @Failure 500 {object} domain.BaseResponse
|
||||
func (s *Handler) listArticles(c echo.Context) error {
|
||||
p := ArticlesListResults{
|
||||
ApiStatusModel: ApiStatusModel{
|
||||
Message: "OK",
|
||||
StatusCode: http.StatusOK,
|
||||
resp := domain.ArticleResponse{
|
||||
BaseResponse: domain.BaseResponse{
|
||||
Message: ResponseMessageSuccess,
|
||||
},
|
||||
}
|
||||
|
||||
queryPage := c.QueryParam("page")
|
||||
|
||||
// if a page number was sent, process it
|
||||
if len(queryPage) >= 1 {
|
||||
page, err := strconv.Atoi(queryPage)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusBadRequest, err)
|
||||
}
|
||||
|
||||
res, err := s.dto.ListArticles(c.Request().Context(), 25, page)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, err)
|
||||
}
|
||||
p.Payload = res
|
||||
return c.JSON(http.StatusOK, p)
|
||||
} else {
|
||||
res, err := s.dto.ListArticles(c.Request().Context(), 25, 0)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, err)
|
||||
}
|
||||
p.Payload = res
|
||||
return c.JSON(http.StatusOK, p)
|
||||
page, err := strconv.Atoi(c.QueryParam("page"))
|
||||
if err != nil {
|
||||
page = 0
|
||||
}
|
||||
|
||||
res, err := s.repo.Articles.ListByPage(c.Request().Context(), page, 25)
|
||||
if err != nil {
|
||||
s.WriteError(c, err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
resp.Payload = services.ArticlesToDto(res)
|
||||
return c.JSON(http.StatusOK, resp)
|
||||
}
|
||||
|
||||
// GetArticle
|
||||
// @Summary Returns an article based on defined ID.
|
||||
// @Param ID path string true "uuid"
|
||||
// @Param ID path string true "int"
|
||||
// @Produce application/json
|
||||
// @Tags Articles
|
||||
// @Router /articles/{ID} [get]
|
||||
// @Success 200 {object} ArticleGetResults "OK"
|
||||
// @Failure 400 {object} domain.BaseResponse
|
||||
// @Failure 500 {object} domain.BaseResponse
|
||||
func (s *Handler) getArticle(c echo.Context) error {
|
||||
p := ArticleGetResults{
|
||||
ApiStatusModel: ApiStatusModel{
|
||||
Message: "OK",
|
||||
StatusCode: http.StatusOK,
|
||||
p := domain.ArticleResponse{
|
||||
BaseResponse: domain.BaseResponse{
|
||||
Message: ResponseMessageSuccess,
|
||||
},
|
||||
}
|
||||
|
||||
id := c.Param("ID")
|
||||
uuid, err := uuid.Parse(id)
|
||||
idNumber, err := strconv.Atoi(id)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusBadRequest, err)
|
||||
s.WriteError(c, err, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
res, err := s.dto.GetArticle(c.Request().Context(), uuid)
|
||||
item, err := s.repo.Articles.GetById(c.Request().Context(), int64(idNumber))
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, err)
|
||||
}
|
||||
|
||||
p.Payload = res
|
||||
var dtos []domain.ArticleDto
|
||||
dtos = append(dtos, services.ArticleToDto(item))
|
||||
p.Payload = dtos
|
||||
|
||||
return c.JSON(http.StatusOK, p)
|
||||
}
|
||||
|
||||
// GetArticleDetails
|
||||
// @Summary Returns an article and source based on defined ID.
|
||||
// @Param ID path string true "uuid"
|
||||
// @Param ID path string true "int"
|
||||
// @Produce application/json
|
||||
// @Tags Articles
|
||||
// @Router /articles/{ID}/details [get]
|
||||
// @Success 200 {object} ArticleDetailsResult "OK"
|
||||
// @Failure 400 {object} domain.BaseResponse
|
||||
// @Failure 500 {object} domain.BaseResponse
|
||||
func (s *Handler) getArticleDetails(c echo.Context) error {
|
||||
p := ArticleDetailsResult{
|
||||
ApiStatusModel: ApiStatusModel{
|
||||
Message: "OK",
|
||||
StatusCode: http.StatusOK,
|
||||
p := domain.ArticleDetailResponse{
|
||||
BaseResponse: domain.BaseResponse{
|
||||
Message: ResponseMessageSuccess,
|
||||
},
|
||||
Payload: domain.ArticleAndSourceModel{
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
id := c.Param("ID")
|
||||
uuid, err := uuid.Parse(id)
|
||||
id, err := strconv.Atoi(c.Param("ID"))
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusBadRequest, err)
|
||||
s.WriteError(c, err, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
res, err := s.dto.GetArticleDetails(c.Request().Context(), uuid)
|
||||
article, err := s.repo.Articles.GetById(c.Request().Context(), int64(id))
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, err)
|
||||
s.WriteError(c, err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
p.Payload = res
|
||||
source, err := s.repo.Sources.GetById(c.Request().Context(), article.SourceID)
|
||||
if err != nil {
|
||||
s.WriteError(c, err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
p.Payload.Article = services.ArticleToDto(article)
|
||||
p.Payload.Source = services.SourceToDto(source)
|
||||
|
||||
return c.JSON(http.StatusOK, p)
|
||||
}
|
||||
|
||||
// TODO add page support
|
||||
// ListArticlesBySourceID
|
||||
// @Summary Finds the articles based on the SourceID provided. Returns the top 25.
|
||||
// @Param id query string true "Source ID UUID"
|
||||
// @Param id query string true
|
||||
// @Param page query int false "Page to query"
|
||||
// @Produce application/json
|
||||
// @Tags Articles
|
||||
// @Router /articles/by/sourceid [get]
|
||||
// @Success 200 {object} ArticlesListResults "OK"
|
||||
// @Failure 400 {object} domain.BaseResponse
|
||||
// @Failure 500 {object} domain.BaseResponse
|
||||
func (s *Handler) ListArticlesBySourceId(c echo.Context) error {
|
||||
p := ArticlesListResults{
|
||||
ApiStatusModel: ApiStatusModel{
|
||||
Message: "OK",
|
||||
StatusCode: http.StatusOK,
|
||||
p := domain.ArticleResponse{
|
||||
BaseResponse: domain.BaseResponse{
|
||||
Message: ResponseMessageSuccess,
|
||||
},
|
||||
}
|
||||
|
||||
id := c.QueryParam("id")
|
||||
|
||||
uuid, err := uuid.Parse(id)
|
||||
id, err := strconv.Atoi(c.QueryParam("id"))
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusBadRequest, err)
|
||||
s.WriteError(c, err, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
// if a page number was sent, process it
|
||||
if len(c.QueryParam("page")) >= 1 {
|
||||
_page, err := strconv.Atoi(c.QueryParam("page"))
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusBadRequest, err)
|
||||
}
|
||||
|
||||
res, err := s.dto.ListNewArticlesBySourceId(c.Request().Context(), uuid, 25, _page)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, err)
|
||||
}
|
||||
|
||||
p.Payload = res
|
||||
} else {
|
||||
res, err := s.dto.ListNewArticlesBySourceId(c.Request().Context(), uuid, 25, 0)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, err)
|
||||
}
|
||||
|
||||
p.Payload = res
|
||||
// if the page number is missing, default to 0
|
||||
_page, err := strconv.Atoi(c.QueryParam("page"))
|
||||
if err != nil {
|
||||
_page = 0
|
||||
}
|
||||
|
||||
items, err := s.repo.Articles.ListBySource(c.Request().Context(), _page, 25, id, "")
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, err)
|
||||
}
|
||||
|
||||
p.Payload = services.ArticlesToDto(items)
|
||||
return c.JSON(http.StatusOK, p)
|
||||
|
||||
}
|
||||
|
@ -60,21 +60,21 @@ func (s *Handler) GetDiscordWebHooksById(c echo.Context) error {
|
||||
|
||||
_id := c.Param("ID")
|
||||
if _id == "" {
|
||||
return c.JSON(http.StatusBadRequest, domain.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, domain.BaseResponse{
|
||||
Message: ErrIdValueMissing,
|
||||
})
|
||||
}
|
||||
|
||||
uuid, err := uuid.Parse(_id)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusBadRequest, domain.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, domain.BaseResponse{
|
||||
Message: ErrUnableToParseId,
|
||||
})
|
||||
}
|
||||
|
||||
res, err := s.dto.GetDiscordWebhook(c.Request().Context(), uuid)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusBadRequest, domain.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, domain.BaseResponse{
|
||||
Message: ErrNoRecordFound,
|
||||
})
|
||||
}
|
||||
@ -100,21 +100,21 @@ func (s *Handler) GetDiscordWebHooksByServerAndChannel(c echo.Context) error {
|
||||
|
||||
_server := c.QueryParam("server")
|
||||
if _server == "" {
|
||||
return c.JSON(http.StatusBadRequest, domain.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, domain.BaseResponse{
|
||||
Message: ErrIdValueMissing,
|
||||
})
|
||||
}
|
||||
|
||||
_channel := c.QueryParam("channel")
|
||||
if _channel == "" {
|
||||
return c.JSON(http.StatusBadRequest, domain.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, domain.BaseResponse{
|
||||
Message: fmt.Sprintf("%s channel", ErrParameterMissing),
|
||||
})
|
||||
}
|
||||
|
||||
res, err := s.dto.GetDiscordWebHookByServerAndChannel(c.Request().Context(), _server, _channel)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, domain.BaseResponse{
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
@ -136,22 +136,22 @@ func (s *Handler) NewDiscordWebHook(c echo.Context) error {
|
||||
_channel := c.QueryParam("channel")
|
||||
|
||||
if _url == "" {
|
||||
return c.JSON(http.StatusBadRequest, domain.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, domain.BaseResponse{
|
||||
Message: "url is missing a value",
|
||||
})
|
||||
}
|
||||
if !strings.Contains(_url, "discord.com/api/webhooks") {
|
||||
return c.JSON(http.StatusBadRequest, domain.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, domain.BaseResponse{
|
||||
Message: "invalid url",
|
||||
})
|
||||
}
|
||||
if _server == "" {
|
||||
return c.JSON(http.StatusBadRequest, domain.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, domain.BaseResponse{
|
||||
Message: "server is missing",
|
||||
})
|
||||
}
|
||||
if _channel == "" {
|
||||
return c.JSON(http.StatusBadRequest, domain.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, domain.BaseResponse{
|
||||
Message: "channel is missing",
|
||||
})
|
||||
}
|
||||
@ -176,7 +176,7 @@ func (s *Handler) disableDiscordWebHook(c echo.Context) error {
|
||||
id := c.Param("ID")
|
||||
uuid, err := uuid.Parse(id)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusBadRequest, domain.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, domain.BaseResponse{
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
@ -189,7 +189,7 @@ func (s *Handler) disableDiscordWebHook(c echo.Context) error {
|
||||
|
||||
err = s.Db.DisableDiscordWebHook(c.Request().Context(), uuid)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, domain.BaseResponse{
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
@ -205,7 +205,7 @@ func (s *Handler) enableDiscordWebHook(c echo.Context) error {
|
||||
id := c.Param("ID")
|
||||
uuid, err := uuid.Parse(id)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusBadRequest, domain.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, domain.BaseResponse{
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
|
@ -15,22 +15,24 @@ import (
|
||||
)
|
||||
|
||||
type Handler struct {
|
||||
Router *echo.Echo
|
||||
Db *database.Queries
|
||||
dto *dto.DtoClient
|
||||
config services.Configs
|
||||
sqlConnection *sql.DB
|
||||
Router *echo.Echo
|
||||
Db *database.Queries
|
||||
dto *dto.DtoClient
|
||||
config services.Configs
|
||||
repo services.RepositoryService
|
||||
}
|
||||
|
||||
const (
|
||||
HeaderContentType = "Content-Type"
|
||||
|
||||
ApplicationJson = "application/json"
|
||||
//ApplicationJson = "application/json"
|
||||
|
||||
ErrParameterIdMissing = "The requested parameter ID was not found."
|
||||
ErrParameterMissing = "The requested parameter was found found:"
|
||||
ErrUnableToParseId = "Unable to parse the requested ID."
|
||||
ErrRecordMissing = "The requested record was not found"
|
||||
|
||||
ResponseMessageSuccess = "Success"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -42,18 +44,12 @@ var (
|
||||
|
||||
func NewServer(ctx context.Context, db *database.Queries, configs services.Configs, conn *sql.DB) *Handler {
|
||||
s := &Handler{
|
||||
Db: db,
|
||||
dto: dto.NewDtoClient(db),
|
||||
config: configs,
|
||||
sqlConnection: conn,
|
||||
Db: db,
|
||||
dto: dto.NewDtoClient(db),
|
||||
config: configs,
|
||||
repo: services.NewRepositoryService(conn),
|
||||
}
|
||||
|
||||
db, err := openDatabase(ctx)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
s.Db = db
|
||||
|
||||
router := echo.New()
|
||||
router.GET("/swagger/*", swagger.WrapHandler)
|
||||
|
||||
@ -73,11 +69,11 @@ func NewServer(ctx context.Context, db *database.Queries, configs services.Confi
|
||||
dwh.POST("/:ID/disable", s.disableDiscordWebHook)
|
||||
dwh.POST("/:ID/enable", s.enableDiscordWebHook)
|
||||
|
||||
queue := v1.Group("/queue")
|
||||
queue.GET("/discord/webhooks", s.ListDiscordWebhookQueue) // TODO this needs to be reworked
|
||||
//queue := v1.Group("/queue")
|
||||
//queue.GET("/discord/webhooks", s.ListDiscordWebhookQueue) // TODO this needs to be reworked
|
||||
|
||||
settings := v1.Group("/settings")
|
||||
settings.GET("/", s.getSettings)
|
||||
//settings := v1.Group("/settings")
|
||||
//settings.GET("/", s.getSettings)
|
||||
|
||||
sources := v1.Group("/sources")
|
||||
sources.GET("/", s.listSources)
|
||||
@ -103,28 +99,6 @@ func NewServer(ctx context.Context, db *database.Queries, configs services.Confi
|
||||
return s
|
||||
}
|
||||
|
||||
func openDatabase(ctx context.Context) (*database.Queries, error) {
|
||||
_env := services.NewConfig()
|
||||
connString := _env.GetConfig(services.Sql_Connection_String)
|
||||
db, err := sql.Open("postgres", connString)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
queries := database.New(db)
|
||||
return queries, err
|
||||
}
|
||||
|
||||
func (s *Handler) MountRoutes() {
|
||||
//s.Router.Get("/swagger/*", httpSwagger.Handler(
|
||||
// httpSwagger.URL("doc.json"), //The url pointing to API definition
|
||||
//))
|
||||
//s.Router.Get("/api/settings", s.getSettings)
|
||||
|
||||
//s.Router.Mount("/api/sources", s.GetSourcesRouter())
|
||||
//s.Router.Mount("/api/subscriptions", s.GetSubscriptionsRouter())
|
||||
}
|
||||
|
||||
type ApiStatusModel struct {
|
||||
StatusCode int `json:"status"`
|
||||
Message string `json:"message"`
|
||||
@ -134,8 +108,14 @@ type ApiError struct {
|
||||
*ApiStatusModel
|
||||
}
|
||||
|
||||
func (s *Handler) WriteError(c echo.Context, errMessage string, HttpStatusCode int) error {
|
||||
return c.JSON(HttpStatusCode, domain.ErrorResponse{
|
||||
Message: errMessage,
|
||||
func (s *Handler) WriteError(c echo.Context, errMessage error, HttpStatusCode int) error {
|
||||
return c.JSON(HttpStatusCode, domain.BaseResponse{
|
||||
Message: errMessage.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
func (s *Handler) WriteMessage(c echo.Context, msg string, HttpStatusCode int) error {
|
||||
return c.JSON(HttpStatusCode, domain.BaseResponse{
|
||||
Message: msg,
|
||||
})
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ func (s *Handler) ListDiscordWebhookQueue(c echo.Context) error {
|
||||
// Get the raw resp from sql
|
||||
res, err := s.dto.ListDiscordWebhookQueueDetails(c.Request().Context(), 50)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, domain.BaseResponse{
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ func (s *Handler) getSettings(c echo.Context) error {
|
||||
|
||||
uuid, err := uuid.Parse(id)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusBadRequest, domain.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, domain.BaseResponse{
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
|
@ -10,19 +10,10 @@ import (
|
||||
"git.jamestombleson.com/jtom38/newsbot-api/internal/database"
|
||||
"git.jamestombleson.com/jtom38/newsbot-api/internal/domain"
|
||||
"git.jamestombleson.com/jtom38/newsbot-api/internal/domain/models"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/google/uuid"
|
||||
"github.com/labstack/echo/v4"
|
||||
)
|
||||
|
||||
func (s *Handler) GetSourcesRouter() http.Handler {
|
||||
r := chi.NewRouter()
|
||||
|
||||
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
type ListSources struct {
|
||||
ApiStatusModel
|
||||
Payload []models.SourceDto `json:"payload"`
|
||||
@ -38,8 +29,8 @@ type GetSource struct {
|
||||
// @Produce application/json
|
||||
// @Tags Source
|
||||
// @Router /sources [get]
|
||||
// @Success 200 {object} ListSources "ok"
|
||||
// @Failure 400 {object} ApiError "Unable to reach SQL or Data problems"
|
||||
// @Success 200 {object} ListSources "ok"
|
||||
// @Failure 400 {object} domain.BaseResponse "Unable to reach SQL or Data problems"
|
||||
func (s *Handler) listSources(c echo.Context) error {
|
||||
//TODO Add top?
|
||||
/*
|
||||
@ -61,9 +52,7 @@ func (s *Handler) listSources(c echo.Context) error {
|
||||
// Default way of showing all sources
|
||||
items, err := s.dto.ListSources(c.Request().Context(), 50)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{
|
||||
Message: err.Error(),
|
||||
})
|
||||
s.WriteError(c, err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
p.Payload = items
|
||||
@ -102,7 +91,7 @@ func (s *Handler) listSourcesBySource(c echo.Context) error {
|
||||
// Shows the list by Sources.source
|
||||
res, err := s.dto.ListSourcesBySource(c.Request().Context(), source)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, domain.BaseResponse{
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
@ -132,14 +121,14 @@ func (s *Handler) getSources(c echo.Context) error {
|
||||
id := c.Param("ID")
|
||||
uuid, err := uuid.Parse(id)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusBadRequest, domain.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, domain.BaseResponse{
|
||||
Message: ErrUnableToParseId,
|
||||
})
|
||||
}
|
||||
|
||||
res, err := s.dto.GetSourceById(c.Request().Context(), uuid)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, domain.BaseResponse{
|
||||
Message: ErrNoRecordFound,
|
||||
})
|
||||
}
|
||||
@ -170,7 +159,7 @@ func (s *Handler) GetSourceBySourceAndName(c echo.Context) error {
|
||||
var param domain.GetSourceBySourceAndNameParamRequest
|
||||
err := c.Bind(¶m)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusBadRequest, domain.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, domain.BaseResponse{
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
@ -206,7 +195,7 @@ func (s *Handler) newRedditSource(c echo.Context) error {
|
||||
var param domain.NewSourceParamRequest
|
||||
err := c.Bind(¶m)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusBadRequest, domain.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, domain.BaseResponse{
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
@ -216,12 +205,12 @@ func (s *Handler) newRedditSource(c echo.Context) error {
|
||||
//_tags := query["tags"][0]
|
||||
|
||||
if param.Url == "" {
|
||||
return c.JSON(http.StatusBadRequest, domain.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, domain.BaseResponse{
|
||||
Message: "Url is missing a value",
|
||||
})
|
||||
}
|
||||
if !strings.Contains(param.Url, "reddit.com") {
|
||||
return c.JSON(http.StatusBadRequest, domain.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, domain.BaseResponse{
|
||||
Message: "Invalid URL given",
|
||||
})
|
||||
}
|
||||
@ -248,7 +237,7 @@ func (s *Handler) newRedditSource(c echo.Context) error {
|
||||
}
|
||||
err = s.Db.CreateSource(c.Request().Context(), params)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, domain.BaseResponse{
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
@ -272,7 +261,7 @@ func (s *Handler) newYoutubeSource(c echo.Context) error {
|
||||
var param domain.NewSourceParamRequest
|
||||
err := c.Bind(¶m)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusBadRequest, domain.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, domain.BaseResponse{
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
@ -283,12 +272,12 @@ func (s *Handler) newYoutubeSource(c echo.Context) error {
|
||||
////_tags := query["tags"][0]
|
||||
|
||||
if param.Url == "" {
|
||||
return c.JSON(http.StatusBadRequest, domain.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, domain.BaseResponse{
|
||||
Message: "url is missing a value",
|
||||
})
|
||||
}
|
||||
if !strings.Contains(param.Url, "youtube.com") {
|
||||
return c.JSON(http.StatusBadRequest, domain.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, domain.BaseResponse{
|
||||
Message: "Invalid URL",
|
||||
})
|
||||
}
|
||||
@ -318,7 +307,7 @@ func (s *Handler) newYoutubeSource(c echo.Context) error {
|
||||
|
||||
bJson, err := json.Marshal(¶ms)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, domain.BaseResponse{
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
@ -335,7 +324,7 @@ func (s *Handler) newTwitchSource(c echo.Context) error {
|
||||
var param domain.NewSourceParamRequest
|
||||
err := c.Bind(¶m)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusBadRequest, domain.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, domain.BaseResponse{
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
@ -358,14 +347,14 @@ func (s *Handler) newTwitchSource(c echo.Context) error {
|
||||
}
|
||||
err = s.Db.CreateSource(c.Request().Context(), params)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, domain.BaseResponse{
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
bJson, err := json.Marshal(¶ms)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, domain.BaseResponse{
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
@ -382,7 +371,7 @@ func (s *Handler) deleteSources(c echo.Context) error {
|
||||
id := c.Param("ID")
|
||||
uuid, err := uuid.Parse(id)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusBadRequest, domain.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, domain.BaseResponse{
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
@ -390,7 +379,7 @@ func (s *Handler) deleteSources(c echo.Context) error {
|
||||
// Check to make sure we can find the record
|
||||
_, err = s.Db.GetSourceByID(c.Request().Context(), uuid)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, domain.BaseResponse{
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
@ -398,7 +387,7 @@ func (s *Handler) deleteSources(c echo.Context) error {
|
||||
// Delete the record
|
||||
err = s.Db.DeleteSource(c.Request().Context(), uuid)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, domain.BaseResponse{
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
@ -410,7 +399,7 @@ func (s *Handler) deleteSources(c echo.Context) error {
|
||||
|
||||
b, err := json.Marshal(p)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, domain.BaseResponse{
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
@ -427,7 +416,7 @@ func (s *Handler) disableSource(c echo.Context) error {
|
||||
id := c.Param("ID")
|
||||
uuid, err := uuid.Parse(id)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusBadRequest, domain.ErrorResponse{
|
||||
return c.JSON(http.StatusBadRequest, domain.BaseResponse{
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
@ -435,14 +424,14 @@ func (s *Handler) disableSource(c echo.Context) error {
|
||||
// Check to make sure we can find the record
|
||||
_, err = s.Db.GetSourceByID(c.Request().Context(), uuid)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, domain.BaseResponse{
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
err = s.Db.DisableSource(context.Background(), uuid)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, domain.BaseResponse{
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
@ -454,7 +443,7 @@ func (s *Handler) disableSource(c echo.Context) error {
|
||||
|
||||
b, err := json.Marshal(p)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, domain.BaseResponse{
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
@ -477,14 +466,14 @@ func (s *Handler) enableSource(c echo.Context) error {
|
||||
// Check to make sure we can find the record
|
||||
_, err = s.Db.GetSourceByID(c.Request().Context(), uuid)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, domain.BaseResponse{
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
err = s.Db.EnableSource(c.Request().Context(), uuid)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, domain.BaseResponse{
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
@ -496,7 +485,7 @@ func (s *Handler) enableSource(c echo.Context) error {
|
||||
|
||||
b, err := json.Marshal(p)
|
||||
if err != nil {
|
||||
return c.JSON(http.StatusInternalServerError, domain.ErrorResponse{
|
||||
return c.JSON(http.StatusInternalServerError, domain.BaseResponse{
|
||||
Message: err.Error(),
|
||||
})
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package v1
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"git.jamestombleson.com/jtom38/newsbot-api/internal/database"
|
||||
@ -44,7 +45,7 @@ func (s *Handler) ListSubscriptions(c echo.Context) error {
|
||||
|
||||
res, err := s.dto.ListSubscriptions(c.Request().Context(), 50)
|
||||
if err != nil {
|
||||
return s.WriteError(c, err.Error(), http.StatusBadRequest)
|
||||
return s.WriteError(c, err, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
payload.Payload = res
|
||||
@ -67,7 +68,7 @@ func (s *Handler) ListSubscriptionDetails(c echo.Context) error {
|
||||
|
||||
res, err := s.dto.ListSubscriptionDetails(c.Request().Context(), 50)
|
||||
if err != nil {
|
||||
return s.WriteError(c, err.Error(), http.StatusInternalServerError)
|
||||
return s.WriteError(c, err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
payload.Payload = res
|
||||
@ -93,18 +94,18 @@ func (s *Handler) GetSubscriptionsByDiscordId(c echo.Context) error {
|
||||
|
||||
id := c.QueryParam("id")
|
||||
if id == "" {
|
||||
return s.WriteError(c, ErrIdValueMissing, http.StatusBadRequest)
|
||||
return s.WriteError(c, errors.New(ErrIdValueMissing), http.StatusBadRequest)
|
||||
}
|
||||
|
||||
uuid, err := uuid.Parse(id)
|
||||
if err != nil {
|
||||
return s.WriteError(c, ErrValueNotUuid, http.StatusBadRequest)
|
||||
return s.WriteError(c, errors.New(ErrValueNotUuid), http.StatusBadRequest)
|
||||
|
||||
}
|
||||
|
||||
res, err := s.dto.ListSubscriptionsByDiscordWebhookId(context.Background(), uuid)
|
||||
if err != nil {
|
||||
return s.WriteError(c, err.Error(), http.StatusNoContent)
|
||||
return s.WriteError(c, err, http.StatusNoContent)
|
||||
}
|
||||
|
||||
p.Payload = res
|
||||
@ -128,17 +129,17 @@ func (s *Handler) GetSubscriptionsBySourceId(c echo.Context) error {
|
||||
|
||||
_id := c.QueryParam("id")
|
||||
if _id == "" {
|
||||
return s.WriteError(c, ErrIdValueMissing, http.StatusBadRequest)
|
||||
return s.WriteError(c, errors.New(ErrIdValueMissing), http.StatusBadRequest)
|
||||
}
|
||||
|
||||
uuid, err := uuid.Parse(_id)
|
||||
if err != nil {
|
||||
return s.WriteError(c, err.Error(), http.StatusBadRequest)
|
||||
return s.WriteError(c, err, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
res, err := s.dto.ListSubscriptionsBySourceId(context.Background(), uuid)
|
||||
if err != nil {
|
||||
return s.WriteError(c, err.Error(), http.StatusNoContent)
|
||||
return s.WriteError(c, err, http.StatusNoContent)
|
||||
}
|
||||
|
||||
p.Payload = res
|
||||
@ -158,20 +159,20 @@ func (s *Handler) newDiscordWebHookSubscription(c echo.Context) error {
|
||||
|
||||
// Check to make we didn't get a null
|
||||
if discordWebHookId == "" {
|
||||
return s.WriteError(c, "invalid discordWebHooksId given", http.StatusBadRequest)
|
||||
return s.WriteError(c, errors.New("invalid discordWebHooksId given"), http.StatusBadRequest)
|
||||
}
|
||||
if sourceId == "" {
|
||||
return s.WriteError(c, "invalid sourceID given", http.StatusBadRequest)
|
||||
return s.WriteError(c, errors.New("invalid sourceID given"), http.StatusBadRequest)
|
||||
}
|
||||
|
||||
// Validate they are UUID values
|
||||
uHook, err := uuid.Parse(discordWebHookId)
|
||||
if err != nil {
|
||||
return s.WriteError(c, err.Error(), http.StatusBadRequest)
|
||||
return s.WriteError(c, err, http.StatusBadRequest)
|
||||
}
|
||||
uSource, err := uuid.Parse(sourceId)
|
||||
if err != nil {
|
||||
return s.WriteError(c, err.Error(), http.StatusBadRequest)
|
||||
return s.WriteError(c, err, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
// Check if the sub already exists
|
||||
@ -180,7 +181,7 @@ func (s *Handler) newDiscordWebHookSubscription(c echo.Context) error {
|
||||
Sourceid: uSource,
|
||||
})
|
||||
if err == nil {
|
||||
return s.WriteError(c, "a subscription already exists between these two entities", http.StatusBadRequest)
|
||||
return s.WriteError(c, errors.New("a subscription already exists between these two entities"), http.StatusBadRequest)
|
||||
}
|
||||
|
||||
// Does not exist, so make it.
|
||||
@ -191,12 +192,12 @@ func (s *Handler) newDiscordWebHookSubscription(c echo.Context) error {
|
||||
}
|
||||
err = s.Db.CreateSubscription(context.Background(), params)
|
||||
if err != nil {
|
||||
return s.WriteError(c, err.Error(), http.StatusInternalServerError)
|
||||
return s.WriteError(c, err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
bJson, err := json.Marshal(¶ms)
|
||||
if err != nil {
|
||||
return s.WriteError(c, err.Error(), http.StatusInternalServerError)
|
||||
return s.WriteError(c, err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, bJson)
|
||||
@ -212,17 +213,17 @@ func (s *Handler) DeleteDiscordWebHookSubscription(c echo.Context) error {
|
||||
|
||||
id := c.QueryParam("id")
|
||||
if id == "" {
|
||||
return s.WriteError(c, ErrMissingSubscriptionID, http.StatusBadRequest)
|
||||
return s.WriteError(c, errors.New(ErrMissingSubscriptionID), http.StatusBadRequest)
|
||||
}
|
||||
|
||||
uid, err := uuid.Parse(id)
|
||||
if err != nil {
|
||||
return s.WriteError(c, err.Error(), http.StatusBadRequest)
|
||||
return s.WriteError(c, err, http.StatusBadRequest)
|
||||
}
|
||||
|
||||
err = s.Db.DeleteSubscription(context.Background(), uid)
|
||||
if err != nil {
|
||||
return s.WriteError(c, err.Error(), http.StatusInternalServerError)
|
||||
return s.WriteError(c, err, http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, nil)
|
||||
|
244
internal/repository/article.go
Normal file
244
internal/repository/article.go
Normal file
@ -0,0 +1,244 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"git.jamestombleson.com/jtom38/newsbot-api/internal/domain"
|
||||
"github.com/huandu/go-sqlbuilder"
|
||||
)
|
||||
|
||||
const (
|
||||
ArticleOrderByPublishDateDesc = "pubDate desc"
|
||||
ArticleOrderByPublishDateAsc = "pubDate asc"
|
||||
)
|
||||
|
||||
type ArticlesRepo interface {
|
||||
GetById(ctx context.Context, id int64) (domain.ArticleEntity, error)
|
||||
GetByUrl(ctx context.Context, url string) (domain.ArticleEntity, error)
|
||||
ListTop(ctx context.Context, limit int) ([]domain.ArticleEntity, error)
|
||||
ListByPage(ctx context.Context, page, limit int) ([]domain.ArticleEntity, error)
|
||||
ListByPublishDate(ctx context.Context, page, limit int, orderBy string) ([]domain.ArticleEntity, error)
|
||||
ListBySource(ctx context.Context, page, limit, sourceId int, orderBy string) ([]domain.ArticleEntity, error)
|
||||
Create(ctx context.Context, sourceId int64, tags, title, url, thumbnailUrl, description, authorName, authorImageUrl string, pubDate time.Time, isVideo bool) (int64, error)
|
||||
}
|
||||
|
||||
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(ctx context.Context, 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.QueryContext(ctx, 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(ctx context.Context, 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.QueryContext(ctx, 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) ListTop(ctx context.Context, limit int) ([]domain.ArticleEntity, error) {
|
||||
builder := sqlbuilder.NewSelectBuilder()
|
||||
builder.Select("*")
|
||||
builder.From("articles")
|
||||
builder.Limit(limit)
|
||||
|
||||
query, args := builder.Build()
|
||||
rows, err := ar.conn.QueryContext(ctx, 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(ctx context.Context, page, limit int) ([]domain.ArticleEntity, error) {
|
||||
builder := sqlbuilder.NewSelectBuilder()
|
||||
builder.Select("*")
|
||||
builder.From("articles")
|
||||
builder.OrderBy(ArticleOrderByPublishDateDesc)
|
||||
builder.Offset(page * limit)
|
||||
builder.Limit(limit)
|
||||
|
||||
query, args := builder.Build()
|
||||
rows, err := ar.conn.QueryContext(ctx, 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(ctx context.Context, page, limit int, orderBy string) ([]domain.ArticleEntity, error) {
|
||||
builder := sqlbuilder.NewSelectBuilder()
|
||||
builder.Select("*")
|
||||
builder.From("articles")
|
||||
if orderBy != "" {
|
||||
builder.OrderBy(orderBy)
|
||||
}
|
||||
builder.Offset(page * limit)
|
||||
builder.Limit(limit)
|
||||
|
||||
query, args := builder.Build()
|
||||
rows, err := ar.conn.QueryContext(ctx, 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(ctx context.Context, page, limit, sourceId int, orderBy string) ([]domain.ArticleEntity, error) {
|
||||
builder := sqlbuilder.NewSelectBuilder()
|
||||
builder.Select("*")
|
||||
builder.From("articles")
|
||||
builder.JoinWithOption("InnerJoin", "sources", "articles.sourceId=sources.Id")
|
||||
if orderBy != "" {
|
||||
builder.OrderBy(orderBy)
|
||||
}
|
||||
builder.Where(
|
||||
builder.Equal("SourceId", sourceId),
|
||||
)
|
||||
builder.Offset(50)
|
||||
builder.Limit(page * limit)
|
||||
|
||||
query, args := builder.Build()
|
||||
rows, err := ar.conn.QueryContext(ctx, 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(ctx context.Context, 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", "DeletedAt", "SourceId", "Tags", "Title", "Url", "PubDate", "IsVideo", "ThumbnailUrl", "Description", "AuthorName", "AuthorImageUrl")
|
||||
queryBuilder.Values(dt, dt, timeZero, sourceId, tags, title, url, pubDate, isVideo, thumbnailUrl, description, authorName, authorImageUrl)
|
||||
query, args := queryBuilder.Build()
|
||||
|
||||
_, err := ar.conn.ExecContext(ctx, 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 time.Time
|
||||
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,
|
||||
DeletedAt: deletedAt,
|
||||
SourceID: sourceId,
|
||||
Tags: tags,
|
||||
Title: title,
|
||||
Url: url,
|
||||
PubDate: pubDate,
|
||||
IsVideo: isVideo,
|
||||
Thumbnail: thumbnail,
|
||||
Description: description,
|
||||
AuthorName: authorName,
|
||||
AuthorImageUrl: authorImageUrl,
|
||||
}
|
||||
|
||||
items = append(items, item)
|
||||
}
|
||||
|
||||
return items
|
||||
}
|
156
internal/repository/article_test.go
Normal file
156
internal/repository/article_test.go
Normal file
@ -0,0 +1,156 @@
|
||||
package repository_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.jamestombleson.com/jtom38/newsbot-api/internal/repository"
|
||||
)
|
||||
|
||||
const (
|
||||
articleFakeDotCom = "www.fake.com"
|
||||
)
|
||||
|
||||
func TestCreateArticle(t *testing.T) {
|
||||
t.Log(time.Time{})
|
||||
db, err := setupInMemoryDb()
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
r := repository.NewArticleRepository(db)
|
||||
created, err := r.Create(context.Background(), 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", 0)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
article, err := r.GetByUrl(context.Background(), 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", 0)
|
||||
insertFakeArticles(r, "u2", 0)
|
||||
insertFakeArticles(r, "u3", 0)
|
||||
insertFakeArticles(r, "u4", 0)
|
||||
|
||||
items, err := r.ListTop(context.Background(), 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", 0)
|
||||
insertFakeArticles(r, "u2", 0)
|
||||
insertFakeArticles(r, "u3", 0)
|
||||
insertFakeArticles(r, "u4", 0)
|
||||
|
||||
items, err := r.ListByPage(context.Background(), 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 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(context.Background(), 0, 2, repository.ArticleOrderByPublishDateDesc)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if len(items) != 2 {
|
||||
t.Log("expected two items back")
|
||||
t.FailNow()
|
||||
}
|
||||
pubDate := items[1].PubDate.Day()
|
||||
expectedDay := today.Day() - 1
|
||||
if pubDate != expectedDay {
|
||||
t.Log("expected a 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(context.Background(), 1, "", title, articleFakeDotCom, "", "testing", "", "", pubDate, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
71
internal/repository/common.go
Normal file
71
internal/repository/common.go
Normal file
@ -0,0 +1,71 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"github.com/huandu/go-sqlbuilder"
|
||||
)
|
||||
|
||||
var (
|
||||
timeZero = time.Time{}
|
||||
)
|
||||
|
||||
func deleteFromTable(ctx context.Context, conn *sql.DB, tableName string, id int64) (int64, error) {
|
||||
b := sqlbuilder.NewDeleteBuilder()
|
||||
b.DeleteFrom(tableName)
|
||||
b.Where(
|
||||
b.Equal("Id", id),
|
||||
)
|
||||
query, args := b.Build()
|
||||
|
||||
_, err := conn.ExecContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
func restoreRow(ctx context.Context, conn *sql.DB, tableName string, id int64) (int64, error) {
|
||||
timeZero := time.Time{}
|
||||
b := sqlbuilder.NewUpdateBuilder()
|
||||
b.Update(tableName)
|
||||
b.Set(
|
||||
b.Assign("UpdatedAt", time.Now()),
|
||||
b.Assign("DeletedAt", timeZero),
|
||||
)
|
||||
b.Where(
|
||||
b.Equal("Id", id),
|
||||
)
|
||||
query, args := b.Build()
|
||||
|
||||
_, err := conn.ExecContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
func softDeleteRow(ctx context.Context, conn *sql.DB, tableName string, id int64) (int64, error) {
|
||||
now := time.Now()
|
||||
b := sqlbuilder.NewUpdateBuilder()
|
||||
b.Update(tableName)
|
||||
b.Set(
|
||||
b.Assign("UpdatedAt", now),
|
||||
b.Assign("DeletedAt", now),
|
||||
)
|
||||
b.Where(
|
||||
b.Equal("Id", id),
|
||||
)
|
||||
query, args := b.Build()
|
||||
|
||||
_, err := conn.ExecContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return 1, nil
|
||||
}
|
226
internal/repository/discordWebHooks.go
Normal file
226
internal/repository/discordWebHooks.go
Normal file
@ -0,0 +1,226 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"git.jamestombleson.com/jtom38/newsbot-api/internal/domain"
|
||||
"github.com/huandu/go-sqlbuilder"
|
||||
)
|
||||
|
||||
type DiscordWebHookRepo interface{
|
||||
Create(ctx context.Context, url, server, channel string, enabled bool) (int64, error)
|
||||
Enable(ctx context.Context, id int64) (int64, error)
|
||||
Disable(ctx context.Context, id int64) (int64, error)
|
||||
SoftDelete(ctx context.Context, id int64) (int64, error)
|
||||
Restore(ctx context.Context, id int64) (int64, error)
|
||||
Delete(ctx context.Context, id int64) (int64, error)
|
||||
GetById(ctx context.Context, id int64) (domain.DiscordWebHookEntity, error)
|
||||
GetByUrl(ctx context.Context, url string) (domain.DiscordWebHookEntity, error)
|
||||
ListByServerName(ctx context.Context, name string) ([]domain.DiscordWebHookEntity, error)
|
||||
ListByServerAndChannel(ctx context.Context, server, channel string) ([]domain.DiscordWebHookEntity, error)
|
||||
}
|
||||
|
||||
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", "DeletedAt", "Url", "Server", "Channel", "Enabled")
|
||||
queryBuilder.Values(dt, dt, timeZero, 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()),
|
||||
)
|
||||
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) 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()),
|
||||
)
|
||||
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) SoftDelete(ctx context.Context, id int64) (int64, error) {
|
||||
return softDeleteRow(ctx, r.conn, "DiscordWebHooks", id)
|
||||
}
|
||||
|
||||
func (r discordWebHookRepository) Restore(ctx context.Context, id int64) (int64, error) {
|
||||
return restoreRow(ctx, r.conn, "DiscordWebHooks", id)
|
||||
}
|
||||
|
||||
func (r discordWebHookRepository) Delete(ctx context.Context, id int64) (int64, error) {
|
||||
return deleteFromTable(ctx, r.conn, "DiscordWebHooks", id)
|
||||
}
|
||||
|
||||
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 time.Time
|
||||
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,
|
||||
DeletedAt: deletedAt,
|
||||
Url: url,
|
||||
Server: server,
|
||||
Channel: channel,
|
||||
Enabled: enabled,
|
||||
}
|
||||
|
||||
items = append(items, item)
|
||||
}
|
||||
|
||||
return items, nil
|
||||
}
|
287
internal/repository/discordWebHooks_test.go
Normal file
287
internal/repository/discordWebHooks_test.go
Normal file
@ -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)
|
||||
}
|
@ -14,7 +14,7 @@ const (
|
||||
refreshTokenTableName = "RefreshTokens"
|
||||
)
|
||||
|
||||
type RefreshTokenTable interface {
|
||||
type RefreshToken interface {
|
||||
Create(username string, token string) (int64, error)
|
||||
GetByUsername(name string) (domain.RefreshTokenEntity, error)
|
||||
DeleteById(id int64) (int64, error)
|
||||
|
253
internal/repository/source.go
Normal file
253
internal/repository/source.go
Normal file
@ -0,0 +1,253 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"time"
|
||||
|
||||
"git.jamestombleson.com/jtom38/newsbot-api/internal/domain"
|
||||
"github.com/huandu/go-sqlbuilder"
|
||||
)
|
||||
|
||||
type Sources interface {
|
||||
Create(ctx context.Context, source, displayName, url, tags string, enabled bool) (int64, error)
|
||||
GetById(ctx context.Context, id int64) (domain.SourceEntity, error)
|
||||
GetByDisplayName(ctx context.Context, displayName string) (domain.SourceEntity, error)
|
||||
GetBySource(ctx context.Context, source string) (domain.SourceEntity, error)
|
||||
List(ctx context.Context, page, limit int) ([]domain.SourceEntity, error)
|
||||
ListBySource(ctx context.Context, page, limit int, source string) ([]domain.SourceEntity, error)
|
||||
Enable(ctx context.Context, id int64) (int64, error)
|
||||
Disable(ctx context.Context, id int64) (int64, error)
|
||||
SoftDelete(ctx context.Context, id int64) (int64, error)
|
||||
Restore(ctx context.Context, id int64) (int64, error)
|
||||
Delete(ctx context.Context, id int64) (int64, error)
|
||||
}
|
||||
|
||||
type sourceRepository struct {
|
||||
conn *sql.DB
|
||||
}
|
||||
|
||||
func NewSourceRepository(conn *sql.DB) sourceRepository {
|
||||
return sourceRepository{
|
||||
conn: conn,
|
||||
}
|
||||
}
|
||||
|
||||
func (r sourceRepository) Create(ctx context.Context, source, displayName, url, tags string, enabled bool) (int64, error) {
|
||||
dt := time.Now()
|
||||
queryBuilder := sqlbuilder.NewInsertBuilder()
|
||||
queryBuilder.InsertInto("Sources")
|
||||
queryBuilder.Cols("CreatedAt", "UpdatedAt", "DeletedAt", "DisplayName", "Source", "Url", "Tags", "Enabled")
|
||||
queryBuilder.Values(dt, dt, timeZero, displayName, source, url, tags, enabled)
|
||||
query, args := queryBuilder.Build()
|
||||
|
||||
_, err := r.conn.ExecContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
func (r sourceRepository) GetById(ctx context.Context, id int64) (domain.SourceEntity, error) {
|
||||
b := sqlbuilder.NewSelectBuilder()
|
||||
b.Select("*")
|
||||
b.From("Sources").Where(
|
||||
b.Equal("Id", id),
|
||||
)
|
||||
b.Limit(1)
|
||||
query, args := b.Build()
|
||||
|
||||
rows, err := r.conn.QueryContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return domain.SourceEntity{}, err
|
||||
}
|
||||
|
||||
data, err := r.processRows(rows)
|
||||
if len(data) == 0 {
|
||||
return domain.SourceEntity{}, err
|
||||
}
|
||||
|
||||
return data[0], nil
|
||||
}
|
||||
|
||||
func (r sourceRepository) GetByDisplayName(ctx context.Context, displayName string) (domain.SourceEntity, error) {
|
||||
b := sqlbuilder.NewSelectBuilder()
|
||||
b.Select("*")
|
||||
b.From("Sources").Where(
|
||||
b.Equal("DisplayName", displayName),
|
||||
)
|
||||
b.Limit(1)
|
||||
query, args := b.Build()
|
||||
|
||||
rows, err := r.conn.QueryContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return domain.SourceEntity{}, err
|
||||
}
|
||||
|
||||
data, err := r.processRows(rows)
|
||||
if len(data) == 0 {
|
||||
return domain.SourceEntity{}, err
|
||||
}
|
||||
|
||||
return data[0], nil
|
||||
}
|
||||
|
||||
func (r sourceRepository) GetBySource(ctx context.Context, source string) (domain.SourceEntity, error) {
|
||||
b := sqlbuilder.NewSelectBuilder()
|
||||
b.Select("*")
|
||||
b.From("Sources").Where(
|
||||
b.Equal("Source", source),
|
||||
)
|
||||
b.Limit(1)
|
||||
query, args := b.Build()
|
||||
|
||||
rows, err := r.conn.QueryContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return domain.SourceEntity{}, err
|
||||
}
|
||||
|
||||
data, err := r.processRows(rows)
|
||||
if len(data) == 0 {
|
||||
return domain.SourceEntity{}, err
|
||||
}
|
||||
|
||||
return data[0], nil
|
||||
}
|
||||
|
||||
func (r sourceRepository) List(ctx context.Context, page, limit int) ([]domain.SourceEntity, error) {
|
||||
builder := sqlbuilder.NewSelectBuilder()
|
||||
builder.Select("*")
|
||||
builder.From("Sources")
|
||||
builder.Offset(page * limit)
|
||||
builder.Limit(limit)
|
||||
|
||||
query, args := builder.Build()
|
||||
rows, err := r.conn.QueryContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return []domain.SourceEntity{}, err
|
||||
}
|
||||
|
||||
data, err := r.processRows(rows)
|
||||
if len(data) == 0 {
|
||||
return []domain.SourceEntity{}, err
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (r sourceRepository) ListBySource(ctx context.Context, page, limit int, source string) ([]domain.SourceEntity, error) {
|
||||
builder := sqlbuilder.NewSelectBuilder()
|
||||
builder.Select("*")
|
||||
builder.From("Sources")
|
||||
builder.Where(
|
||||
builder.Equal("Source", source),
|
||||
)
|
||||
builder.Offset(page * limit)
|
||||
builder.Limit(limit)
|
||||
|
||||
query, args := builder.Build()
|
||||
rows, err := r.conn.QueryContext(ctx, query, args...)
|
||||
if err != nil {
|
||||
return []domain.SourceEntity{}, err
|
||||
}
|
||||
|
||||
data, err := r.processRows(rows)
|
||||
if len(data) == 0 {
|
||||
return []domain.SourceEntity{}, err
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (r sourceRepository) Enable(ctx context.Context, id int64) (int64, error) {
|
||||
b := sqlbuilder.NewUpdateBuilder()
|
||||
b.Update("Sources")
|
||||
b.Set(
|
||||
b.Assign("Enabled", true),
|
||||
b.Assign("UpdatedAt", time.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 sourceRepository) Disable(ctx context.Context, id int64) (int64, error) {
|
||||
b := sqlbuilder.NewUpdateBuilder()
|
||||
b.Update("Sources")
|
||||
b.Set(
|
||||
b.Assign("Enabled", false),
|
||||
b.Assign("UpdatedAt", time.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 sourceRepository) SoftDelete(ctx context.Context, id int64) (int64, error) {
|
||||
return softDeleteRow(ctx, r.conn, "Sources", id)
|
||||
}
|
||||
|
||||
func (r sourceRepository) Restore(ctx context.Context, id int64) (int64, error) {
|
||||
return restoreRow(ctx, r.conn, "Sources", id)
|
||||
}
|
||||
|
||||
func (r sourceRepository) Delete(ctx context.Context, id int64) (int64, error) {
|
||||
return deleteFromTable(ctx, r.conn, "Sources", id)
|
||||
}
|
||||
|
||||
func (r sourceRepository) processRows(rows *sql.Rows) ([]domain.SourceEntity, error) {
|
||||
items := []domain.SourceEntity{}
|
||||
|
||||
for rows.Next() {
|
||||
var id int64
|
||||
var createdAt time.Time
|
||||
var updatedAt time.Time
|
||||
var deletedAt time.Time
|
||||
var displayName string
|
||||
var source string
|
||||
var enabled bool
|
||||
var url string
|
||||
var tags string
|
||||
err := rows.Scan(
|
||||
&id, &createdAt, &updatedAt,
|
||||
&deletedAt, &displayName, &source,
|
||||
&enabled, &url, &tags,
|
||||
)
|
||||
if err != nil {
|
||||
return items, err
|
||||
}
|
||||
|
||||
item := domain.SourceEntity{
|
||||
ID: id,
|
||||
CreatedAt: createdAt,
|
||||
UpdatedAt: updatedAt,
|
||||
DeletedAt: deletedAt,
|
||||
DisplayName: displayName,
|
||||
Source: source,
|
||||
Enabled: enabled,
|
||||
Url: url,
|
||||
Tags: tags,
|
||||
}
|
||||
|
||||
items = append(items, item)
|
||||
}
|
||||
|
||||
return items, nil
|
||||
}
|
246
internal/repository/source_test.go
Normal file
246
internal/repository/source_test.go
Normal file
@ -0,0 +1,246 @@
|
||||
package repository_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"git.jamestombleson.com/jtom38/newsbot-api/internal/domain"
|
||||
"git.jamestombleson.com/jtom38/newsbot-api/internal/repository"
|
||||
)
|
||||
|
||||
func TestSourceCreate(t *testing.T) {
|
||||
db, err := setupInMemoryDb()
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
defer db.Close()
|
||||
ctx := context.Background()
|
||||
r := repository.NewSourceRepository(db)
|
||||
|
||||
rows, err := r.Create(ctx, domain.SourceCollectorRss, "Test", "www.badurl.com", "rss, badurl", true)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if rows != 1 {
|
||||
t.Log("failed to create a record, why")
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSourceGetById(t *testing.T) {
|
||||
db, err := setupInMemoryDb()
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
defer db.Close()
|
||||
ctx := context.Background()
|
||||
r := repository.NewSourceRepository(db)
|
||||
|
||||
_, err = r.Create(ctx, domain.SourceCollectorRss, "Test", "www.badurl.com", "rss, badurl", true)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
item, err := r.GetById(ctx, 1)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
if item.ID != 1 {
|
||||
t.Log("got no record or the wrong one")
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSourceGetByDisplayName(t *testing.T) {
|
||||
db, err := setupInMemoryDb()
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
defer db.Close()
|
||||
ctx := context.Background()
|
||||
r := repository.NewSourceRepository(db)
|
||||
|
||||
_, err = r.Create(ctx, domain.SourceCollectorRss, "Test", "www.badurl.com", "rss, badurl", true)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
item, err := r.GetByDisplayName(ctx, "Test")
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
if item.DisplayName != "Test" {
|
||||
t.Log("got no record or the wrong one")
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSourceGetBySource(t *testing.T) {
|
||||
db, err := setupInMemoryDb()
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
defer db.Close()
|
||||
ctx := context.Background()
|
||||
r := repository.NewSourceRepository(db)
|
||||
|
||||
_, err = r.Create(ctx, domain.SourceCollectorRss, "Test", "www.badurl.com", "rss, badurl", true)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
item, err := r.GetBySource(ctx, domain.SourceCollectorRss)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
if item.Source != domain.SourceCollectorRss {
|
||||
t.Log("got no record or the wrong one")
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSourceList(t *testing.T) {
|
||||
db, err := setupInMemoryDb()
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
defer db.Close()
|
||||
ctx := context.Background()
|
||||
r := repository.NewSourceRepository(db)
|
||||
|
||||
_, _ = r.Create(ctx, domain.SourceCollectorRss, "Test", "www.badurl.com", "rss, badurl", true)
|
||||
_, _ = r.Create(ctx, domain.SourceCollectorRss, "Test", "www.badurl.com", "rss, badurl", true)
|
||||
_, _ = r.Create(ctx, domain.SourceCollectorRss, "Test", "www.badurl.com", "rss, badurl", true)
|
||||
_, _ = r.Create(ctx, domain.SourceCollectorRss, "Test", "www.badurl.com", "rss, badurl", true)
|
||||
|
||||
items, err := r.List(ctx, 0, 4)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
if len(items ) != 4 {
|
||||
t.Log("something bad happened here")
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSourceListBySource(t *testing.T) {
|
||||
db, err := setupInMemoryDb()
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
defer db.Close()
|
||||
ctx := context.Background()
|
||||
r := repository.NewSourceRepository(db)
|
||||
|
||||
_, _ = r.Create(ctx, domain.SourceCollectorRss, "Test", "www.badurl.com", "rss, badurl", true)
|
||||
_, _ = r.Create(ctx, domain.SourceCollectorRss, "Test", "www.badurl.com", "rss, badurl", true)
|
||||
_, _ = r.Create(ctx, domain.SourceCollectorRss, "Test", "www.badurl.com", "rss, badurl", true)
|
||||
_, _ = r.Create(ctx, domain.SourceCollectorRss, "Test", "www.badurl.com", "rss, badurl", true)
|
||||
|
||||
items, err := r.ListBySource(ctx, 0, 4, domain.SourceCollectorRss)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
if len(items ) != 4 {
|
||||
t.Log("something bad happened here")
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSourcesEnableRecord(t *testing.T) {
|
||||
// This depends on the seed migration
|
||||
db, err := setupInMemoryDb()
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
r := repository.NewSourceRepository(db)
|
||||
_, _ = r.Create(ctx, domain.SourceCollectorRss, "Test", "www.badurl.com", "rss, badurl", false)
|
||||
item, err := r.GetByDisplayName(ctx, "Test")
|
||||
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, item.ID)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
updated, err := r.GetById(ctx, item.ID)
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
if item.Enabled == updated.Enabled {
|
||||
t.Log("failed to update the enabled value")
|
||||
t.FailNow()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSourcesDisableRecord(t *testing.T) {
|
||||
db, err := setupInMemoryDb()
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
t.FailNow()
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
ctx := context.Background()
|
||||
r := repository.NewSourceRepository(db)
|
||||
_, _ = r.Create(ctx, domain.SourceCollectorRss, "Test", "www.badurl.com", "rss, badurl", true)
|
||||
item, err := r.GetByDisplayName(ctx, "Test")
|
||||
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()
|
||||
}
|
||||
}
|
@ -17,7 +17,7 @@ const (
|
||||
ErrUserNotFound string = "requested user was not found"
|
||||
)
|
||||
|
||||
type IUserTable interface {
|
||||
type Users interface {
|
||||
GetByName(name string) (domain.UserEntity, error)
|
||||
Create(name, password, scope string) (int64, error)
|
||||
Update(id int, entity domain.UserEntity) error
|
||||
@ -27,17 +27,17 @@ type IUserTable interface {
|
||||
}
|
||||
|
||||
// Creates a new instance of UserRepository with the bound sql
|
||||
func NewUserRepository(conn *sql.DB) UserRepository {
|
||||
return UserRepository{
|
||||
func NewUserRepository(conn *sql.DB) userRepository {
|
||||
return userRepository{
|
||||
connection: conn,
|
||||
}
|
||||
}
|
||||
|
||||
type UserRepository struct {
|
||||
type userRepository struct {
|
||||
connection *sql.DB
|
||||
}
|
||||
|
||||
func (ur UserRepository) GetByName(name string) (domain.UserEntity, error) {
|
||||
func (ur userRepository) GetByName(name string) (domain.UserEntity, error) {
|
||||
builder := sqlbuilder.NewSelectBuilder()
|
||||
builder.Select("*").From("users").Where(
|
||||
builder.E("Name", name),
|
||||
@ -57,7 +57,7 @@ func (ur UserRepository) GetByName(name string) (domain.UserEntity, error) {
|
||||
return data[0], nil
|
||||
}
|
||||
|
||||
func (ur UserRepository) Create(name, password, scope string) (int64, error) {
|
||||
func (ur userRepository) Create(name, password, scope string) (int64, error) {
|
||||
passwordBytes := []byte(password)
|
||||
hash, err := bcrypt.GenerateFromPassword(passwordBytes, bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
@ -79,11 +79,11 @@ func (ur UserRepository) Create(name, password, scope string) (int64, error) {
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
func (ur UserRepository) Update(id int, entity domain.UserEntity) error {
|
||||
func (ur userRepository) Update(id int, entity domain.UserEntity) error {
|
||||
return errors.New("not implemented")
|
||||
}
|
||||
|
||||
func (ur UserRepository) UpdatePassword(name, password string) error {
|
||||
func (ur userRepository) UpdatePassword(name, password string) error {
|
||||
_, err := ur.GetByName(name)
|
||||
if err != nil {
|
||||
return nil
|
||||
@ -97,7 +97,7 @@ func (ur UserRepository) UpdatePassword(name, password string) error {
|
||||
|
||||
// If the hash matches what we have in the database, an error will not be returned.
|
||||
// If the user does not exist or the hash does not match, an error will be returned
|
||||
func (ur UserRepository) CheckUserHash(name, password string) error {
|
||||
func (ur userRepository) CheckUserHash(name, password string) error {
|
||||
record, err := ur.GetByName(name)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -111,7 +111,7 @@ func (ur UserRepository) CheckUserHash(name, password string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ur UserRepository) UpdateScopes(name, scope string) error {
|
||||
func (ur userRepository) UpdateScopes(name, scope string) error {
|
||||
builder := sqlbuilder.NewUpdateBuilder()
|
||||
builder.Update("users")
|
||||
builder.Set(
|
||||
@ -129,7 +129,7 @@ func (ur UserRepository) UpdateScopes(name, scope string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ur UserRepository) processRows(rows *sql.Rows) []domain.UserEntity {
|
||||
func (ur userRepository) processRows(rows *sql.Rows) []domain.UserEntity {
|
||||
items := []domain.UserEntity{}
|
||||
|
||||
for rows.Next() {
|
||||
|
@ -12,7 +12,6 @@ import (
|
||||
)
|
||||
|
||||
func TestCanCreateNewUser(t *testing.T) {
|
||||
//t.Log(time.Now().String())
|
||||
db, err := setupInMemoryDb()
|
||||
if err != nil {
|
||||
t.Log(err)
|
||||
|
25
internal/services/database.go
Normal file
25
internal/services/database.go
Normal file
@ -0,0 +1,25 @@
|
||||
package services
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"git.jamestombleson.com/jtom38/newsbot-api/internal/repository"
|
||||
)
|
||||
|
||||
type RepositoryService struct {
|
||||
Articles repository.ArticlesRepo
|
||||
DiscordWebHooks repository.DiscordWebHookRepo
|
||||
Sources repository.Sources
|
||||
Users repository.Users
|
||||
RefreshTokens repository.RefreshToken
|
||||
}
|
||||
|
||||
func NewRepositoryService(conn *sql.DB) RepositoryService {
|
||||
return RepositoryService{
|
||||
Articles: repository.NewArticleRepository(conn),
|
||||
DiscordWebHooks: repository.NewDiscordWebHookRepository(conn),
|
||||
Sources: repository.NewSourceRepository(conn),
|
||||
Users: repository.NewUserRepository(conn),
|
||||
RefreshTokens: repository.NewRefreshTokenRepository(conn),
|
||||
}
|
||||
}
|
46
internal/services/dtoconv.go
Normal file
46
internal/services/dtoconv.go
Normal file
@ -0,0 +1,46 @@
|
||||
package services
|
||||
|
||||
import "git.jamestombleson.com/jtom38/newsbot-api/internal/domain"
|
||||
|
||||
func ArticlesToDto(items []domain.ArticleEntity) []domain.ArticleDto {
|
||||
var dtos []domain.ArticleDto
|
||||
for _, item := range items {
|
||||
dtos = append(dtos, ArticleToDto(item))
|
||||
}
|
||||
return dtos
|
||||
}
|
||||
|
||||
func ArticleToDto(item domain.ArticleEntity) domain.ArticleDto {
|
||||
return domain.ArticleDto{
|
||||
ID: item.ID,
|
||||
SourceID: item.SourceID,
|
||||
Tags: item.Tags,
|
||||
Title: item.Title,
|
||||
Url: item.Url,
|
||||
PubDate: item.PubDate,
|
||||
IsVideo: item.IsVideo,
|
||||
Thumbnail: item.Thumbnail,
|
||||
Description: item.Description,
|
||||
AuthorName: item.AuthorName,
|
||||
AuthorImageUrl: item.AuthorImageUrl,
|
||||
}
|
||||
}
|
||||
|
||||
func SourcesToDto(items []domain.SourceEntity) []domain.SourceDto {
|
||||
var dtos []domain.SourceDto
|
||||
for _, item := range items {
|
||||
dtos = append(dtos, SourceToDto(item))
|
||||
}
|
||||
return dtos
|
||||
}
|
||||
|
||||
func SourceToDto(item domain.SourceEntity) domain.SourceDto {
|
||||
return domain.SourceDto{
|
||||
ID: item.ID,
|
||||
Source: item.Source,
|
||||
DisplayName: item.DisplayName,
|
||||
Url: item.Url,
|
||||
Tags: item.Tags,
|
||||
Enabled: item.Enabled,
|
||||
}
|
||||
}
|
@ -26,7 +26,7 @@ func NewRssClient(sourceRecord domain.SourceEntity) rssClient {
|
||||
//}
|
||||
|
||||
func (rc rssClient) getCacheGroup() string {
|
||||
return fmt.Sprintf("rss-%v", rc.SourceRecord.Name)
|
||||
return fmt.Sprintf("rss-%v", rc.SourceRecord.DisplayName)
|
||||
}
|
||||
|
||||
func (rc rssClient) GetContent() error {
|
||||
|
@ -9,7 +9,7 @@ import (
|
||||
|
||||
var rssRecord = domain.SourceEntity{
|
||||
ID: 1,
|
||||
Name: "ArsTechnica",
|
||||
DisplayName: "ArsTechnica",
|
||||
Url: "https://feeds.arstechnica.com/arstechnica/index",
|
||||
}
|
||||
|
||||
|
5
makefile
5
makefile
@ -3,7 +3,6 @@ help: ## Shows this help command
|
||||
@egrep -h '\s##\s' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
|
||||
|
||||
build: ## builds the application with the current go runtime
|
||||
sqlc generate
|
||||
~/go/bin/swag f
|
||||
~/go/bin/swag i
|
||||
go build .
|
||||
@ -19,8 +18,8 @@ migrate-dev-down: ## revert sql migrations to dev db
|
||||
goose -dir "./internal/database/migrations" postgres "user=postgres password=postgres dbname=postgres sslmode=disable" down
|
||||
|
||||
swag: ## Generates the swagger documentation with the swag tool
|
||||
~/go/bin/swag f
|
||||
~/go/bin/swag i
|
||||
~/go/bin/swag f
|
||||
~/go/bin/swag init -g cmd/server.go
|
||||
|
||||
gensql: ## Generates SQL code with sqlc
|
||||
sqlc generate
|
Loading…
Reference in New Issue
Block a user