From 663cbc4e37f52d3192de89437c0cb82759ca50dc Mon Sep 17 00:00:00 2001 From: James Tombleson Date: Sat, 21 Jan 2023 16:08:12 -0800 Subject: [PATCH] article routes have been moved to support dto --- docs/docs.go | 215 ++++++++++++++++++++++++++++++++++++++----- docs/swagger.json | 215 ++++++++++++++++++++++++++++++++++++++----- docs/swagger.yaml | 144 +++++++++++++++++++++++++---- domain/models/dto.go | 33 +++++++ dto/articles.go | 115 +++++++++++++++++++++++ dto/sources.go | 26 ++++++ main.go | 13 ++- routes/articles.go | 188 ++++++++++++++++++++++++------------- routes/server.go | 27 +++--- 9 files changed, 827 insertions(+), 149 deletions(-) create mode 100644 dto/articles.go create mode 100644 dto/sources.go diff --git a/docs/docs.go b/docs/docs.go index fb33cf1..f8ace09 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -25,7 +25,14 @@ const docTemplate = `{ "Articles" ], "summary": "Lists the top 50 records", - "responses": {} + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/routes.ArticlesListResults" + } + } + } } }, "/articles/by/sourceid": { @@ -46,28 +53,14 @@ const docTemplate = `{ "required": true } ], - "responses": {} - } - }, - "/articles/by/tag": { - "get": { - "produces": [ - "application/json" - ], - "tags": [ - "Articles" - ], - "summary": "Finds the articles based on the SourceID provided. Returns the top 50.", - "parameters": [ - { - "type": "string", - "description": "Tag name", - "name": "tag", - "in": "query", - "required": true + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/routes.ArticlesListResults" + } } - ], - "responses": {} + } } }, "/articles/{ID}": { @@ -83,12 +76,47 @@ const docTemplate = `{ { "type": "string", "description": "uuid", - "name": "id", + "name": "ID", "in": "path", "required": true } ], - "responses": {} + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/routes.ArticleGetResults" + } + } + } + } + }, + "/articles/{ID}/details": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Articles" + ], + "summary": "Returns an article and source based on defined ID.", + "parameters": [ + { + "type": "string", + "description": "uuid", + "name": "ID", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/routes.ArticleDetailsResult" + } + } + } } }, "/discord/webhooks": { @@ -767,6 +795,100 @@ const docTemplate = `{ } }, "definitions": { + "models.ArticleDetailsDto": { + "type": "object", + "properties": { + "authorImage": { + "type": "string" + }, + "authorName": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "pubdate": { + "type": "string" + }, + "source": { + "$ref": "#/definitions/models.SourceDto" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "thumbnail": { + "type": "string" + }, + "title": { + "type": "string" + }, + "url": { + "type": "string" + }, + "video": { + "type": "string" + }, + "videoHeight": { + "type": "integer" + }, + "videoWidth": { + "type": "integer" + } + } + }, + "models.ArticleDto": { + "type": "object", + "properties": { + "authorImage": { + "type": "string" + }, + "authorName": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "pubdate": { + "type": "string" + }, + "sourceid": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "thumbnail": { + "type": "string" + }, + "title": { + "type": "string" + }, + "url": { + "type": "string" + }, + "video": { + "type": "string" + }, + "videoHeight": { + "type": "integer" + }, + "videoWidth": { + "type": "integer" + } + } + }, "models.DiscordQueueDto": { "type": "object", "properties": { @@ -875,6 +997,51 @@ const docTemplate = `{ } } }, + "routes.ArticleDetailsResult": { + "type": "object", + "properties": { + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/definitions/models.ArticleDetailsDto" + }, + "status": { + "type": "integer" + } + } + }, + "routes.ArticleGetResults": { + "type": "object", + "properties": { + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/definitions/models.ArticleDto" + }, + "status": { + "type": "integer" + } + } + }, + "routes.ArticlesListResults": { + "type": "object", + "properties": { + "message": { + "type": "string" + }, + "payload": { + "type": "array", + "items": { + "$ref": "#/definitions/models.ArticleDto" + } + }, + "status": { + "type": "integer" + } + } + }, "routes.GetSourceResult": { "type": "object", "properties": { diff --git a/docs/swagger.json b/docs/swagger.json index 651a874..fff510e 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -16,7 +16,14 @@ "Articles" ], "summary": "Lists the top 50 records", - "responses": {} + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/routes.ArticlesListResults" + } + } + } } }, "/articles/by/sourceid": { @@ -37,28 +44,14 @@ "required": true } ], - "responses": {} - } - }, - "/articles/by/tag": { - "get": { - "produces": [ - "application/json" - ], - "tags": [ - "Articles" - ], - "summary": "Finds the articles based on the SourceID provided. Returns the top 50.", - "parameters": [ - { - "type": "string", - "description": "Tag name", - "name": "tag", - "in": "query", - "required": true + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/routes.ArticlesListResults" + } } - ], - "responses": {} + } } }, "/articles/{ID}": { @@ -74,12 +67,47 @@ { "type": "string", "description": "uuid", - "name": "id", + "name": "ID", "in": "path", "required": true } ], - "responses": {} + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/routes.ArticleGetResults" + } + } + } + } + }, + "/articles/{ID}/details": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Articles" + ], + "summary": "Returns an article and source based on defined ID.", + "parameters": [ + { + "type": "string", + "description": "uuid", + "name": "ID", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/routes.ArticleDetailsResult" + } + } + } } }, "/discord/webhooks": { @@ -758,6 +786,100 @@ } }, "definitions": { + "models.ArticleDetailsDto": { + "type": "object", + "properties": { + "authorImage": { + "type": "string" + }, + "authorName": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "pubdate": { + "type": "string" + }, + "source": { + "$ref": "#/definitions/models.SourceDto" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "thumbnail": { + "type": "string" + }, + "title": { + "type": "string" + }, + "url": { + "type": "string" + }, + "video": { + "type": "string" + }, + "videoHeight": { + "type": "integer" + }, + "videoWidth": { + "type": "integer" + } + } + }, + "models.ArticleDto": { + "type": "object", + "properties": { + "authorImage": { + "type": "string" + }, + "authorName": { + "type": "string" + }, + "description": { + "type": "string" + }, + "id": { + "type": "string" + }, + "pubdate": { + "type": "string" + }, + "sourceid": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "thumbnail": { + "type": "string" + }, + "title": { + "type": "string" + }, + "url": { + "type": "string" + }, + "video": { + "type": "string" + }, + "videoHeight": { + "type": "integer" + }, + "videoWidth": { + "type": "integer" + } + } + }, "models.DiscordQueueDto": { "type": "object", "properties": { @@ -866,6 +988,51 @@ } } }, + "routes.ArticleDetailsResult": { + "type": "object", + "properties": { + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/definitions/models.ArticleDetailsDto" + }, + "status": { + "type": "integer" + } + } + }, + "routes.ArticleGetResults": { + "type": "object", + "properties": { + "message": { + "type": "string" + }, + "payload": { + "$ref": "#/definitions/models.ArticleDto" + }, + "status": { + "type": "integer" + } + } + }, + "routes.ArticlesListResults": { + "type": "object", + "properties": { + "message": { + "type": "string" + }, + "payload": { + "type": "array", + "items": { + "$ref": "#/definitions/models.ArticleDto" + } + }, + "status": { + "type": "integer" + } + } + }, "routes.GetSourceResult": { "type": "object", "properties": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index 8eba8d7..a664598 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -1,5 +1,67 @@ basePath: /api definitions: + models.ArticleDetailsDto: + properties: + authorImage: + type: string + authorName: + type: string + description: + type: string + id: + type: string + pubdate: + type: string + source: + $ref: '#/definitions/models.SourceDto' + tags: + items: + type: string + type: array + thumbnail: + type: string + title: + type: string + url: + type: string + video: + type: string + videoHeight: + type: integer + videoWidth: + type: integer + type: object + models.ArticleDto: + properties: + authorImage: + type: string + authorName: + type: string + description: + type: string + id: + type: string + pubdate: + type: string + sourceid: + type: string + tags: + items: + type: string + type: array + thumbnail: + type: string + title: + type: string + url: + type: string + video: + type: string + videoHeight: + type: integer + videoWidth: + type: integer + type: object models.DiscordQueueDto: properties: articleId: @@ -70,6 +132,35 @@ definitions: status: type: integer type: object + routes.ArticleDetailsResult: + properties: + message: + type: string + payload: + $ref: '#/definitions/models.ArticleDetailsDto' + status: + type: integer + type: object + routes.ArticleGetResults: + properties: + message: + type: string + payload: + $ref: '#/definitions/models.ArticleDto' + status: + type: integer + type: object + routes.ArticlesListResults: + properties: + message: + type: string + payload: + items: + $ref: '#/definitions/models.ArticleDto' + type: array + status: + type: integer + type: object routes.GetSourceResult: properties: message: @@ -132,7 +223,11 @@ paths: get: produces: - application/json - responses: {} + responses: + "200": + description: OK + schema: + $ref: '#/definitions/routes.ArticlesListResults' summary: Lists the top 50 records tags: - Articles @@ -141,15 +236,37 @@ paths: parameters: - description: uuid in: path - name: id + name: ID required: true type: string produces: - application/json - responses: {} + responses: + "200": + description: OK + schema: + $ref: '#/definitions/routes.ArticleGetResults' summary: Returns an article based on defined ID. tags: - Articles + /articles/{ID}/details: + get: + parameters: + - description: uuid + in: path + name: ID + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/routes.ArticleDetailsResult' + summary: Returns an article and source based on defined ID. + tags: + - Articles /articles/by/sourceid: get: parameters: @@ -160,22 +277,11 @@ paths: type: string produces: - application/json - responses: {} - summary: Finds the articles based on the SourceID provided. Returns the top - 50. - tags: - - Articles - /articles/by/tag: - get: - parameters: - - description: Tag name - in: query - name: tag - required: true - type: string - produces: - - application/json - responses: {} + responses: + "200": + description: OK + schema: + $ref: '#/definitions/routes.ArticlesListResults' summary: Finds the articles based on the SourceID provided. Returns the top 50. tags: diff --git a/domain/models/dto.go b/domain/models/dto.go index 91a6546..d25a8b1 100644 --- a/domain/models/dto.go +++ b/domain/models/dto.go @@ -2,12 +2,45 @@ package models import ( "strings" + "time" "github.com/google/uuid" "github.com/jtom38/newsbot/collector/database" ) +type ArticleDto struct { + ID uuid.UUID `json:"id"` + Source uuid.UUID `json:"sourceid"` + Tags []string `json:"tags"` + Title string `json:"title"` + Url string `json:"url"` + Pubdate time.Time `json:"pubdate"` + Video string `json:"video"` + Videoheight int32 `json:"videoHeight"` + Videowidth int32 `json:"videoWidth"` + Thumbnail string `json:"thumbnail"` + Description string `json:"description"` + Authorname string `json:"authorName"` + Authorimage string `json:"authorImage"` +} + +type ArticleDetailsDto struct { + ID uuid.UUID `json:"id"` + Source SourceDto `json:"source"` + Tags []string `json:"tags"` + Title string `json:"title"` + Url string `json:"url"` + Pubdate time.Time `json:"pubdate"` + Video string `json:"video"` + Videoheight int32 `json:"videoHeight"` + Videowidth int32 `json:"videoWidth"` + Thumbnail string `json:"thumbnail"` + Description string `json:"description"` + Authorname string `json:"authorName"` + Authorimage string `json:"authorImage"` +} + type DiscordWebHooksDto struct { ID uuid.UUID `json:"ID"` Url string `json:"url"` diff --git a/dto/articles.go b/dto/articles.go new file mode 100644 index 0000000..8de325e --- /dev/null +++ b/dto/articles.go @@ -0,0 +1,115 @@ +// The converter package lives between the database calls and the API calls. +// This way if any new methods like RPC calls are added later, the API does not need to be reworked as much +package dto + +import ( + "context" + "strings" + + "github.com/google/uuid" + "github.com/jtom38/newsbot/collector/database" + "github.com/jtom38/newsbot/collector/domain/models" +) + +type DtoClient struct { + db *database.Queries +} + +func NewDtoClient(db *database.Queries) DtoClient { + return DtoClient{ + db: db, + } +} + +func (c DtoClient) ListArticles(ctx context.Context, limit int) ([]models.ArticleDto, error) { + var res []models.ArticleDto + + a, err := c.db.ListArticles(ctx, int32(limit)) + if err != nil { + return res, err + } + + for _, article := range a { + res = append(res, c.convertArticle(article)) + } + return res, nil +} + +func (c DtoClient) GetArticle(ctx context.Context, ID uuid.UUID) (models.ArticleDto, error) { + a, err := c.db.GetArticleByID(ctx, ID) + if err != nil { + return models.ArticleDto{}, err + } + + return c.convertArticle(a), nil +} + +func (c DtoClient) GetArticleDetails(ctx context.Context, ID uuid.UUID) (models.ArticleDetailsDto, error) { + a, err := c.db.GetArticleByID(ctx, ID) + if err != nil { + return models.ArticleDetailsDto{}, err + } + + s, err := c.db.GetSourceByID(ctx, a.Sourceid) + if err != nil { + return models.ArticleDetailsDto{}, err + } + + res := c.convertArticleDetails(a, s) + + return res, nil +} + +func (c DtoClient) GetArticlesBySourceId(ctx context.Context, SourceID uuid.UUID) ([]models.ArticleDto, error) { + var res []models.ArticleDto + a, err := c.db.GetArticlesBySourceId(ctx, SourceID) + if err != nil { + return res, err + } + + for _, article := range a { + res = append(res, c.convertArticle(article)) + } + + return res, nil +} + +func (c DtoClient) convertArticle(i database.Article) models.ArticleDto { + return models.ArticleDto{ + ID: i.ID, + Source: i.Sourceid, + Tags: c.SplitTags(i.Tags), + Title: i.Title, + Url: i.Url, + Pubdate: i.Pubdate, + Video: i.Video.String, + Videoheight: i.Videoheight, + Videowidth: i.Videoheight, + Thumbnail: i.Thumbnail, + Description: i.Description, + Authorname: i.Authorname.String, + Authorimage: i.Authorimage.String, + } +} + +func (c DtoClient) convertArticleDetails(i database.Article, s database.Source) models.ArticleDetailsDto { + return models.ArticleDetailsDto{ + ID: i.ID, + Source: c.ConvertToSourceDto(s), + Tags: c.SplitTags(i.Tags), + Title: i.Title, + Url: i.Url, + Pubdate: i.Pubdate, + Video: i.Video.String, + Videoheight: i.Videoheight, + Videowidth: i.Videoheight, + Thumbnail: i.Thumbnail, + Description: i.Description, + Authorname: i.Authorname.String, + Authorimage: i.Authorimage.String, + } +} + +func (c DtoClient) SplitTags(t string) []string { + return strings.Split(t, ", ") +} diff --git a/dto/sources.go b/dto/sources.go new file mode 100644 index 0000000..f06ad33 --- /dev/null +++ b/dto/sources.go @@ -0,0 +1,26 @@ +package dto + +import ( + "github.com/jtom38/newsbot/collector/database" + "github.com/jtom38/newsbot/collector/domain/models" +) + +func (c DtoClient) ConvertToSourceDto(i database.Source) models.SourceDto { + var deleted bool + if !i.Deleted.Valid { + deleted = true + } + + return models.SourceDto{ + ID: i.ID, + Site: i.Site, + Name: i.Name, + Source: i.Source, + Type: i.Type, + Value: i.Value.String, + Enabled: i.Enabled, + Url: i.Url, + Tags: c.SplitTags(i.Tags), + Deleted: deleted, + } +} diff --git a/main.go b/main.go index bdabf89..d5c35a0 100644 --- a/main.go +++ b/main.go @@ -2,9 +2,11 @@ package main import ( "context" + "database/sql" "fmt" "net/http" + "github.com/jtom38/newsbot/collector/database" "github.com/jtom38/newsbot/collector/docs" "github.com/jtom38/newsbot/collector/routes" "github.com/jtom38/newsbot/collector/services/config" @@ -20,16 +22,23 @@ func main() { docs.SwaggerInfo.Host = fmt.Sprintf("%v:8081", address) ctx := context.Background() + db, err := sql.Open("postgres", cfg.GetConfig(config.Sql_Connection_String)) + if err != nil { + panic(err) + } + + queries := database.New(db) + c := cron.New(ctx) c.Start() - server := routes.NewServer(ctx) + server := routes.NewServer(ctx, queries) fmt.Println("API is online and waiting for requests.") fmt.Printf("API: http://%v:8081/api\r\n", address) fmt.Printf("Swagger: http://%v:8081/swagger/index.html\r\n", address) - err := http.ListenAndServe(":8081", server.Router) + err = http.ListenAndServe(":8081", server.Router) if err != nil { panic(err) } diff --git a/routes/articles.go b/routes/articles.go index 00e3f2c..c7accd6 100644 --- a/routes/articles.go +++ b/routes/articles.go @@ -6,61 +6,156 @@ import ( "github.com/go-chi/chi/v5" "github.com/google/uuid" + "github.com/jtom38/newsbot/collector/domain/models" ) +func (s *Server) 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.GetArticlesBySourceId) + + return r +} + +type ArticlesListResults struct { + ApiStatusModel + Payload []models.ArticleDto `json:"payload"` +} + // ListArticles // @Summary Lists the top 50 records // @Produce application/json // @Tags Articles // @Router /articles [get] +// @Success 200 {object} ArticlesListResults "OK" func (s *Server) listArticles(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", ApplicationJson) - - res, err := s.Db.ListArticlesByDate(*s.ctx, 50) - if err != nil { - w.Write([]byte(err.Error())) - panic(err) + p := ArticlesListResults{ + ApiStatusModel: ApiStatusModel{ + Message: "OK", + StatusCode: http.StatusOK, + }, } - bres, err := json.Marshal(res) + w.Header().Set(HeaderContentType, ApplicationJson) + + res, err := s.dto.ListArticles(r.Context(), 50) if err != nil { - w.Write([]byte(err.Error())) - panic(err) + s.WriteError(w, err.Error(), http.StatusInternalServerError) + return + } + + p.Payload = res + + bres, err := json.Marshal(p) + if err != nil { + s.WriteError(w, err.Error(), http.StatusInternalServerError) + return } w.Write(bres) } -// GetArticleById +type ArticleGetResults struct { + ApiStatusModel + Payload models.ArticleDto `json:"payload"` +} + +// GetArticle // @Summary Returns an article based on defined ID. -// @Param id path string true "uuid" +// @Param ID path string true "uuid" // @Produce application/json // @Tags Articles // @Router /articles/{ID} [get] -func (s *Server) getArticleById(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") +// @Success 200 {object} ArticleGetResults "OK" +func (s *Server) getArticle(w http.ResponseWriter, r *http.Request) { + p := ArticleGetResults { + ApiStatusModel: ApiStatusModel{ + Message: "OK", + StatusCode: http.StatusOK, + }, + } + + w.Header().Set(HeaderContentType, ApplicationJson) id := chi.URLParam(r, "ID") uuid, err := uuid.Parse(id) if err != nil { - w.Write([]byte(err.Error())) - panic(err) + s.WriteError(w, err.Error(), http.StatusBadRequest) + return } - res, err := s.Db.GetArticleByID(*s.ctx, uuid) + res, err := s.dto.GetArticle(r.Context(), uuid) if err != nil { - w.Write([]byte(err.Error())) - panic(err) + s.WriteError(w, err.Error(), http.StatusInternalServerError) + return } - bres, err := json.Marshal(res) + p.Payload = res + + bres, err := json.Marshal(p) if err != nil { - w.Write([]byte(err.Error())) - panic(err) + s.WriteError(w, err.Error(), http.StatusInternalServerError) + return } w.Write(bres) } +type ArticleDetailsResult struct { + ApiStatusModel + Payload models.ArticleDetailsDto `json:"payload"` +} + +// GetArticleDetails +// @Summary Returns an article and source based on defined ID. +// @Param ID path string true "uuid" +// @Produce application/json +// @Tags Articles +// @Router /articles/{ID}/details [get] +// @Success 200 {object} ArticleDetailsResult "OK" +func (s *Server) getArticleDetails(w http.ResponseWriter, r *http.Request) { + p := ArticleDetailsResult { + ApiStatusModel: ApiStatusModel{ + Message: "OK", + StatusCode: http.StatusOK, + }, + } + + w.Header().Set(HeaderContentType, ApplicationJson) + + id := chi.URLParam(r, "ID") + uuid, err := uuid.Parse(id) + if err != nil { + s.WriteError(w, err.Error(), http.StatusBadRequest) + return + } + + res, err := s.dto.GetArticleDetails(r.Context(), uuid) + if err != nil { + s.WriteError(w, err.Error(), http.StatusInternalServerError) + return + } + + p.Payload = res + + bres, err := json.Marshal(p) + if err != nil { + s.WriteError(w, err.Error(), http.StatusInternalServerError) + return + } + + w.Write(bres) +} + +type ArticlesBySourceIDResults struct { + ApiStatusModel + Payload []models.ArticleDto `json:"payload"` +} + // TODO add page support // GetArticlesBySourceID // @Summary Finds the articles based on the SourceID provided. Returns the top 50. @@ -68,6 +163,7 @@ func (s *Server) getArticleById(w http.ResponseWriter, r *http.Request) { // @Produce application/json // @Tags Articles // @Router /articles/by/sourceid [get] +// @Success 200 {object} ArticlesListResults "OK" func (s *Server) GetArticlesBySourceId(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") @@ -77,56 +173,20 @@ func (s *Server) GetArticlesBySourceId(w http.ResponseWriter, r *http.Request) { uuid, err := uuid.Parse(_id) if err != nil { - w.Write([]byte(err.Error())) - panic(err) + s.WriteError(w, err.Error(), http.StatusBadRequest) + return } - res, err := s.Db.GetNewArticlesBySourceId(*s.ctx, uuid) - //res, err := s.Db.GetArticlesBySourceId(*s.ctx, uuid) + res, err := s.dto.GetArticlesBySourceId(r.Context(), uuid) if err != nil { - w.Write([]byte(err.Error())) - panic(err) + s.WriteError(w, err.Error(), http.StatusInternalServerError) + return } bres, err := json.Marshal(res) if err != nil { - w.Write([]byte(err.Error())) - panic(err) - } - - w.Write(bres) -} - -// TODO add page support -// GetArticlesByTag -// @Summary Finds the articles based on the SourceID provided. Returns the top 50. -// @Param tag query string true "Tag name" -// @Produce application/json -// @Tags Articles -// @Router /articles/by/tag [get] -func (s *Server) GetArticlesByTag(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - - r.URL.Query() - query := r.URL.Query() - _id := query["tag"][0] - - uuid, err := uuid.Parse(_id) - if err != nil { - w.Write([]byte(err.Error())) - panic(err) - } - - res, err := s.Db.GetArticlesBySourceId(*s.ctx, uuid) - if err != nil { - w.Write([]byte(err.Error())) - panic(err) - } - - bres, err := json.Marshal(res) - if err != nil { - w.Write([]byte(err.Error())) - panic(err) + s.WriteError(w, err.Error(), http.StatusInternalServerError) + return } w.Write(bres) diff --git a/routes/server.go b/routes/server.go index 01a874f..dcfa23c 100644 --- a/routes/server.go +++ b/routes/server.go @@ -6,20 +6,20 @@ import ( "encoding/json" "net/http" - //"net/http" - "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" _ "github.com/lib/pq" httpSwagger "github.com/swaggo/http-swagger" "github.com/jtom38/newsbot/collector/database" + "github.com/jtom38/newsbot/collector/dto" "github.com/jtom38/newsbot/collector/services/config" ) type Server struct { Router *chi.Mux Db *database.Queries + dto dto.DtoClient ctx *context.Context } @@ -36,16 +36,18 @@ var ( ErrUnableToConvertToJson string = "Unable to convert to json" ) -func NewServer(ctx context.Context) *Server { +func NewServer(ctx context.Context, db *database.Queries) *Server { s := &Server{ ctx: &ctx, + Db: db, + dto: dto.NewDtoClient(db), } - db, err := openDatabase(ctx) - if err != nil { - panic(err) - } - s.Db = db + //db, err := openDatabase(ctx) + //if err != nil { + // panic(err) + //} + //s.Db = db s.Router = chi.NewRouter() s.MountMiddleware() @@ -76,14 +78,7 @@ func (s *Server) MountRoutes() { httpSwagger.URL("doc.json"), //The url pointing to API definition )) - /* Article Routes */ - s.Router.Get("/api/articles", s.listArticles) - s.Router.Route("/api/articles/{ID}", func(r chi.Router) { - r.Get("/", s.getArticleById) - }) - s.Router.Get("/api/articles/by/sourceid", s.GetArticlesBySourceId) - - /* Queue */ + s.Router.Mount("/api/articles", s.GetArticleRouter()) s.Router.Mount("/api/queue", s.GetQueueRouter()) /* Discord WebHooks */