Features/delete source and first dto (#36)

* updated db, added dto for ListSources, and added delete source

* updated from model > models

* updated to models

* sources now sends back a standard message

* updated subscription routes to have beter logid and swagger details

* moved the dto objects back to modles given they are not bound to the database

* cleaned up how we return the error

* cleaned up swag and updated models to take from the base apistatusmodel. less human errors this way

* cleaned up swag and updated models

* swag updated

* updated queue to return a router and also renamed it as it will hold all queue info later on

* removed config tag

* added subscription details route

* article routes have been moved to support dto

* updated discordwebhooks to use dto

* updated discordwebhookqueue to return details on the items via dto

* removed the example routes

* updated sources to use dto

* subscriptions moved to dto

* generated swag
This commit is contained in:
James Tombleson 2023-01-22 10:12:55 -08:00 committed by GitHub
parent a2147294d5
commit ada453e08a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 3423 additions and 1351 deletions

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT. // Code generated by sqlc. DO NOT EDIT.
// versions: // versions:
// sqlc v1.13.0 // sqlc v1.16.0
package database package database

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT. // Code generated by sqlc. DO NOT EDIT.
// versions: // versions:
// sqlc v1.13.0 // sqlc v1.16.0
package database package database

View File

@ -1,6 +1,6 @@
// Code generated by sqlc. DO NOT EDIT. // Code generated by sqlc. DO NOT EDIT.
// versions: // versions:
// sqlc v1.13.0 // sqlc v1.16.0
// source: query.sql // source: query.sql
package database package database

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,239 @@
basePath: /api 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.DiscordQueueDetailsDto:
properties:
article:
$ref: '#/definitions/models.ArticleDetailsDto'
id:
type: string
type: object
models.DiscordWebHooksDto:
properties:
ID:
type: string
channel:
type: string
enabled:
type: boolean
server:
type: string
url:
type: string
type: object
models.SourceDto:
properties:
deleted:
type: boolean
enabled:
type: boolean
id:
type: string
name:
type: string
site:
type: string
source:
type: string
tags:
items:
type: string
type: array
type:
type: string
url:
type: string
value:
type: string
type: object
models.SubscriptionDetailsDto:
properties:
discordwebhook:
$ref: '#/definitions/models.DiscordWebHooksDto'
id:
type: string
source:
$ref: '#/definitions/models.SourceDto'
type: object
models.SubscriptionDto:
properties:
discordwebhookid:
type: string
id:
type: string
sourceid:
type: string
type: object
routes.ApiError:
properties:
message:
type: string
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.GetDiscordWebhook:
properties:
message:
type: string
payload:
$ref: '#/definitions/models.DiscordWebHooksDto'
status:
type: integer
type: object
routes.GetSource:
properties:
message:
type: string
payload:
$ref: '#/definitions/models.SourceDto'
status:
type: integer
type: object
routes.ListDiscordWebHooksQueueResults:
properties:
message:
type: string
payload:
items:
$ref: '#/definitions/models.DiscordQueueDetailsDto'
type: array
status:
type: integer
type: object
routes.ListDiscordWebhooks:
properties:
message:
type: string
payload:
items:
$ref: '#/definitions/models.DiscordWebHooksDto'
type: array
status:
type: integer
type: object
routes.ListSources:
properties:
message:
type: string
payload:
items:
$ref: '#/definitions/models.SourceDto'
type: array
status:
type: integer
type: object
routes.ListSubscriptionDetails:
properties:
message:
type: string
payload:
items:
$ref: '#/definitions/models.SubscriptionDetailsDto'
type: array
status:
type: integer
type: object
routes.ListSubscriptions:
properties:
message:
type: string
payload:
items:
$ref: '#/definitions/models.SubscriptionDto'
type: array
status:
type: integer
type: object
info: info:
contact: {} contact: {}
title: NewsBot collector title: NewsBot collector
@ -8,7 +243,11 @@ paths:
get: get:
produces: produces:
- application/json - application/json
responses: {} responses:
"200":
description: OK
schema:
$ref: '#/definitions/routes.ArticlesListResults'
summary: Lists the top 50 records summary: Lists the top 50 records
tags: tags:
- Articles - Articles
@ -17,15 +256,37 @@ paths:
parameters: parameters:
- description: uuid - description: uuid
in: path in: path
name: id name: ID
required: true required: true
type: string type: string
produces: produces:
- application/json - application/json
responses: {} responses:
"200":
description: OK
schema:
$ref: '#/definitions/routes.ArticleGetResults'
summary: Returns an article based on defined ID. summary: Returns an article based on defined ID.
tags: tags:
- Articles - 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: /articles/by/sourceid:
get: get:
parameters: parameters:
@ -36,184 +297,15 @@ paths:
type: string type: string
produces: produces:
- application/json - application/json
responses: {} responses:
"200":
description: OK
schema:
$ref: '#/definitions/routes.ArticlesListResults'
summary: Finds the articles based on the SourceID provided. Returns the top summary: Finds the articles based on the SourceID provided. Returns the top
50. 50.
tags: tags:
- Articles - Articles
/articles/by/tag:
get:
parameters:
- description: Tag name
in: query
name: tag
required: true
type: string
produces:
- application/json
responses: {}
summary: Finds the articles based on the SourceID provided. Returns the top
50.
tags:
- Articles
/config/sources:
get:
produces:
- application/json
responses: {}
summary: Lists the top 50 records
tags:
- Config
- Source
/config/sources/{id}:
get:
parameters:
- description: uuid
in: path
name: id
required: true
type: string
produces:
- application/json
responses: {}
summary: Returns a single entity by ID
tags:
- Config
- Source
post:
parameters:
- description: id
in: path
name: id
required: true
type: string
responses: {}
summary: Marks a source as deleted based on its ID value.
tags:
- Source
/config/sources/{id}/disable:
post:
parameters:
- description: id
in: path
name: id
required: true
type: string
responses: {}
summary: Disables a source from processing.
tags:
- Config
- Source
/config/sources/{id}/enable:
post:
parameters:
- description: id
in: path
name: id
required: true
type: string
responses: {}
summary: Enables a source to continue processing.
tags:
- Config
- Source
/config/sources/by/source:
get:
parameters:
- description: Source Name
in: query
name: source
required: true
type: string
produces:
- application/json
responses: {}
summary: 'Lists the top 50 records based on the name given. Example: reddit'
tags:
- Config
- Source
/config/sources/by/sourceAndName:
get:
parameters:
- description: dadjokes
in: query
name: name
required: true
type: string
- description: reddit
in: query
name: source
required: true
type: string
produces:
- application/json
responses: {}
summary: Returns a single entity by ID
tags:
- Config
- Source
/config/sources/new/reddit:
post:
parameters:
- description: name
in: query
name: name
required: true
type: string
- description: url
in: query
name: url
required: true
type: string
responses: {}
summary: Creates a new reddit source to monitor.
tags:
- Config
- Source
- Reddit
/config/sources/new/twitch:
post:
parameters:
- description: name
in: query
name: name
required: true
type: string
responses: {}
summary: Creates a new twitch source to monitor.
tags:
- Config
- Source
- Twitch
/config/sources/new/youtube:
post:
parameters:
- description: name
in: query
name: name
required: true
type: string
- description: url
in: query
name: url
required: true
type: string
responses: {}
summary: Creates a new youtube source to monitor.
tags:
- Config
- Source
- YouTube
/discord/queue:
get:
produces:
- application/json
responses: {}
summary: Returns the top 100 entries from the queue to be processed.
tags:
- Debug
- Discord
- Queue
/discord/webhooks: /discord/webhooks:
get: get:
produces: produces:
@ -221,10 +313,9 @@ paths:
responses: {} responses: {}
summary: Returns the top 100 entries from the queue to be processed. summary: Returns the top 100 entries from the queue to be processed.
tags: tags:
- Config
- Discord - Discord
- Webhook - Webhook
/discord/webhooks/{id}: /discord/webhooks/{ID}:
delete: delete:
parameters: parameters:
- description: id - description: id
@ -235,9 +326,35 @@ paths:
responses: {} responses: {}
summary: Deletes a record by ID. summary: Deletes a record by ID.
tags: tags:
- Config
- Discord - Discord
- Webhook - Webhook
/discord/webhooks/{ID}/disable:
post:
parameters:
- description: id
in: path
name: id
required: true
type: string
responses: {}
summary: Disables a Webhook from being used.
tags:
- Discord
- Webhook
/discord/webhooks/{ID}/enable:
post:
parameters:
- description: id
in: path
name: id
required: true
type: string
responses: {}
summary: Enables a source to continue processing.
tags:
- Discord
- Webhook
/discord/webhooks/{id}:
get: get:
parameters: parameters:
- description: id - description: id
@ -247,10 +364,13 @@ paths:
type: string type: string
produces: produces:
- application/json - application/json
responses: {} responses:
"200":
description: OK
schema:
$ref: '#/definitions/routes.GetDiscordWebhook'
summary: Returns the top 100 entries from the queue to be processed. summary: Returns the top 100 entries from the queue to be processed.
tags: tags:
- Config
- Discord - Discord
- Webhook - Webhook
patch: patch:
@ -263,35 +383,6 @@ paths:
responses: {} responses: {}
summary: Updates a valid discord webhook ID based on the body given. summary: Updates a valid discord webhook ID based on the body given.
tags: tags:
- Config
- Discord
- Webhook
/discord/webhooks/{id}/disable:
post:
parameters:
- description: id
in: path
name: id
required: true
type: string
responses: {}
summary: Disables a Webhook from being used.
tags:
- Config
- Discord
- Webhook
/discord/webhooks/{id}/enable:
post:
parameters:
- description: id
in: path
name: id
required: true
type: string
responses: {}
summary: Enables a source to continue processing.
tags:
- Config
- Discord - Discord
- Webhook - Webhook
/discord/webhooks/by/serverAndChannel: /discord/webhooks/by/serverAndChannel:
@ -309,10 +400,13 @@ paths:
type: string type: string
produces: produces:
- application/json - application/json
responses: {} responses:
"200":
description: OK
schema:
$ref: '#/definitions/routes.ListDiscordWebhooks'
summary: Returns all the known web hooks based on the Server and Channel given. summary: Returns all the known web hooks based on the Server and Channel given.
tags: tags:
- Config
- Discord - Discord
- Webhook - Webhook
/discord/webhooks/new: /discord/webhooks/new:
@ -336,62 +430,229 @@ paths:
responses: {} responses: {}
summary: Creates a new record for a discord web hook to post data to. summary: Creates a new record for a discord web hook to post data to.
tags: tags:
- Config
- Discord - Discord
- Webhook - Webhook
/hello/{who}: /queue/discord/webhooks:
get:
produces:
- application/json
responses:
"200":
description: ok
schema:
$ref: '#/definitions/routes.ListDiscordWebHooksQueueResults'
summary: Returns the top 100 entries from the queue to be processed.
tags:
- Queue
/sources:
get:
produces:
- application/json
responses:
"200":
description: ok
schema:
$ref: '#/definitions/routes.ListSources'
"400":
description: Unable to reach SQL or Data problems
schema:
$ref: '#/definitions/routes.ApiError'
summary: Lists the top 50 records
tags:
- Source
/sources/{id}:
get: get:
parameters: parameters:
- description: Who - description: uuid
in: path in: path
name: who name: id
required: true
type: string
produces:
- text/plain
responses: {}
summary: Responds back with "Hello x" depending on param passed in.
tags:
- Debug
/helloworld:
get:
produces:
- text/plain
responses: {}
summary: Responds back with "Hello world!"
tags:
- Debug
/ping:
get:
produces:
- text/plain
responses: {}
summary: Sends back "pong". Good to test with.
tags:
- Debug
/settings/{key}:
get:
parameters:
- description: Settings Key value
in: path
name: key
required: true required: true
type: string type: string
produces: produces:
- application/json - application/json
responses: {} responses:
summary: Returns a object based on the Key that was given. "200":
description: ok
schema:
$ref: '#/definitions/routes.GetSource'
"204":
description: No record found.
schema:
$ref: '#/definitions/routes.ApiError'
"400":
description: Unable to query SQL.
schema:
$ref: '#/definitions/routes.ApiError'
"500":
description: Failed to process data from SQL.
schema:
$ref: '#/definitions/routes.ApiError'
summary: Returns a single entity by ID
tags: tags:
- Settings - Source
post:
parameters:
- description: id
in: path
name: id
required: true
type: string
responses: {}
summary: Marks a source as deleted based on its ID value.
tags:
- Source
/sources/{id}/disable:
post:
parameters:
- description: id
in: path
name: id
required: true
type: string
responses: {}
summary: Disables a source from processing.
tags:
- Source
/sources/{id}/enable:
post:
parameters:
- description: id
in: path
name: id
required: true
type: string
responses: {}
summary: Enables a source to continue processing.
tags:
- Source
/sources/by/source:
get:
parameters:
- description: Source Name
in: query
name: source
required: true
type: string
produces:
- application/json
responses:
"200":
description: ok
schema:
$ref: '#/definitions/routes.ListSources'
"400":
description: Unable to query SQL.
schema:
$ref: '#/definitions/routes.ApiError'
"500":
description: Problems with data.
schema:
$ref: '#/definitions/routes.ApiError'
summary: 'Lists the top 50 records based on the name given. Example: reddit'
tags:
- Source
/sources/by/sourceAndName:
get:
parameters:
- description: dadjokes
in: query
name: name
required: true
type: string
- description: reddit
in: query
name: source
required: true
type: string
produces:
- application/json
responses:
"200":
description: ok
schema:
$ref: '#/definitions/routes.GetSource'
"204":
description: No record found.
schema:
$ref: '#/definitions/routes.ApiError'
"400":
description: Unable to query SQL.
schema:
$ref: '#/definitions/routes.ApiError'
"500":
description: Failed to process data from SQL.
schema:
$ref: '#/definitions/routes.ApiError'
summary: Returns a single entity by ID
tags:
- Source
/sources/new/reddit:
post:
parameters:
- description: name
in: query
name: name
required: true
type: string
- description: url
in: query
name: url
required: true
type: string
responses: {}
summary: Creates a new reddit source to monitor.
tags:
- Source
/sources/new/twitch:
post:
parameters:
- description: name
in: query
name: name
required: true
type: string
responses: {}
summary: Creates a new twitch source to monitor.
tags:
- Source
/sources/new/youtube:
post:
parameters:
- description: name
in: query
name: name
required: true
type: string
- description: url
in: query
name: url
required: true
type: string
responses: {}
summary: Creates a new youtube source to monitor.
tags:
- Source
/subscriptions: /subscriptions:
get: get:
produces: produces:
- application/json - application/json
responses: {} responses:
"200":
description: ok
schema:
$ref: '#/definitions/routes.ListSubscriptions'
"400":
description: Unable to reach SQL.
schema:
$ref: '#/definitions/routes.ApiError'
"500":
description: Failed to process data from SQL.
schema:
$ref: '#/definitions/routes.ApiError'
summary: Returns the top 100 entries from the queue to be processed. summary: Returns the top 100 entries from the queue to be processed.
tags: tags:
- Subscription - Subscription
/subscriptions/byDiscordId: /subscriptions/by/SourceId:
get: get:
parameters: parameters:
- description: id - description: id
@ -401,11 +662,15 @@ paths:
type: string type: string
produces: produces:
- application/json - application/json
responses: {} responses:
"200":
description: ok
schema:
$ref: '#/definitions/routes.ListSubscriptions'
summary: Returns the top 100 entries from the queue to be processed. summary: Returns the top 100 entries from the queue to be processed.
tags: tags:
- Subscription - Subscription
/subscriptions/bySourceId: /subscriptions/by/discordId:
get: get:
parameters: parameters:
- description: id - description: id
@ -415,26 +680,47 @@ paths:
type: string type: string
produces: produces:
- application/json - application/json
responses: {} responses:
"200":
description: ok
schema:
$ref: '#/definitions/routes.ListSubscriptions'
"400":
description: Unable to reach SQL or Data problems
schema:
$ref: '#/definitions/routes.ApiError'
"500":
description: Data problems
schema:
$ref: '#/definitions/routes.ApiError'
summary: Returns the top 100 entries from the queue to be processed. summary: Returns the top 100 entries from the queue to be processed.
tags: tags:
- Subscription - Subscription
/subscriptions/details:
get:
produces:
- application/json
responses:
"200":
description: ok
schema:
$ref: '#/definitions/routes.ListSubscriptionDetails'
summary: Returns the top 50 entries with full deatils on the source and output.
tags:
- Subscription
/subscriptions/discord/webhook/delete: /subscriptions/discord/webhook/delete:
delete: delete:
parameters: parameters:
- description: Id - description: id
in: query in: query
name: Id name: id
required: true required: true
type: string type: string
responses: {} responses: {}
summary: Removes a Discord WebHook Subscription based on the Subscription ID. summary: Removes a Discord WebHook Subscription based on the Subscription ID.
tags: tags:
- Config
- Source
- Discord
- Subscription - Subscription
/subscriptions/new/discordwebhook: /subscriptions/discord/webhook/new:
post: post:
parameters: parameters:
- description: discordWebHookId - description: discordWebHookId

View File

@ -1,4 +1,4 @@
package model package models
import ( import (
"time" "time"

View File

@ -1,4 +1,4 @@
package model package models
import ( import (
"time" "time"

129
domain/models/dto.go Normal file
View File

@ -0,0 +1,129 @@
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"`
Server string `json:"server"`
Channel string `json:"channel"`
Enabled bool `json:"enabled"`
}
func ConvertToDiscordWebhookDto(i database.Discordwebhook) DiscordWebHooksDto {
return DiscordWebHooksDto{
ID: i.ID,
Url: i.Url,
Server: i.Server,
Channel: i.Channel,
Enabled: i.Enabled,
}
}
type SourceDto struct {
ID uuid.UUID `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"`
Deleted bool `json:"deleted"`
}
func ConvertToSourceDto(i database.Source) SourceDto {
var deleted bool
if !i.Deleted.Valid {
deleted = true
}
return 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: splitTags(i.Tags),
Deleted: deleted,
}
}
type DiscordQueueDto struct {
ID uuid.UUID `json:"id"`
Articleid uuid.UUID `json:"articleId"`
}
type DiscordQueueDetailsDto struct {
ID uuid.UUID `json:"id"`
Article ArticleDetailsDto `json:"article"`
}
type SubscriptionDto struct {
ID uuid.UUID `json:"id"`
DiscordWebhookId uuid.UUID `json:"discordwebhookid"`
SourceId uuid.UUID `json:"sourceid"`
}
func ConvertToSubscriptionDto(i database.Subscription) SubscriptionDto {
c := SubscriptionDto{
ID: i.ID,
DiscordWebhookId: i.Discordwebhookid,
SourceId: i.Sourceid,
}
return c
}
type SubscriptionDetailsDto struct {
ID uuid.UUID `json:"id"`
Source SourceDto `json:"source"`
DiscordWebHook DiscordWebHooksDto `json:"discordwebhook"`
}
func splitTags(t string) []string {
items := strings.Split(t, ", ")
return items
}

View File

@ -1,4 +1,4 @@
package model package models
// This is the root Json object. It does not contain data that we care about though. // This is the root Json object. It does not contain data that we care about though.
type RedditJsonContent struct { type RedditJsonContent struct {

115
dto/articles.go Normal file
View File

@ -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.ConvertToSource(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, ", ")
}

63
dto/discordwebhooks.go Normal file
View File

@ -0,0 +1,63 @@
package dto
import (
"context"
"github.com/google/uuid"
"github.com/jtom38/newsbot/collector/database"
"github.com/jtom38/newsbot/collector/domain/models"
)
func (c DtoClient) ListDiscordWebHooks(ctx context.Context, total int32) ([]models.DiscordWebHooksDto, error) {
var res []models.DiscordWebHooksDto
items, err := c.db.ListDiscordWebhooks(ctx, total)
if err != nil {
return res, nil
}
for _, item := range items {
res = append(res, c.ConvertDiscordWebhook(item))
}
return res, nil
}
func (c DtoClient) GetDiscordWebhook(ctx context.Context, id uuid.UUID) (models.DiscordWebHooksDto, error) {
var res models.DiscordWebHooksDto
item, err := c.db.GetDiscordWebHooksByID(ctx, id)
if err != nil {
return res, err
}
return c.ConvertDiscordWebhook(item), nil
}
func (c DtoClient) GetDiscordWebHookByServerAndChannel(ctx context.Context, server, channel string) ([]models.DiscordWebHooksDto, error) {
var res []models.DiscordWebHooksDto
items, err := c.db.GetDiscordWebHooksByServerAndChannel(ctx, database.GetDiscordWebHooksByServerAndChannelParams{
Server: server,
Channel: channel,
})
if err != nil {
return res, err
}
for _, item := range items {
res = append(res, c.ConvertDiscordWebhook(item))
}
return res, nil
}
func (c DtoClient) ConvertDiscordWebhook(i database.Discordwebhook) models.DiscordWebHooksDto {
return models.DiscordWebHooksDto{
ID: i.ID,
Url: i.Url,
Server: i.Server,
Channel: i.Channel,
Enabled: i.Enabled,
}
}

42
dto/queue.go Normal file
View File

@ -0,0 +1,42 @@
package dto
import (
"context"
"github.com/jtom38/newsbot/collector/database"
"github.com/jtom38/newsbot/collector/domain/models"
)
func (c DtoClient) ListDiscordWebhookQueue(ctx context.Context, limit int32) {
}
func (c DtoClient) ListDiscordWebhookQueueDetails(ctx context.Context, limit int32) ([]models.DiscordQueueDetailsDto, error) {
var res []models.DiscordQueueDetailsDto
items, err := c.db.ListDiscordQueueItems(ctx, limit)
if err != nil {
return res, err
}
for _, item := range items {
article, err := c.GetArticleDetails(ctx, item.ID)
if err != nil {
return res, err
}
res = append(res, models.DiscordQueueDetailsDto{
ID: item.ID,
Article: article,
})
}
return res, nil
}
func (c DtoClient) ConvertToDiscordQueueDto(i database.Discordqueue) models.DiscordQueueDto {
return models.DiscordQueueDto{
ID: i.ID,
Articleid: i.Articleid,
}
}

85
dto/sources.go Normal file
View File

@ -0,0 +1,85 @@
package dto
import (
"context"
"strings"
"github.com/google/uuid"
"github.com/jtom38/newsbot/collector/database"
"github.com/jtom38/newsbot/collector/domain/models"
)
func (c DtoClient) ListSources(ctx context.Context, limit int32) ([]models.SourceDto, error) {
var res []models.SourceDto
items, err := c.db.ListSources(ctx, limit)
if err != nil {
return res, err
}
for _, item := range items {
res = append(res, c.ConvertToSource(item))
}
return res, nil
}
func (c DtoClient) ListSourcesBySource(ctx context.Context, sourceName string) ([]models.SourceDto, error) {
var res []models.SourceDto
items, err := c.db.ListSourcesBySource(ctx, strings.ToLower(sourceName))
if err != nil {
return res, err
}
for _, item := range items {
res = append(res, c.ConvertToSource(item))
}
return res, nil
}
func (c DtoClient) GetSourceById(ctx context.Context, id uuid.UUID) (models.SourceDto, error) {
var res models.SourceDto
item, err := c.db.GetSourceByID(ctx, id)
if err != nil {
return res, err
}
return c.ConvertToSource(item), nil
}
func (c DtoClient) GetSourceByNameAndSource(ctx context.Context, name, source string) (models.SourceDto, error) {
var res models.SourceDto
item, err := c.db.GetSourceByNameAndSource(ctx, database.GetSourceByNameAndSourceParams{
Name: name,
Source: source,
})
if err != nil {
return res, err
}
return c.ConvertToSource(item), nil
}
func (c DtoClient) ConvertToSource(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,
}
}

91
dto/subscriptions.go Normal file
View File

@ -0,0 +1,91 @@
package dto
import (
"context"
"github.com/google/uuid"
"github.com/jtom38/newsbot/collector/database"
"github.com/jtom38/newsbot/collector/domain/models"
)
func (c DtoClient) ListSubscriptions(ctx context.Context, limit int32) ([]models.SubscriptionDto, error) {
var res []models.SubscriptionDto
items, err := c.db.ListSubscriptions(ctx, limit)
if err != nil {
return res, err
}
for _, item := range items {
res = append(res, c.ConvertSubscription(item))
}
return res, nil
}
func (c DtoClient) ListSubscriptionDetails(ctx context.Context, limit int32) ([]models.SubscriptionDetailsDto, error) {
var res []models.SubscriptionDetailsDto
items, err := c.ListSubscriptions(ctx, limit)
if err != nil {
return res, err
}
for _, item := range items {
dwh, err := c.GetDiscordWebhook(ctx, item.DiscordWebhookId)
if err != nil {
return res, err
}
source, err := c.GetSourceById(ctx, item.SourceId)
if err != nil {
return res, err
}
res = append(res, models.SubscriptionDetailsDto{
ID: item.ID,
Source: source,
DiscordWebHook: dwh,
})
}
return res, nil
}
func (c DtoClient) ListSubscriptionsByDiscordWebhookId(ctx context.Context, id uuid.UUID) ([]models.SubscriptionDto, error) {
var res []models.SubscriptionDto
items, err := c.db.GetSubscriptionsByDiscordWebHookId(ctx, id)
if err != nil {
return res, err
}
for _, item := range items {
res = append(res, c.ConvertSubscription(item))
}
return res, nil
}
func (c DtoClient) ListSubscriptionsBySourceId(ctx context.Context, id uuid.UUID) ([]models.SubscriptionDto, error) {
var res []models.SubscriptionDto
items, err := c.db.GetSubscriptionsBySourceID(ctx, id)
if err != nil {
return res, err
}
for _, item := range items {
res = append(res, c.ConvertSubscription(item))
}
return res, nil
}
func (c DtoClient) ConvertSubscription(i database.Subscription) models.SubscriptionDto {
return models.SubscriptionDto{
ID: i.ID,
DiscordWebhookId: i.Discordwebhookid,
SourceId: i.Sourceid,
}
}

13
main.go
View File

@ -2,9 +2,11 @@ package main
import ( import (
"context" "context"
"database/sql"
"fmt" "fmt"
"net/http" "net/http"
"github.com/jtom38/newsbot/collector/database"
"github.com/jtom38/newsbot/collector/docs" "github.com/jtom38/newsbot/collector/docs"
"github.com/jtom38/newsbot/collector/routes" "github.com/jtom38/newsbot/collector/routes"
"github.com/jtom38/newsbot/collector/services/config" "github.com/jtom38/newsbot/collector/services/config"
@ -20,16 +22,23 @@ func main() {
docs.SwaggerInfo.Host = fmt.Sprintf("%v:8081", address) docs.SwaggerInfo.Host = fmt.Sprintf("%v:8081", address)
ctx := context.Background() 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 := cron.New(ctx)
c.Start() c.Start()
server := routes.NewServer(ctx) server := routes.NewServer(ctx, queries)
fmt.Println("API is online and waiting for requests.") fmt.Println("API is online and waiting for requests.")
fmt.Printf("API: http://%v:8081/api\r\n", address) fmt.Printf("API: http://%v:8081/api\r\n", address)
fmt.Printf("Swagger: http://%v:8081/swagger/index.html\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 { if err != nil {
panic(err) panic(err)
} }

View File

@ -6,61 +6,156 @@ import (
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"github.com/google/uuid" "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 // ListArticles
// @Summary Lists the top 50 records // @Summary Lists the top 50 records
// @Produce application/json // @Produce application/json
// @Tags Articles // @Tags Articles
// @Router /articles [get] // @Router /articles [get]
// @Success 200 {object} ArticlesListResults "OK"
func (s *Server) listArticles(w http.ResponseWriter, r *http.Request) { func (s *Server) listArticles(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") p := ArticlesListResults{
ApiStatusModel: ApiStatusModel{
res, err := s.Db.ListArticlesByDate(*s.ctx, 50) Message: "OK",
if err != nil { StatusCode: http.StatusOK,
w.Write([]byte(err.Error())) },
panic(err)
} }
bres, err := json.Marshal(res) w.Header().Set(HeaderContentType, ApplicationJson)
res, err := s.dto.ListArticles(r.Context(), 50)
if err != nil { if err != nil {
w.Write([]byte(err.Error())) s.WriteError(w, err.Error(), http.StatusInternalServerError)
panic(err) return
}
p.Payload = res
bres, err := json.Marshal(p)
if err != nil {
s.WriteError(w, err.Error(), http.StatusInternalServerError)
return
} }
w.Write(bres) w.Write(bres)
} }
// GetArticleById type ArticleGetResults struct {
ApiStatusModel
Payload models.ArticleDto `json:"payload"`
}
// GetArticle
// @Summary Returns an article based on defined ID. // @Summary Returns an article based on defined ID.
// @Param id path string true "uuid" // @Param ID path string true "uuid"
// @Produce application/json // @Produce application/json
// @Tags Articles // @Tags Articles
// @Router /articles/{ID} [get] // @Router /articles/{ID} [get]
func (s *Server) getArticleById(w http.ResponseWriter, r *http.Request) { // @Success 200 {object} ArticleGetResults "OK"
w.Header().Set("Content-Type", "application/json") 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") id := chi.URLParam(r, "ID")
uuid, err := uuid.Parse(id) uuid, err := uuid.Parse(id)
if err != nil { if err != nil {
w.Write([]byte(err.Error())) s.WriteError(w, err.Error(), http.StatusBadRequest)
panic(err) return
} }
res, err := s.Db.GetArticleByID(*s.ctx, uuid) res, err := s.dto.GetArticle(r.Context(), uuid)
if err != nil { if err != nil {
w.Write([]byte(err.Error())) s.WriteError(w, err.Error(), http.StatusInternalServerError)
panic(err) return
} }
bres, err := json.Marshal(res) p.Payload = res
bres, err := json.Marshal(p)
if err != nil { if err != nil {
w.Write([]byte(err.Error())) s.WriteError(w, err.Error(), http.StatusInternalServerError)
panic(err) return
} }
w.Write(bres) 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 // TODO add page support
// GetArticlesBySourceID // GetArticlesBySourceID
// @Summary Finds the articles based on the SourceID provided. Returns the top 50. // @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 // @Produce application/json
// @Tags Articles // @Tags Articles
// @Router /articles/by/sourceid [get] // @Router /articles/by/sourceid [get]
// @Success 200 {object} ArticlesListResults "OK"
func (s *Server) GetArticlesBySourceId(w http.ResponseWriter, r *http.Request) { func (s *Server) GetArticlesBySourceId(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") 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) uuid, err := uuid.Parse(_id)
if err != nil { if err != nil {
w.Write([]byte(err.Error())) s.WriteError(w, err.Error(), http.StatusBadRequest)
panic(err) return
} }
res, err := s.Db.GetNewArticlesBySourceId(*s.ctx, uuid) res, err := s.dto.GetArticlesBySourceId(r.Context(), uuid)
//res, err := s.Db.GetArticlesBySourceId(*s.ctx, uuid)
if err != nil { if err != nil {
w.Write([]byte(err.Error())) s.WriteError(w, err.Error(), http.StatusInternalServerError)
panic(err) return
} }
bres, err := json.Marshal(res) bres, err := json.Marshal(res)
if err != nil { if err != nil {
w.Write([]byte(err.Error())) s.WriteError(w, err.Error(), http.StatusInternalServerError)
panic(err) return
}
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)
} }
w.Write(bres) w.Write(bres)

View File

@ -1,29 +0,0 @@
package routes
import (
"encoding/json"
"net/http"
)
// GetDiscordQueue
// @Summary Returns the top 100 entries from the queue to be processed.
// @Produce application/json
// @Tags Debug, Discord, Queue
// @Router /discord/queue [get]
func (s *Server) GetDiscordQueue(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
res, err := s.Db.ListDiscordQueueItems(*s.ctx, 100)
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)
}
w.Write(bres)
}

View File

@ -1,7 +1,6 @@
package routes package routes
import ( import (
"context"
"encoding/json" "encoding/json"
"log" "log"
"net/http" "net/http"
@ -10,62 +9,106 @@ import (
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/jtom38/newsbot/collector/database" "github.com/jtom38/newsbot/collector/database"
"github.com/jtom38/newsbot/collector/domain/models"
) )
// GetDiscordWebHooks func (s Server) DiscordWebHookRouter() http.Handler {
r := chi.NewRouter()
r.Get("/", s.ListDiscordWebHooks)
r.Post("/new", s.NewDiscordWebHook)
r.Get("/by/serverAndChannel", s.GetDiscordWebHooksByServerAndChannel)
r.Route("/{ID}", func(r chi.Router) {
r.Get("/", s.GetDiscordWebHooksById)
r.Delete("/", s.deleteDiscordWebHook)
r.Post("/disable", s.disableDiscordWebHook)
r.Post("/enable", s.enableDiscordWebHook)
})
return r
}
type ListDiscordWebhooks struct {
ApiStatusModel
Payload []models.DiscordWebHooksDto `json:"payload"`
}
// ListDiscordWebhooks
// @Summary Returns the top 100 entries from the queue to be processed. // @Summary Returns the top 100 entries from the queue to be processed.
// @Produce application/json // @Produce application/json
// @Tags Config, Discord, Webhook // @Tags Discord, Webhook
// @Router /discord/webhooks [get] // @Router /discord/webhooks [get]
func (s *Server) GetDiscordWebHooks(w http.ResponseWriter, r *http.Request) { func (s *Server) ListDiscordWebHooks(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") p := ListDiscordWebhooks{
ApiStatusModel: ApiStatusModel{
res, err := s.Db.ListDiscordWebhooks(*s.ctx, 100) Message: "OK",
if err != nil { StatusCode: http.StatusOK,
w.Write([]byte(err.Error())) },
panic(err)
} }
bres, err := json.Marshal(res) w.Header().Set(HeaderContentType, ApplicationJson)
res, err := s.dto.ListDiscordWebHooks(r.Context(), 50)
if err != nil { if err != nil {
w.Write([]byte(err.Error())) s.WriteError(w, err.Error(), http.StatusInternalServerError)
panic(err) return
}
p.Payload = res
bres, err := json.Marshal(p)
if err != nil {
s.WriteError(w, err.Error(), http.StatusInternalServerError)
return
} }
w.Write(bres) w.Write(bres)
} }
type GetDiscordWebhook struct {
ApiStatusModel
Payload models.DiscordWebHooksDto `json:"payload"`
}
// GetDiscordWebHook // GetDiscordWebHook
// @Summary Returns the top 100 entries from the queue to be processed. // @Summary Returns the top 100 entries from the queue to be processed.
// @Produce application/json // @Produce application/json
// @Param id path string true "id" // @Param id path string true "id"
// @Tags Config, Discord, Webhook // @Tags Discord, Webhook
// @Router /discord/webhooks/{id} [get] // @Router /discord/webhooks/{id} [get]
// @Success 200 {object} GetDiscordWebhook "OK"
func (s *Server) GetDiscordWebHooksById(w http.ResponseWriter, r *http.Request) { func (s *Server) GetDiscordWebHooksById(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") p := GetDiscordWebhook{
ApiStatusModel: ApiStatusModel{
Message: "OK",
StatusCode: http.StatusOK,
},
}
w.Header().Set(HeaderContentType, ApplicationJson)
_id := chi.URLParam(r, "ID") _id := chi.URLParam(r, "ID")
if _id == "" { if _id == "" {
http.Error(w, "id is missing", http.StatusBadRequest) s.WriteError(w, "id is missing", http.StatusBadRequest)
return return
} }
uuid, err := uuid.Parse(_id) uuid, err := uuid.Parse(_id)
if err != nil { if err != nil {
http.Error(w, "unable to parse id value", http.StatusBadRequest) s.WriteError(w, "unable to parse id value", http.StatusBadRequest)
return return
} }
res, err := s.Db.GetDiscordWebHooksByID(*s.ctx, uuid) res, err := s.dto.GetDiscordWebhook(r.Context(), uuid)
if err != nil { if err != nil {
http.Error(w, "no record found", http.StatusBadRequest) s.WriteError(w, "no record found", http.StatusBadRequest)
return return
} }
p.Payload = res
bres, err := json.Marshal(res) bres, err := json.Marshal(p)
if err != nil { if err != nil {
http.Error(w, "unable to convert to json", http.StatusBadRequest) s.WriteError(w, "unable to convert to json", http.StatusBadRequest)
panic(err) return
} }
w.Write(bres) w.Write(bres)
@ -76,49 +119,55 @@ func (s *Server) GetDiscordWebHooksById(w http.ResponseWriter, r *http.Request)
// @Produce application/json // @Produce application/json
// @Param server query string true "Fancy Server" // @Param server query string true "Fancy Server"
// @Param channel query string true "memes" // @Param channel query string true "memes"
// @Tags Config, Discord, Webhook // @Tags Discord, Webhook
// @Router /discord/webhooks/by/serverAndChannel [get] // @Router /discord/webhooks/by/serverAndChannel [get]
// @Success 200 {object} ListDiscordWebhooks "OK"
func (s *Server) GetDiscordWebHooksByServerAndChannel(w http.ResponseWriter, r *http.Request) { func (s *Server) GetDiscordWebHooksByServerAndChannel(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") p := ListDiscordWebhooks{
ApiStatusModel: ApiStatusModel{
Message: "OK",
StatusCode: http.StatusOK,
},
}
w.Header().Set(HeaderContentType, ApplicationJson)
query := r.URL.Query() query := r.URL.Query()
_server := query["server"][0] _server := query["server"][0]
if _server == "" { if _server == "" {
http.Error(w, "ID is missing", http.StatusInternalServerError) s.WriteError(w, "ID is missing", http.StatusInternalServerError)
return return
} }
_channel := query["channel"][0] _channel := query["channel"][0]
if _channel == "" { if _channel == "" {
http.Error(w, "Channel is missing", http.StatusInternalServerError) s.WriteError(w, "Channel is missing", http.StatusInternalServerError)
return
}
res, err := s.Db.GetDiscordWebHooksByServerAndChannel(context.Background(), database.GetDiscordWebHooksByServerAndChannelParams{
Server: _server,
Channel: _channel,
})
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return return
} }
bres, err := json.Marshal(res) res, err := s.dto.GetDiscordWebHookByServerAndChannel(r.Context(), _server, _channel)
if err != nil { if err != nil {
http.Error(w, "unable to convert to json", http.StatusInternalServerError) s.WriteError(w, err.Error(), http.StatusInternalServerError)
panic(err) return
}
p.Payload = res
bres, err := json.Marshal(p)
if err != nil {
s.WriteError(w, "unable to convert to json", http.StatusInternalServerError)
return
} }
w.Write(bres) w.Write(bres)
} }
// NewDiscordWebHook // NewDiscordWebHook
// @Summary Creates a new record for a discord web hook to post data to. // @Summary Creates a new record for a discord web hook to post data to.
// @Param url query string true "url" // @Param url query string true "url"
// @Param server query string true "Server name" // @Param server query string true "Server name"
// @Param channel query string true "Channel name" // @Param channel query string true "Channel name"
// @Tags Config, Discord, Webhook // @Tags Discord, Webhook
// @Router /discord/webhooks/new [post] // @Router /discord/webhooks/new [post]
func (s *Server) NewDiscordWebHook(w http.ResponseWriter, r *http.Request) { func (s *Server) NewDiscordWebHook(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query() query := r.URL.Query()
@ -160,82 +209,84 @@ func (s *Server) NewDiscordWebHook(w http.ResponseWriter, r *http.Request) {
// DisableDiscordWebHooks // DisableDiscordWebHooks
// @Summary Disables a Webhook from being used. // @Summary Disables a Webhook from being used.
// @Param id path string true "id" // @Param id path string true "id"
// @Tags Config, Discord, Webhook // @Tags Discord, Webhook
// @Router /discord/webhooks/{id}/disable [post] // @Router /discord/webhooks/{ID}/disable [post]
func (s *Server) disableDiscordWebHook(w http.ResponseWriter, r *http.Request) { func (s *Server) disableDiscordWebHook(w http.ResponseWriter, r *http.Request) {
id := chi.URLParam(r, "ID") id := chi.URLParam(r, "ID")
uuid, err := uuid.Parse(id) uuid, err := uuid.Parse(id)
if err != nil { if err != nil {
log.Panicln(err) s.WriteError(w, err.Error(), http.StatusBadRequest)
return
} }
// Check to make sure we can find the record // Check to make sure we can find the record
_, err = s.Db.GetDiscordWebHooksByID(*s.ctx, uuid) _, err = s.Db.GetDiscordWebHooksByID(*s.ctx, uuid)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) s.WriteError(w, err.Error(), http.StatusBadRequest)
return
} }
err = s.Db.DisableDiscordWebHook(*s.ctx, uuid) err = s.Db.DisableDiscordWebHook(*s.ctx, uuid)
if err != nil { if err != nil {
log.Panic(err) s.WriteError(w, err.Error(), http.StatusInternalServerError)
} }
} }
// EnableDiscordWebHook // EnableDiscordWebHook
// @Summary Enables a source to continue processing. // @Summary Enables a source to continue processing.
// @Param id path string true "id" // @Param id path string true "id"
// @Tags Config, Discord, Webhook // @Tags Discord, Webhook
// @Router /discord/webhooks/{id}/enable [post] // @Router /discord/webhooks/{ID}/enable [post]
func (s *Server) enableDiscordWebHook(w http.ResponseWriter, r *http.Request) { func (s *Server) enableDiscordWebHook(w http.ResponseWriter, r *http.Request) {
id := chi.URLParam(r, "ID") id := chi.URLParam(r, "ID")
uuid, err := uuid.Parse(id) uuid, err := uuid.Parse(id)
if err != nil { if err != nil {
log.Panicln(err) s.WriteError(w, err.Error(), http.StatusBadRequest)
} }
// Check to make sure we can find the record // Check to make sure we can find the record
_, err = s.Db.GetDiscordWebHooksByID(*s.ctx, uuid) _, err = s.Db.GetDiscordWebHooksByID(*s.ctx, uuid)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) s.WriteError(w, err.Error(), http.StatusBadRequest)
} }
err = s.Db.EnableDiscordWebHook(*s.ctx, uuid) err = s.Db.EnableDiscordWebHook(*s.ctx, uuid)
if err != nil { if err != nil {
log.Panic(err) s.WriteError(w, err.Error(), http.StatusInternalServerError)
} }
} }
// DeleteDiscordWebHook // DeleteDiscordWebHook
// @Summary Deletes a record by ID. // @Summary Deletes a record by ID.
// @Param id path string true "id" // @Param id path string true "id"
// @Tags Config, Discord, Webhook // @Tags Discord, Webhook
// @Router /discord/webhooks/{id} [delete] // @Router /discord/webhooks/{ID} [delete]
func (s *Server) deleteDiscordWebHook(w http.ResponseWriter, r *http.Request) { func (s *Server) deleteDiscordWebHook(w http.ResponseWriter, r *http.Request) {
//var item model.Sources = model.Sources{} //var item model.Sources = model.Sources{}
id := chi.URLParam(r, "ID") id := chi.URLParam(r, "ID")
uuid, err := uuid.Parse(id) uuid, err := uuid.Parse(id)
if err != nil { if err != nil {
log.Panicln(err) s.WriteError(w, err.Error(), http.StatusBadRequest)
} }
// Check to make sure we can find the record // Check to make sure we can find the record
_, err = s.Db.GetDiscordQueueByID(*s.ctx, uuid) _, err = s.Db.GetDiscordQueueByID(*s.ctx, uuid)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) s.WriteError(w, err.Error(), http.StatusBadRequest)
} }
// Delete the record // Delete the record
err = s.Db.DeleteDiscordWebHooks(*s.ctx, uuid) err = s.Db.DeleteDiscordWebHooks(*s.ctx, uuid)
if err != nil { if err != nil {
log.Panic(err) s.WriteError(w, err.Error(), http.StatusInternalServerError)
} }
} }
// UpdateDiscordWebHook // UpdateDiscordWebHook
// @Summary Updates a valid discord webhook ID based on the body given. // @Summary Updates a valid discord webhook ID based on the body given.
// @Param id path string true "id" // @Param id path string true "id"
// @Tags Config, Discord, Webhook // @Tags Discord, Webhook
// @Router /discord/webhooks/{id} [patch] // @Router /discord/webhooks/{id} [patch]
func (s *Server) UpdateDiscordWebHook(w http.ResponseWriter, r *http.Request) { func (s *Server) UpdateDiscordWebHook(w http.ResponseWriter, r *http.Request) {
id := chi.URLParam(r, "ID") id := chi.URLParam(r, "ID")

57
routes/queue.go Normal file
View File

@ -0,0 +1,57 @@
package routes
import (
"encoding/json"
"net/http"
"github.com/go-chi/chi/v5"
"github.com/jtom38/newsbot/collector/domain/models"
)
func (s *Server) GetQueueRouter() http.Handler {
r := chi.NewRouter()
r.Get("/discord/webhooks", s.ListDiscordWebhookQueue)
return r
}
type ListDiscordWebHooksQueueResults struct {
ApiStatusModel
Payload []models.DiscordQueueDetailsDto `json:"payload"`
}
// GetDiscordQueue
// @Summary Returns the top 100 entries from the queue to be processed.
// @Produce application/json
// @Tags Queue
// @Router /queue/discord/webhooks [get]
// @Success 200 {object} ListDiscordWebHooksQueueResults "ok"
func (s *Server) ListDiscordWebhookQueue(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
p := ListDiscordWebHooksQueueResults{
ApiStatusModel: ApiStatusModel{
Message: "OK",
StatusCode: http.StatusOK,
},
}
// Get the raw resp from sql
res, err := s.dto.ListDiscordWebhookQueueDetails(r.Context(), 50)
if err != nil {
s.WriteError(w, err.Error(), http.StatusInternalServerError)
return
}
p.Payload = res
// convert to json
b, err := json.Marshal(p)
if err != nil {
s.WriteError(w, err.Error(), http.StatusInternalServerError)
return
}
w.Write(b)
}

View File

@ -1,50 +0,0 @@
package routes
import (
"fmt"
"net/http"
"github.com/go-chi/chi/v5"
)
func RootRoutes() chi.Router {
app := chi.NewRouter()
app.Route("/", func(r chi.Router) {
r.Get("/helloworld", helloWorld)
r.Get("/ping", ping)
r.Route("/hello/{who}", func(r chi.Router) {
r.Get("/", helloWho)
})
})
return app
}
// HelloWorld
// @Summary Responds back with "Hello world!"
// @Produce plain
// @Tags Debug
// @Router /helloworld [get]
func helloWorld(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello World!"))
}
// Ping
// @Summary Sends back "pong". Good to test with.
// @Produce plain
// @Tags Debug
// @Router /ping [get]
func ping(w http.ResponseWriter, r *http.Request) {
msg := "pong"
w.Write([]byte(msg))
}
// HelloWho
// @Summary Responds back with "Hello x" depending on param passed in.
// @Param who path string true "Who"
// @Produce plain
// @Tags Debug
// @Router /hello/{who} [get]
func helloWho(w http.ResponseWriter, r *http.Request) {
msg := fmt.Sprintf("Hello %v", chi.URLParam(r, "who"))
w.Write([]byte(msg))
}

View File

@ -3,8 +3,8 @@ package routes
import ( import (
"context" "context"
"database/sql" "database/sql"
"encoding/json"
//"net/http" "net/http"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware" "github.com/go-chi/chi/v5/middleware"
@ -12,15 +12,23 @@ import (
httpSwagger "github.com/swaggo/http-swagger" httpSwagger "github.com/swaggo/http-swagger"
"github.com/jtom38/newsbot/collector/database" "github.com/jtom38/newsbot/collector/database"
"github.com/jtom38/newsbot/collector/dto"
"github.com/jtom38/newsbot/collector/services/config" "github.com/jtom38/newsbot/collector/services/config"
) )
type Server struct { type Server struct {
Router *chi.Mux Router *chi.Mux
Db *database.Queries Db *database.Queries
dto dto.DtoClient
ctx *context.Context ctx *context.Context
} }
const (
HeaderContentType = "Content-Type"
ApplicationJson = "application/json"
)
var ( var (
ErrIdValueMissing string = "id value is missing" ErrIdValueMissing string = "id value is missing"
ErrValueNotUuid string = "a value given was expected to be a uuid but was not correct." ErrValueNotUuid string = "a value given was expected to be a uuid but was not correct."
@ -28,16 +36,18 @@ var (
ErrUnableToConvertToJson string = "Unable to convert to json" ErrUnableToConvertToJson string = "Unable to convert to json"
) )
func NewServer(ctx context.Context) *Server { func NewServer(ctx context.Context, db *database.Queries) *Server {
s := &Server{ s := &Server{
ctx: &ctx, ctx: &ctx,
Db: db,
dto: dto.NewDtoClient(db),
} }
db, err := openDatabase(ctx) //db, err := openDatabase(ctx)
if err != nil { //if err != nil {
panic(err) // panic(err)
} //}
s.Db = db //s.Db = db
s.Router = chi.NewRouter() s.Router = chi.NewRouter()
s.MountMiddleware() s.MountMiddleware()
@ -68,56 +78,37 @@ func (s *Server) MountRoutes() {
httpSwagger.URL("doc.json"), //The url pointing to API definition httpSwagger.URL("doc.json"), //The url pointing to API definition
)) ))
/* Root Routes */ s.Router.Mount("/api/articles", s.GetArticleRouter())
s.Router.Get("/api/helloworld", helloWorld) s.Router.Mount("/api/queue", s.GetQueueRouter())
s.Router.Get("/api/hello/{who}", helloWho) s.Router.Mount("/api/discord/webhooks", s.DiscordWebHookRouter())
s.Router.Get("/api/ping", ping)
/* 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)
/* Discord Queue */
s.Router.Get("/api/discord/queue", s.GetDiscordQueue)
/* Discord WebHooks */
s.Router.Post("/api/discord/webhooks/new", s.NewDiscordWebHook)
s.Router.Get("/api/discord/webhooks", s.GetDiscordWebHooks)
//s.Router.Get("/api/discord/webhooks/byId", s.GetDiscordWebHooksById)
s.Router.Get("/api/discord/webhooks/by/serverAndChannel", s.GetDiscordWebHooksByServerAndChannel)
s.Router.Route("/api/discord/webhooks/{ID}", func(r chi.Router) { //s.Router.Get("/api/settings", s.getSettings)
r.Get("/", s.GetDiscordWebHooksById)
r.Delete("/", s.deleteDiscordWebHook)
r.Post("/disable", s.disableDiscordWebHook)
r.Post("/enable", s.enableDiscordWebHook)
})
/* Settings */ s.Router.Mount("/api/sources", s.GetSourcesRouter())
s.Router.Get("/api/settings", s.getSettings) s.Router.Mount("/api/subscriptions", s.GetSubscriptionsRouter())
}
/* Source Routes */
s.Router.Get("/api/config/sources", s.listSources) type ApiStatusModel struct {
s.Router.Get("/api/config/sources/by/source", s.listSourcesBySource) StatusCode int `json:"status"`
s.Router.Post("/api/config/sources/new/reddit", s.newRedditSource) Message string `json:"message"`
s.Router.Post("/api/config/sources/new/youtube", s.newYoutubeSource) }
s.Router.Post("/api/config/sources/new/twitch", s.newTwitchSource)
s.Router.Route("/api/config/sources/{ID}", func(r chi.Router) { type ApiError struct {
r.Get("/", s.getSources) *ApiStatusModel
r.Delete("/", s.deleteSources) }
r.Post("/disable", s.disableSource)
r.Post("/enable", s.enableSource) func (s *Server) WriteError(w http.ResponseWriter, errMessage string, HttpStatusCode int) {
//r.Post("/delete", ) e := ApiError{
}) ApiStatusModel: &ApiStatusModel{
s.Router.Get("/api/config/sources/by/sourceAndName", s.GetSourceBySourceAndName) StatusCode: http.StatusInternalServerError,
Message: errMessage,
/* Subscriptions */ },
s.Router.Get("/api/subscriptions", s.ListSubscriptions) }
s.Router.Get("/api/subscriptions/byDiscordId", s.GetSubscriptionsByDiscordId)
s.Router.Get("/api/subscriptions/bySourceId", s.GetSubscriptionsBySourceId) b, err := json.Marshal(e)
s.Router.Post("/api/subscriptions/new/discordwebhook", s.newDiscordWebHookSubscription) if err != nil {
s.Router.Delete("/api/subscriptions/discord/webhook/delete", s.DeleteDiscordWebHookSubscription) http.Error(w, err.Error(), http.StatusInternalServerError)
}
w.Write(b)
} }

View File

@ -2,43 +2,43 @@ package routes
import ( import (
"encoding/json" "encoding/json"
"log"
"net/http" "net/http"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"github.com/google/uuid" "github.com/google/uuid"
) )
// GetSettings
// @Summary Returns a object based on the Key that was given.
// @Param key path string true "Settings Key value"
// @Produce application/json
// @Tags Settings
// @Router /settings/{key} [get]
func (s *Server) getSettings(w http.ResponseWriter, r *http.Request) { func (s *Server) getSettings(w http.ResponseWriter, r *http.Request) {
// GetSettings
// @Summary Returns a object based on the Key that was given.
// @Param key path string true "Settings Key value"
// @Produce application/json
// @Tags Settings
// @Router /settings/{key} [get]
w.Header().Set("Content-Type", "application/json")
//var item model.Sources //var item model.Sources
id := chi.URLParam(r, "ID") id := chi.URLParam(r, "ID")
uuid, err := uuid.Parse(id) uuid, err := uuid.Parse(id)
if err != nil { if err != nil {
panic(err) s.WriteError(w, err.Error(), http.StatusBadRequest)
return
} }
res, err := s.Db.GetSourceByID(*s.ctx, uuid) res, err := s.Db.GetSourceByID(*s.ctx, uuid)
if err != nil { if err != nil {
panic(err) s.WriteError(w, err.Error(), http.StatusNotFound)
return
} }
//itemId := fmt.Sprint(item.ID)
//if id != itemId {
// log.Panicln("Unable to find the requested record. Either unable to access SQL or the record does not exist.")
//}
bResult, err := json.Marshal(res) bResult, err := json.Marshal(res)
if err != nil { if err != nil {
log.Panicln(err) s.WriteError(w, err.Error(), http.StatusInternalServerError)
return
} }
w.Header().Set("Content-Type", "application/json")
w.Write(bResult) w.Write(bResult)
} }

View File

@ -1,23 +1,55 @@
package routes package routes
import ( import (
"context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"log"
"net/http" "net/http"
"strings" "strings"
"github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/jtom38/newsbot/collector/database" "github.com/jtom38/newsbot/collector/database"
"github.com/jtom38/newsbot/collector/domain/models"
) )
func (s *Server) GetSourcesRouter() http.Handler {
r := chi.NewRouter()
r.Get("/", s.listSources)
r.Get("/by/source", s.listSourcesBySource)
r.Get("/by/sourceAndName", s.GetSourceBySourceAndName)
r.Post("/new/reddit", s.newRedditSource)
r.Post("/new/youtube", s.newYoutubeSource)
r.Post("/new/twitch", s.newTwitchSource)
r.Route("/{ID}", func(p chi.Router) {
p.Get("/", s.getSources)
p.Delete("/", s.deleteSources)
p.Post("/disable", s.disableSource)
p.Post("/enable", s.enableSource)
})
return r
}
type ListSources struct {
ApiStatusModel
Payload []models.SourceDto `json:"payload"`
}
type GetSource struct {
ApiStatusModel
Payload models.SourceDto `json:"payload"`
}
// ListSources // ListSources
// @Summary Lists the top 50 records // @Summary Lists the top 50 records
// @Produce application/json // @Produce application/json
// @Tags Config, Source // @Tags Source
// @Router /config/sources [get] // @Router /sources [get]
// @Success 200 {object} ListSources "ok"
// @Failure 400 {object} ApiError "Unable to reach SQL or Data problems"
func (s *Server) listSources(w http.ResponseWriter, r *http.Request) { func (s *Server) listSources(w http.ResponseWriter, r *http.Request) {
//TODO Add top? //TODO Add top?
/* /*
@ -29,35 +61,41 @@ func (s *Server) listSources(w http.ResponseWriter, r *http.Request) {
res, err := s.Db.ListSources(*s.ctx, int32(topInt)) res, err := s.Db.ListSources(*s.ctx, int32(topInt))
*/ */
w.Header().Set(HeaderContentType, ApplicationJson)
result := ListSources{
ApiStatusModel: ApiStatusModel{
StatusCode: http.StatusOK,
Message: "OK",
},
}
// Default way of showing all sources // Default way of showing all sources
res, err := s.Db.ListSources(*s.ctx, 50) items, err := s.dto.ListSources(r.Context(), 50)
if err != nil { if err != nil {
http.Error(w, "url is missing a value", http.StatusBadRequest) s.WriteError(w, err.Error(), http.StatusInternalServerError)
return return
} }
var dto []database.SourceDto result.Payload = items
for _, item := range res {
dto = append(dto, database.ConvertToSourceDto(item))
}
bResult, err := json.Marshal(dto) bResult, err := json.Marshal(result)
if err != nil { if err != nil {
http.Error(w, "unable to convert to json", http.StatusBadRequest) s.WriteError(w, err.Error(), http.StatusInternalServerError)
return return
} }
w.Header().Set("Content-Type", "application/json")
w.Write(bResult) w.Write(bResult)
} }
// ListSourcesBySource // ListSourcesBySource
// @Summary Lists the top 50 records based on the name given. Example: reddit // @Summary Lists the top 50 records based on the name given. Example: reddit
// @Param source query string true "Source Name" // @Param source query string true "Source Name"
// @Produce application/json // @Produce application/json
// @Tags Config, Source // @Tags Source
// @Router /config/sources/by/source [get] // @Router /sources/by/source [get]
// @Success 200 {object} ListSources "ok"
// @Failure 400 {object} ApiError "Unable to query SQL."
// @Failure 500 {object} ApiError "Problems with data."
func (s *Server) listSourcesBySource(w http.ResponseWriter, r *http.Request) { func (s *Server) listSourcesBySource(w http.ResponseWriter, r *http.Request) {
//TODO Add top? //TODO Add top?
/* /*
@ -68,23 +106,33 @@ func (s *Server) listSourcesBySource(w http.ResponseWriter, r *http.Request) {
} }
res, err := s.Db.ListSources(*s.ctx, int32(topInt)) res, err := s.Db.ListSources(*s.ctx, int32(topInt))
*/ */
w.Header().Set(HeaderContentType, ApplicationJson)
result := ListSources{
ApiStatusModel: ApiStatusModel{
StatusCode: http.StatusOK,
Message: "OK",
},
}
query := r.URL.Query() query := r.URL.Query()
_source := query["source"][0] _source := query["source"][0]
// Shows the list by Sources.source // Shows the list by Sources.source
res, err := s.Db.ListSourcesBySource(*s.ctx, strings.ToLower(_source)) res, err := s.dto.ListSourcesBySource(r.Context(), _source)
if err != nil { if err != nil {
http.Error(w, "invalid source is missing a value", http.StatusBadRequest) s.WriteError(w, err.Error(), http.StatusBadRequest)
return return
} }
bResult, err := json.Marshal(res)
if err != nil { result.Payload = res
http.Error(w, "unable to convert to json", http.StatusBadRequest)
bResult, err := json.Marshal(result)
if err != nil {
s.WriteError(w, err.Error(), http.StatusInternalServerError)
return return
} }
w.Header().Set("Content-Type", "application/json")
w.Write(bResult) w.Write(bResult)
} }
@ -92,29 +140,43 @@ func (s *Server) listSourcesBySource(w http.ResponseWriter, r *http.Request) {
// @Summary Returns a single entity by ID // @Summary Returns a single entity by ID
// @Param id path string true "uuid" // @Param id path string true "uuid"
// @Produce application/json // @Produce application/json
// @Tags Config, Source // @Tags Source
// @Router /config/sources/{id} [get] // @Router /sources/{id} [get]
// @Success 200 {object} GetSource "ok"
// @Failure 204 {object} ApiError "No record found."
// @Failure 400 {object} ApiError "Unable to query SQL."
// @Failure 500 {object} ApiError "Failed to process data from SQL."
func (s *Server) getSources(w http.ResponseWriter, r *http.Request) { func (s *Server) getSources(w http.ResponseWriter, r *http.Request) {
id := chi.URLParam(r, "ID") payload := GetSource{
ApiStatusModel: ApiStatusModel{
Message: "OK",
StatusCode: http.StatusOK,
},
}
w.Header().Set(HeaderContentType, ApplicationJson)
id := chi.URLParam(r, "ID")
uuid, err := uuid.Parse(id) uuid, err := uuid.Parse(id)
if err != nil { if err != nil {
http.Error(w, "id is not a uuid", http.StatusBadRequest) s.WriteError(w, err.Error(), http.StatusBadRequest)
return return
} }
res, err := s.Db.GetSourceByID(*s.ctx, uuid) res, err := s.dto.GetSourceById(r.Context(), uuid)
if err != nil { if err != nil {
http.Error(w, "invalid id was given", http.StatusBadRequest) s.WriteError(w, err.Error(), http.StatusNoContent)
panic(err) return
} }
bResult, err := json.Marshal(res) payload.Payload = res
bResult, err := json.Marshal(payload)
if err != nil { if err != nil {
log.Panicln(err) s.WriteError(w, err.Error(), http.StatusInternalServerError)
return
} }
w.Header().Set("Content-Type", "application/json")
w.Write(bResult) w.Write(bResult)
} }
@ -123,37 +185,48 @@ func (s *Server) getSources(w http.ResponseWriter, r *http.Request) {
// @Param name query string true "dadjokes" // @Param name query string true "dadjokes"
// @Param source query string true "reddit" // @Param source query string true "reddit"
// @Produce application/json // @Produce application/json
// @Tags Config, Source // @Tags Source
// @Router /config/sources/by/sourceAndName [get] // @Router /sources/by/sourceAndName [get]
// @Success 200 {object} GetSource "ok"
// @Failure 204 {object} ApiError "No record found."
// @Failure 400 {object} ApiError "Unable to query SQL."
// @Failure 500 {object} ApiError "Failed to process data from SQL."
func (s *Server) GetSourceBySourceAndName(w http.ResponseWriter, r *http.Request) { func (s *Server) GetSourceBySourceAndName(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query() p := GetSource{
ApiStatusModel: ApiStatusModel{
Message: "OK",
StatusCode: http.StatusOK,
},
}
query := r.URL.Query()
name := query["name"][0] name := query["name"][0]
if name == "" { if name == "" {
http.Error(w, "Parameter 'name' was missing in the query.", http.StatusInternalServerError) s.WriteError(w, "Parameter 'name' was missing in the query.", http.StatusInternalServerError)
return return
} }
source := query["source"][0] source := query["source"][0]
if source == "" { if source == "" {
http.Error(w, "The parameter 'source' was missing in the query.", http.StatusInternalServerError) s.WriteError(w, "The parameter 'source' was missing in the query.", http.StatusInternalServerError)
return return
} }
item, err := s.Db.GetSourceByNameAndSource(context.Background(), database.GetSourceByNameAndSourceParams{ item, err := s.dto.GetSourceByNameAndSource(r.Context(), name, source)
Name: name,
Source: source,
})
if err != nil { if err != nil {
http.Error(w, "Unable to find the requested record.", http.StatusInternalServerError) s.WriteError(w, "Unable to find the requested record.", http.StatusInternalServerError)
return
} }
p.Payload = item
bResult, err := json.Marshal(item) bResult, err := json.Marshal(item)
if err != nil { if err != nil {
log.Panicln(err) s.WriteError(w, err.Error(), http.StatusInternalServerError)
return
} }
w.Header().Set("Content-Type", "application/json") w.Header().Set(HeaderContentType, ApplicationJson)
w.Write(bResult) w.Write(bResult)
} }
@ -161,20 +234,22 @@ func (s *Server) GetSourceBySourceAndName(w http.ResponseWriter, r *http.Request
// @Summary Creates a new reddit source to monitor. // @Summary Creates a new reddit source to monitor.
// @Param name query string true "name" // @Param name query string true "name"
// @Param url query string true "url" // @Param url query string true "url"
// @Tags Config, Source, Reddit // @Tags Source
// @Router /config/sources/new/reddit [post] // @Router /sources/new/reddit [post]
func (s *Server) newRedditSource(w http.ResponseWriter, r *http.Request) { func (s *Server) newRedditSource(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query() query := r.URL.Query()
_name := query["name"][0] _name := query["name"][0]
_url := query["url"][0] _url := query["url"][0]
//_tags := query["tags"][0] //_tags := query["tags"][0]
w.Header().Set("Content-Type", "application/json")
if _url == "" { if _url == "" {
http.Error(w, "url is missing a value", http.StatusBadRequest) s.WriteError(w, "url is missing a value", http.StatusBadRequest)
return return
} }
if !strings.Contains(_url, "reddit.com") { if !strings.Contains(_url, "reddit.com") {
http.Error(w, "invalid url", http.StatusBadRequest) s.WriteError(w, "invalid url", http.StatusBadRequest)
return return
} }
@ -197,13 +272,18 @@ func (s *Server) newRedditSource(w http.ResponseWriter, r *http.Request) {
Url: _url, Url: _url,
Tags: tags, Tags: tags,
} }
s.Db.CreateSource(*s.ctx, params) err := s.Db.CreateSource(*s.ctx, params)
if err != nil {
s.WriteError(w, err.Error(), http.StatusInternalServerError)
return
}
bJson, err := json.Marshal(&params) bJson, err := json.Marshal(&params)
if err != nil { if err != nil {
log.Panicln(err) s.WriteError(w, err.Error(), http.StatusInternalServerError)
return
} }
w.Header().Set("Content-Type", "application/json")
w.Write(bJson) w.Write(bJson)
} }
@ -211,20 +291,21 @@ func (s *Server) newRedditSource(w http.ResponseWriter, r *http.Request) {
// @Summary Creates a new youtube source to monitor. // @Summary Creates a new youtube source to monitor.
// @Param name query string true "name" // @Param name query string true "name"
// @Param url query string true "url" // @Param url query string true "url"
// @Tags Config, Source, YouTube // @Tags Source
// @Router /config/sources/new/youtube [post] // @Router /sources/new/youtube [post]
func (s *Server) newYoutubeSource(w http.ResponseWriter, r *http.Request) { func (s *Server) newYoutubeSource(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query() query := r.URL.Query()
_name := query["name"][0] _name := query["name"][0]
_url := query["url"][0] _url := query["url"][0]
//_tags := query["tags"][0] //_tags := query["tags"][0]
w.Header().Set("Content-Type", "application/json")
if _url == "" { if _url == "" {
http.Error(w, "url is missing a value", http.StatusBadRequest) s.WriteError(w, "url is missing a value", http.StatusBadRequest)
return return
} }
if !strings.Contains(_url, "youtube.com") { if !strings.Contains(_url, "youtube.com") {
http.Error(w, "invalid url", http.StatusBadRequest) s.WriteError(w, "invalid url", http.StatusBadRequest)
return return
} }
@ -246,22 +327,29 @@ func (s *Server) newYoutubeSource(w http.ResponseWriter, r *http.Request) {
Url: _url, Url: _url,
Tags: tags, Tags: tags,
} }
s.Db.CreateSource(*s.ctx, params) err := s.Db.CreateSource(*s.ctx, params)
if err != nil {
s.WriteError(w, err.Error(), http.StatusInternalServerError)
return
}
bJson, err := json.Marshal(&params) bJson, err := json.Marshal(&params)
if err != nil { if err != nil {
log.Panicln(err) s.WriteError(w, err.Error(), http.StatusInternalServerError)
return
} }
w.Header().Set("Content-Type", "application/json")
w.Write(bJson) w.Write(bJson)
} }
// NewTwitchSource // NewTwitchSource
// @Summary Creates a new twitch source to monitor. // @Summary Creates a new twitch source to monitor.
// @Param name query string true "name" // @Param name query string true "name"
// @Tags Config, Source, Twitch // @Tags Source
// @Router /config/sources/new/twitch [post] // @Router /sources/new/twitch [post]
func (s *Server) newTwitchSource(w http.ResponseWriter, r *http.Request) { func (s *Server) newTwitchSource(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
query := r.URL.Query() query := r.URL.Query()
_name := query["name"][0] _name := query["name"][0]
@ -278,13 +366,18 @@ func (s *Server) newTwitchSource(w http.ResponseWriter, r *http.Request) {
Url: _url, Url: _url,
Tags: tags, Tags: tags,
} }
s.Db.CreateSource(*s.ctx, params) err := s.Db.CreateSource(*s.ctx, params)
if err != nil {
s.WriteError(w, err.Error(), http.StatusInternalServerError)
return
}
bJson, err := json.Marshal(&params) bJson, err := json.Marshal(&params)
if err != nil { if err != nil {
log.Panicln(err) s.WriteError(w, err.Error(), http.StatusInternalServerError)
return
} }
w.Header().Set("Content-Type", "application/json")
w.Write(bJson) w.Write(bJson)
} }
@ -292,73 +385,115 @@ func (s *Server) newTwitchSource(w http.ResponseWriter, r *http.Request) {
// @Summary Marks a source as deleted based on its ID value. // @Summary Marks a source as deleted based on its ID value.
// @Param id path string true "id" // @Param id path string true "id"
// @Tags Source // @Tags Source
// @Router /config/sources/{id} [POST] // @Router /sources/{id} [POST]
func (s *Server) deleteSources(w http.ResponseWriter, r *http.Request) { func (s *Server) deleteSources(w http.ResponseWriter, r *http.Request) {
//var item model.Sources = model.Sources{} //var item model.Sources = model.Sources{}
id := chi.URLParam(r, "ID") id := chi.URLParam(r, "ID")
uuid, err := uuid.Parse(id) uuid, err := uuid.Parse(id)
if err != nil { if err != nil {
log.Panicln(err) s.WriteError(w, err.Error(), http.StatusInternalServerError)
return
} }
// Check to make sure we can find the record // Check to make sure we can find the record
_, err = s.Db.GetSourceByID(*s.ctx, uuid) _, err = s.Db.GetSourceByID(*s.ctx, uuid)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) s.WriteError(w, err.Error(), http.StatusInternalServerError)
return
} }
// Delete the record // Delete the record
err = s.Db.DeleteSource(*s.ctx, uuid) err = s.Db.DeleteSource(*s.ctx, uuid)
if err != nil { if err != nil {
log.Panic(err) s.WriteError(w, err.Error(), http.StatusInternalServerError)
return
} }
p := ApiStatusModel{
Message: "OK",
StatusCode: http.StatusOK,
}
b, err := json.Marshal(p)
if err != nil {
s.WriteError(w, err.Error(), http.StatusInternalServerError)
return
}
w.Write(b)
} }
// DisableSource // DisableSource
// @Summary Disables a source from processing. // @Summary Disables a source from processing.
// @Param id path string true "id" // @Param id path string true "id"
// @Tags Config, Source // @Tags Source
// @Router /config/sources/{id}/disable [post] // @Router /sources/{id}/disable [post]
func (s *Server) disableSource(w http.ResponseWriter, r *http.Request) { func (s *Server) disableSource(w http.ResponseWriter, r *http.Request) {
id := chi.URLParam(r, "ID") id := chi.URLParam(r, "ID")
uuid, err := uuid.Parse(id) uuid, err := uuid.Parse(id)
if err != nil { if err != nil {
log.Panicln(err) s.WriteError(w, err.Error(), http.StatusInternalServerError)
} }
// Check to make sure we can find the record // Check to make sure we can find the record
_, err = s.Db.GetSourceByID(*s.ctx, uuid) _, err = s.Db.GetSourceByID(*s.ctx, uuid)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) s.WriteError(w, err.Error(), http.StatusInternalServerError)
} }
err = s.Db.DisableSource(*s.ctx, uuid) err = s.Db.DisableSource(*s.ctx, uuid)
if err != nil { if err != nil {
log.Panic(err) s.WriteError(w, err.Error(), http.StatusInternalServerError)
} }
p := ApiStatusModel{
Message: "OK",
StatusCode: http.StatusOK,
}
b, err := json.Marshal(p)
if err != nil {
s.WriteError(w, err.Error(), http.StatusInternalServerError)
return
}
w.Write(b)
} }
// EnableSource // EnableSource
// @Summary Enables a source to continue processing. // @Summary Enables a source to continue processing.
// @Param id path string true "id" // @Param id path string true "id"
// @Tags Config, Source // @Tags Source
// @Router /config/sources/{id}/enable [post] // @Router /sources/{id}/enable [post]
func (s *Server) enableSource(w http.ResponseWriter, r *http.Request) { func (s *Server) enableSource(w http.ResponseWriter, r *http.Request) {
id := chi.URLParam(r, "ID") id := chi.URLParam(r, "ID")
uuid, err := uuid.Parse(id) uuid, err := uuid.Parse(id)
if err != nil { if err != nil {
log.Panicln(err) s.WriteError(w, err.Error(), http.StatusInternalServerError)
} }
// Check to make sure we can find the record // Check to make sure we can find the record
_, err = s.Db.GetSourceByID(*s.ctx, uuid) _, err = s.Db.GetSourceByID(*s.ctx, uuid)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) s.WriteError(w, err.Error(), http.StatusInternalServerError)
} }
err = s.Db.EnableSource(*s.ctx, uuid) err = s.Db.EnableSource(*s.ctx, uuid)
if err != nil { if err != nil {
log.Panic(err) s.WriteError(w, err.Error(), http.StatusInternalServerError)
} }
p := ApiStatusModel{
Message: "OK",
StatusCode: http.StatusOK,
}
b, err := json.Marshal(p)
if err != nil {
s.WriteError(w, err.Error(), http.StatusInternalServerError)
return
}
w.Write(b)
} }

View File

@ -5,64 +5,149 @@ import (
"encoding/json" "encoding/json"
"net/http" "net/http"
"github.com/go-chi/chi/v5"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/jtom38/newsbot/collector/database" "github.com/jtom38/newsbot/collector/database"
"github.com/jtom38/newsbot/collector/domain/models"
) )
func (s *Server) GetSubscriptionsRouter() http.Handler {
r := chi.NewRouter()
r.Get("/", s.ListSubscriptions)
r.Get("/details", s.ListSubscriptionDetails)
r.Get("/by/discordId", s.GetSubscriptionsByDiscordId)
r.Get("/by/sourceId", s.GetSubscriptionsBySourceId)
r.Post("/discord/webhook/new", s.newDiscordWebHookSubscription)
r.Delete("/discord/webhook/delete", s.DeleteDiscordWebHookSubscription)
return r
}
type ListSubscriptions struct {
ApiStatusModel
Payload []models.SubscriptionDto `json:"payload"`
}
type GetSubscription struct {
ApiStatusModel
Payload models.SubscriptionDto `json:"payload"`
}
// GetSubscriptions // GetSubscriptions
// @Summary Returns the top 100 entries from the queue to be processed. // @Summary Returns the top 100 entries from the queue to be processed.
// @Produce application/json // @Produce application/json
// @Tags Subscription // @Tags Subscription
// @Router /subscriptions [get] // @Router /subscriptions [get]
// @Success 200 {object} ListSubscriptions "ok"
// @Failure 400 {object} ApiError "Unable to reach SQL."
// @Failure 500 {object} ApiError "Failed to process data from SQL."
func (s *Server) ListSubscriptions(w http.ResponseWriter, r *http.Request) { func (s *Server) ListSubscriptions(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
res, err := s.Db.ListSubscriptions(*s.ctx, 100) payload := ListSubscriptions{
if err != nil { ApiStatusModel: ApiStatusModel{
w.Write([]byte(err.Error())) StatusCode: http.StatusOK,
panic(err) Message: "OK",
},
} }
bres, err := json.Marshal(res) res, err := s.dto.ListSubscriptions(r.Context(), 50)
if err != nil { if err != nil {
http.Error(w, ErrUnableToConvertToJson, http.StatusBadRequest) s.WriteError(w, err.Error(), http.StatusBadRequest)
panic(err) return
}
payload.Payload = res
bres, err := json.Marshal(payload)
if err != nil {
s.WriteError(w, err.Error(), http.StatusInternalServerError)
return
} }
w.Write(bres) w.Write(bres)
} }
type ListSubscriptionDetails struct {
ApiStatusModel
Payload []models.SubscriptionDetailsDto `json:"payload"`
}
// ListSubscriptionDetails
// @Summary Returns the top 50 entries with full deatils on the source and output.
// @Produce application/json
// @Tags Subscription
// @Router /subscriptions/details [get]
// @Success 200 {object} ListSubscriptionDetails "ok"
func (s Server) ListSubscriptionDetails(w http.ResponseWriter, r *http.Request) {
w.Header().Set(HeaderContentType, ApplicationJson)
payload := ListSubscriptionDetails{
ApiStatusModel: ApiStatusModel{
StatusCode: http.StatusOK,
Message: "OK",
},
}
res, err := s.dto.ListSubscriptionDetails(r.Context(), 50)
if err != nil {
s.WriteError(w, err.Error(), http.StatusInternalServerError)
return
}
payload.Payload = res
b, err := json.Marshal(payload)
if err != nil {
s.WriteError(w, err.Error(), http.StatusInternalServerError)
}
w.Write(b)
}
// GetSubscriptionsByDiscordId // GetSubscriptionsByDiscordId
// @Summary Returns the top 100 entries from the queue to be processed. // @Summary Returns the top 100 entries from the queue to be processed.
// @Produce application/json // @Produce application/json
// @Param id query string true "id" // @Param id query string true "id"
// @Tags Subscription // @Tags Subscription
// @Router /subscriptions/byDiscordId [get] // @Router /subscriptions/by/discordId [get]
// @Success 200 {object} ListSubscriptions "ok"
// @Failure 400 {object} ApiError "Unable to reach SQL or Data problems"
// @Failure 500 {object} ApiError "Data problems"
func (s *Server) GetSubscriptionsByDiscordId(w http.ResponseWriter, r *http.Request) { func (s *Server) GetSubscriptionsByDiscordId(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") w.Header().Set(HeaderContentType, ApplicationJson)
p := ListSubscriptions{
ApiStatusModel: ApiStatusModel{
StatusCode: http.StatusOK,
Message: "OK",
},
}
query := r.URL.Query() query := r.URL.Query()
_id := query["id"][0] _id := query["id"][0]
if _id == "" { if _id == "" {
http.Error(w, ErrIdValueMissing, http.StatusBadRequest) s.WriteError(w, ErrIdValueMissing, http.StatusBadRequest)
return return
} }
uuid, err := uuid.Parse(_id) uuid, err := uuid.Parse(_id)
if err != nil { if err != nil {
http.Error(w, ErrValueNotUuid, http.StatusBadRequest) s.WriteError(w, ErrValueNotUuid, http.StatusBadRequest)
return return
} }
res, err := s.Db.GetSubscriptionsByDiscordWebHookId(*s.ctx, uuid) res, err := s.dto.ListSubscriptionsByDiscordWebhookId(r.Context(), uuid)
if err != nil { if err != nil {
http.Error(w, ErrNoRecordFound, http.StatusBadRequest) s.WriteError(w, err.Error(), http.StatusNoContent)
return return
} }
bres, err := json.Marshal(res) p.Payload = res
bres, err := json.Marshal(p)
if err != nil { if err != nil {
http.Error(w, ErrUnableToConvertToJson, http.StatusBadRequest) s.WriteError(w, err.Error(), http.StatusInternalServerError)
return return
} }
@ -74,32 +159,42 @@ func (s *Server) GetSubscriptionsByDiscordId(w http.ResponseWriter, r *http.Requ
// @Produce application/json // @Produce application/json
// @Param id query string true "id" // @Param id query string true "id"
// @Tags Subscription // @Tags Subscription
// @Router /subscriptions/bySourceId [get] // @Router /subscriptions/by/SourceId [get]
// @Success 200 {object} ListSubscriptions "ok"
func (s *Server) GetSubscriptionsBySourceId(w http.ResponseWriter, r *http.Request) { func (s *Server) GetSubscriptionsBySourceId(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
p := ListSubscriptions{
ApiStatusModel: ApiStatusModel{
StatusCode: http.StatusOK,
Message: "OK",
},
}
query := r.URL.Query() query := r.URL.Query()
_id := query["id"][0] _id := query["id"][0]
if _id == "" { if _id == "" {
http.Error(w, ErrIdValueMissing, http.StatusBadRequest) s.WriteError(w, ErrIdValueMissing, http.StatusBadRequest)
return return
} }
uuid, err := uuid.Parse(_id) uuid, err := uuid.Parse(_id)
if err != nil { if err != nil {
http.Error(w, ErrValueNotUuid, http.StatusBadRequest) s.WriteError(w, err.Error(), http.StatusBadRequest)
return return
} }
res, err := s.Db.GetSubscriptionsByDiscordWebHookId(*s.ctx, uuid) res, err := s.dto.ListSubscriptionsBySourceId(r.Context(), uuid)
if err != nil { if err != nil {
http.Error(w, ErrNoRecordFound, http.StatusBadRequest) s.WriteError(w, err.Error(), http.StatusNoContent)
return return
} }
bres, err := json.Marshal(res) p.Payload = res
bres, err := json.Marshal(p)
if err != nil { if err != nil {
http.Error(w, ErrUnableToConvertToJson, http.StatusBadRequest) s.WriteError(w, err.Error(), http.StatusInternalServerError)
return return
} }
@ -111,48 +206,44 @@ func (s *Server) GetSubscriptionsBySourceId(w http.ResponseWriter, r *http.Reque
// @Param discordWebHookId query string true "discordWebHookId" // @Param discordWebHookId query string true "discordWebHookId"
// @Param sourceId query string true "sourceId" // @Param sourceId query string true "sourceId"
// @Tags Subscription // @Tags Subscription
// @Router /subscriptions/new/discordwebhook [post] // @Router /subscriptions/discord/webhook/new [post]
func (s *Server) newDiscordWebHookSubscription(w http.ResponseWriter, r *http.Request) { func (s *Server) newDiscordWebHookSubscription(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
// Extract the values given // Extract the values given
query := r.URL.Query() query := r.URL.Query()
discordWebHookId := query["discordWebHookId"][0] discordWebHookId := query["discordWebHookId"][0]
sourceId := query["sourceId"][0] sourceId := query["sourceId"][0]
// Check to make we didnt get a null // Check to make we didn't get a null
if discordWebHookId == "" { if discordWebHookId == "" {
http.Error(w, "invalid discordWebHooksId given", http.StatusBadRequest) s.WriteError(w, "invalid discordWebHooksId given", http.StatusBadRequest)
return return
} }
if sourceId == "" { if sourceId == "" {
http.Error(w, "invalid sourceID given", http.StatusBadRequest) s.WriteError(w, "invalid sourceID given", http.StatusBadRequest)
return return
} }
// Valide they are UUID values // Validate they are UUID values
uHook, err := uuid.Parse(discordWebHookId) uHook, err := uuid.Parse(discordWebHookId)
if err != nil { if err != nil {
http.Error(w, "DiscordWebHooksID was not a uuid value.", http.StatusBadRequest) s.WriteError(w, err.Error(), http.StatusBadRequest)
return return
} }
uSource, err := uuid.Parse(sourceId) uSource, err := uuid.Parse(sourceId)
if err != nil { if err != nil {
http.Error(w, "SourceId was not a uuid value", http.StatusBadRequest) s.WriteError(w, err.Error(), http.StatusBadRequest)
return return
} }
// Check if the sub already exists // Check if the sub already exists
item, err := s.Db.QuerySubscriptions(*s.ctx, database.QuerySubscriptionsParams{ _, err = s.Db.QuerySubscriptions(*s.ctx, database.QuerySubscriptionsParams{
Discordwebhookid: uHook, Discordwebhookid: uHook,
Sourceid: uSource, Sourceid: uSource,
}) })
if err == nil { if err == nil {
bJson, err := json.Marshal(&item) s.WriteError(w, "a subscription already exists between these two entities", http.StatusBadRequest)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
w.Header().Set("Content-Type", "application/json")
w.Write(bJson)
return return
} }
@ -162,29 +253,39 @@ func (s *Server) newDiscordWebHookSubscription(w http.ResponseWriter, r *http.Re
Discordwebhookid: uHook, Discordwebhookid: uHook,
Sourceid: uSource, Sourceid: uSource,
} }
s.Db.CreateSubscription(*s.ctx, params) err = s.Db.CreateSubscription(*s.ctx, params)
if err != nil {
s.WriteError(w, err.Error(), http.StatusInternalServerError)
return
}
bJson, err := json.Marshal(&params) bJson, err := json.Marshal(&params)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) s.WriteError(w, err.Error(), http.StatusInternalServerError)
return return
} }
w.Header().Set("Content-Type", "application/json")
w.Write(bJson) w.Write(bJson)
} }
// DeleteDiscordWebHookSubscription // DeleteDiscordWebHookSubscription
// @Summary Removes a Discord WebHook Subscription based on the Subscription ID. // @Summary Removes a Discord WebHook Subscription based on the Subscription ID.
// @Param Id query string true "Id" // @Param id query string true "id"
// @Tags Config, Source, Discord, Subscription // @Tags Subscription
// @Router /subscriptions/discord/webhook/delete [delete] // @Router /subscriptions/discord/webhook/delete [delete]
func (s *Server) DeleteDiscordWebHookSubscription(w http.ResponseWriter, r *http.Request) { func (s *Server) DeleteDiscordWebHookSubscription(w http.ResponseWriter, r *http.Request) {
var ErrMissingSubscriptionID string = "Request was missing a 'Id' or was a invalid UUID." var ErrMissingSubscriptionID string = "the request was missing a 'Id'"
query := r.URL.Query() query := r.URL.Query()
uid, err := uuid.Parse(query["Id"][0]) id := query["id"][0]
if id == "" {
s.WriteError(w, ErrMissingSubscriptionID, http.StatusBadRequest)
return
}
uid, err := uuid.Parse(query["id"][0])
if err != nil { if err != nil {
http.Error(w, ErrMissingSubscriptionID, http.StatusBadRequest) s.WriteError(w, err.Error(), http.StatusBadRequest)
return return
} }

View File

@ -3,7 +3,7 @@ package cache
import ( import (
"time" "time"
"github.com/jtom38/newsbot/collector/domain/model" "github.com/jtom38/newsbot/collector/domain/models"
) )
type CacheClient struct { type CacheClient struct {
@ -19,7 +19,7 @@ func NewCacheClient(group string) CacheClient {
} }
func (cc *CacheClient) Insert(key string, value string) { func (cc *CacheClient) Insert(key string, value string) {
item := model.CacheItem{ item := models.CacheItem{
Key: key, Key: key,
Value: value, Value: value,
Group: cc.group, Group: cc.group,
@ -29,7 +29,7 @@ func (cc *CacheClient) Insert(key string, value string) {
cacheStorage = append(cacheStorage, &item) cacheStorage = append(cacheStorage, &item)
} }
func (cc *CacheClient) FindByKey(key string) (*model.CacheItem, error) { func (cc *CacheClient) FindByKey(key string) (*models.CacheItem, error) {
for _, item := range cacheStorage { for _, item := range cacheStorage {
if item.Group != cc.group { if item.Group != cc.group {
continue continue
@ -46,10 +46,10 @@ func (cc *CacheClient) FindByKey(key string) (*model.CacheItem, error) {
return item, nil return item, nil
} }
return &model.CacheItem{}, ErrCacheRecordMissing return &models.CacheItem{}, ErrCacheRecordMissing
} }
func (cc *CacheClient) FindByValue(value string) (*model.CacheItem, error) { func (cc *CacheClient) FindByValue(value string) (*models.CacheItem, error) {
for _, item := range cacheStorage { for _, item := range cacheStorage {
if item.Group != cc.group { if item.Group != cc.group {
continue continue
@ -65,5 +65,5 @@ func (cc *CacheClient) FindByValue(value string) (*model.CacheItem, error) {
} }
return item, nil return item, nil
} }
return &model.CacheItem{}, ErrCacheRecordMissing return &models.CacheItem{}, ErrCacheRecordMissing
} }

View File

@ -3,11 +3,11 @@ package cache
import ( import (
"errors" "errors"
"github.com/jtom38/newsbot/collector/domain/model" "github.com/jtom38/newsbot/collector/domain/models"
) )
var ( var (
cacheStorage []*model.CacheItem cacheStorage []*models.CacheItem
ErrCacheRecordMissing = errors.New("unable to find the requested record") ErrCacheRecordMissing = errors.New("unable to find the requested record")
) )

View File

@ -3,7 +3,7 @@ package cache
import ( import (
"time" "time"
"github.com/jtom38/newsbot/collector/domain/model" "github.com/jtom38/newsbot/collector/domain/models"
) )
// When a record becomes tainted, it needs to be renewed or it will be dropped from the cache. // When a record becomes tainted, it needs to be renewed or it will be dropped from the cache.
@ -36,8 +36,8 @@ func (cam CacheAgeMonitor) CheckExpiredEntries() {
} }
// This creates a new slice and skips over the item that needs to be dropped // This creates a new slice and skips over the item that needs to be dropped
func (cam CacheAgeMonitor) removeEntry(index int) []*model.CacheItem { func (cam CacheAgeMonitor) removeEntry(index int) []*models.CacheItem {
var temp []*model.CacheItem var temp []*models.CacheItem
for i, item := range cacheStorage { for i, item := range cacheStorage {
if i != index { if i != index {
temp = append(temp, item) temp = append(temp, item)

View File

@ -12,7 +12,7 @@ import (
"github.com/go-rod/rod" "github.com/go-rod/rod"
"github.com/go-rod/rod/lib/launcher" "github.com/go-rod/rod/lib/launcher"
"github.com/jtom38/newsbot/collector/database" "github.com/jtom38/newsbot/collector/database"
"github.com/jtom38/newsbot/collector/domain/model" "github.com/jtom38/newsbot/collector/domain/models"
"github.com/jtom38/newsbot/collector/services/config" "github.com/jtom38/newsbot/collector/services/config"
) )
@ -65,8 +65,8 @@ func (rc *RedditClient) GetPage(parser *rod.Browser, url string) *rod.Page {
// GetContent() reaches out to Reddit and pulls the Json data. // GetContent() reaches out to Reddit and pulls the Json data.
// It will then convert the data to a struct and return the struct. // It will then convert the data to a struct and return the struct.
func (rc *RedditClient) GetContent() (model.RedditJsonContent, error) { func (rc *RedditClient) GetContent() (models.RedditJsonContent, error) {
var items model.RedditJsonContent = model.RedditJsonContent{} var items models.RedditJsonContent = models.RedditJsonContent{}
// TODO Wire this to support the config options // TODO Wire this to support the config options
Url := fmt.Sprintf("%v.json", rc.record.Url) Url := fmt.Sprintf("%v.json", rc.record.Url)
@ -88,7 +88,7 @@ func (rc *RedditClient) GetContent() (model.RedditJsonContent, error) {
return items, nil return items, nil
} }
func (rc *RedditClient) ConvertToArticles(items model.RedditJsonContent) []database.Article { func (rc *RedditClient) ConvertToArticles(items models.RedditJsonContent) []database.Article {
var redditArticles []database.Article var redditArticles []database.Article
for _, item := range items.Data.Children { for _, item := range items.Data.Children {
var article database.Article var article database.Article
@ -104,7 +104,7 @@ func (rc *RedditClient) ConvertToArticles(items model.RedditJsonContent) []datab
// ConvertToArticle() will take the reddit model struct and convert them over to Article structs. // ConvertToArticle() will take the reddit model struct and convert them over to Article structs.
// This data can be passed to the database. // This data can be passed to the database.
func (rc *RedditClient) convertToArticle(source model.RedditPost) (database.Article, error) { func (rc *RedditClient) convertToArticle(source models.RedditPost) (database.Article, error) {
var item database.Article var item database.Article
if source.Content == "" && source.Url != "" { if source.Content == "" && source.Url != "" {
@ -131,7 +131,7 @@ func (rc *RedditClient) convertToArticle(source model.RedditPost) (database.Arti
return item, nil return item, nil
} }
func (rc *RedditClient) convertPicturePost(source model.RedditPost) database.Article { func (rc *RedditClient) convertPicturePost(source models.RedditPost) database.Article {
var item = database.Article{ var item = database.Article{
Sourceid: rc.record.ID, Sourceid: rc.record.ID,
Title: source.Title, Title: source.Title,
@ -149,7 +149,7 @@ func (rc *RedditClient) convertPicturePost(source model.RedditPost) database.Art
return item return item
} }
func (rc *RedditClient) convertTextPost(source model.RedditPost) database.Article { func (rc *RedditClient) convertTextPost(source models.RedditPost) database.Article {
var item = database.Article{ var item = database.Article{
Sourceid: rc.record.ID, Sourceid: rc.record.ID,
Tags: "a", Tags: "a",
@ -164,7 +164,7 @@ func (rc *RedditClient) convertTextPost(source model.RedditPost) database.Articl
return item return item
} }
func (rc *RedditClient) convertVideoPost(source model.RedditPost) database.Article { func (rc *RedditClient) convertVideoPost(source models.RedditPost) database.Article {
var item = database.Article{ var item = database.Article{
Sourceid: rc.record.ID, Sourceid: rc.record.ID,
Tags: "a", Tags: "a",
@ -180,7 +180,7 @@ func (rc *RedditClient) convertVideoPost(source model.RedditPost) database.Artic
} }
// This post is nothing more then a redirect to another location. // This post is nothing more then a redirect to another location.
func (rc *RedditClient) convertRedirectPost(source model.RedditPost) database.Article { func (rc *RedditClient) convertRedirectPost(source models.RedditPost) database.Article {
var item = database.Article{ var item = database.Article{
Sourceid: rc.record.ID, Sourceid: rc.record.ID,
Tags: "a", Tags: "a",

View File

@ -4,16 +4,16 @@ import (
"fmt" "fmt"
"log" "log"
"github.com/jtom38/newsbot/collector/domain/model" "github.com/jtom38/newsbot/collector/domain/models"
"github.com/jtom38/newsbot/collector/services/cache" "github.com/jtom38/newsbot/collector/services/cache"
"github.com/mmcdole/gofeed" "github.com/mmcdole/gofeed"
) )
type rssClient struct { type rssClient struct {
SourceRecord model.Sources SourceRecord models.Sources
} }
func NewRssClient(sourceRecord model.Sources) rssClient { func NewRssClient(sourceRecord models.Sources) rssClient {
client := rssClient{ client := rssClient{
SourceRecord: sourceRecord, SourceRecord: sourceRecord,
} }

View File

@ -3,11 +3,11 @@ package input_test
import ( import (
"testing" "testing"
"github.com/jtom38/newsbot/collector/domain/model" "github.com/jtom38/newsbot/collector/domain/models"
"github.com/jtom38/newsbot/collector/services/input" "github.com/jtom38/newsbot/collector/services/input"
) )
var rssRecord = model.Sources{ var rssRecord = models.Sources{
ID: 1, ID: 1,
Name: "ArsTechnica", Name: "ArsTechnica",
Url: "https://feeds.arstechnica.com/arstechnica/index", Url: "https://feeds.arstechnica.com/arstechnica/index",