add news events

This commit is contained in:
nochill 2023-10-11 16:31:52 +07:00
parent f8ecd20220
commit 2f05a2f8e7
13 changed files with 278 additions and 5 deletions

6
api/api.go Normal file
View File

@ -0,0 +1,6 @@
package api
type BaseGetListRequest struct {
Page int32 `form:"page" binding:"required,min=1"`
PageSize int32 `form:"page_size" binding:"required,min=5"`
}

71
api/news_event.go Normal file
View File

@ -0,0 +1,71 @@
package api
import (
"database/sql"
"net/http"
db "git.nochill.in/nochill/hiling_go/db/sqlc"
"github.com/gin-gonic/gin"
)
type CreateNewsEventsReq struct {
Url string `json:"url" binding:"required,url"`
Title string `json:"title" binding:"required"`
Source string `json:"source"`
Description string `json:"description"`
SubmittedBy int32 `json:"submitted_by" binding:"required,numeric"`
}
func (server *Server) createNews(ctx *gin.Context) {
var req CreateNewsEventsReq
if err := ctx.ShouldBindJSON(&req); err != nil {
ctx.JSON(http.StatusBadRequest, ValidationErrorResponse(err))
return
}
err := server.Store.CreateNewsEvents(ctx, db.CreateNewsEventsParams{
Title: req.Title,
Url: req.Url,
Source: req.Source,
Description: sql.NullString{Valid: len(req.Description) > 0, String: req.Description},
SubmittedBy: req.SubmittedBy,
})
if err != nil {
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to save news/evnts"))
return
}
ctx.Writer.WriteHeader(http.StatusCreated)
}
type GetNewsEventsListReq struct {
BaseGetListRequest
Approved int8 `form:"is_with_approval"`
}
func (server *Server) GetNewsEventsList(ctx *gin.Context) {
var req GetNewsEventsListReq
isWithApproval := ""
if err := ctx.ShouldBindQuery(&req); err != nil {
ctx.JSON(http.StatusBadRequest, ValidationErrorResponse(err))
return
}
if req.Approved > 0 {
isWithApproval = "WHERE approved_by IS NOT NULL"
}
news, err := server.Store.GetNewsEventsList(ctx, db.GetNewsEventsListParams{
Limit: req.PageSize,
Offset: (req.Page - 1) * req.PageSize,
IsWithApproved: isWithApproval,
})
if err != nil {
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to get news / events"))
return
}
ctx.JSON(http.StatusOK, news)
}

View File

@ -43,8 +43,6 @@ func (server *Server) getRoutes() {
AllowMethods: []string{"POST", "PUT", "GET", "DELETE", "PATCH"},
}))
// router.Use(CORSMiddleware())
router.POST("/user/signup", server.createUser)
router.POST("/user/login", server.login)
router.POST("/user/logout", server.logout)
@ -64,6 +62,9 @@ func (server *Server) getRoutes() {
//IMAGES
router.GET("/images/location", server.getAllImagesByLocation)
// NEWS / EVENTS
router.GET("/news-events", server.GetNewsEventsList)
// REQUIRE AUTH TOKEN
authRoutes := router.Use(authMiddleware(server.TokenMaker))
authRoutes.POST("/review/location", server.createReview)
@ -72,6 +73,7 @@ func (server *Server) getRoutes() {
authRoutes.GET("/user/profile", server.getUserStats)
authRoutes.PATCH("/user/avatar", server.updateUserAvatar)
authRoutes.DELETE("/user/avatar", server.removeAvatar)
authRoutes.POST("/news-events", server.createNews)
server.Router = router
}

View File

@ -0,0 +1 @@
DROP TABLE IF EXISTS news_events;

View File

@ -0,0 +1,13 @@
CREATE TABLE news_events (
"id" serial primary key not null,
"title" varchar not null,
"url" varchar not null,
"source" varchar not null,
"thumbnail" varchar,
"description" text,
"is_deleted" boolean not null default(false),
"submitted_by" int references "users"("id") not null,
"approved_by" int references "users"("id"),
"created_at" timestamp default(now()),
"updated_at" timestamp default(now())
)

View File

@ -108,6 +108,20 @@ func (mr *MockStoreMockRecorder) CreateLocationTx(arg0, arg1 interface{}) *gomoc
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateLocationTx", reflect.TypeOf((*MockStore)(nil).CreateLocationTx), arg0, arg1)
}
// CreateNewsEvents mocks base method.
func (m *MockStore) CreateNewsEvents(arg0 context.Context, arg1 db.CreateNewsEventsParams) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CreateNewsEvents", arg0, arg1)
ret0, _ := ret[0].(error)
return ret0
}
// CreateNewsEvents indicates an expected call of CreateNewsEvents.
func (mr *MockStoreMockRecorder) CreateNewsEvents(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateNewsEvents", reflect.TypeOf((*MockStore)(nil).CreateNewsEvents), arg0, arg1)
}
// CreateReview mocks base method.
func (m *MockStore) CreateReview(arg0 context.Context, arg1 db.CreateReviewParams) (db.Review, error) {
m.ctrl.T.Helper()
@ -303,6 +317,21 @@ func (mr *MockStoreMockRecorder) GetLocationTag(arg0, arg1 interface{}) *gomock.
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLocationTag", reflect.TypeOf((*MockStore)(nil).GetLocationTag), arg0, arg1)
}
// GetNewsEventsList mocks base method.
func (m *MockStore) GetNewsEventsList(arg0 context.Context, arg1 db.GetNewsEventsListParams) ([]db.NewsEventRow, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetNewsEventsList", arg0, arg1)
ret0, _ := ret[0].([]db.NewsEventRow)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetNewsEventsList indicates an expected call of GetNewsEventsList.
func (mr *MockStoreMockRecorder) GetNewsEventsList(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNewsEventsList", reflect.TypeOf((*MockStore)(nil).GetNewsEventsList), arg0, arg1)
}
// GetSession mocks base method.
func (m *MockStore) GetSession(arg0 context.Context, arg1 int32) (db.UserSession, error) {
m.ctrl.T.Helper()

View File

@ -0,0 +1,8 @@
-- name: CreateNewsEvents :exec
INSERT INTO news_events(
title,
url,
source,
description,
submitted_by
) VALUES ( $1, $2, $3, $4, $5);

View File

@ -46,9 +46,9 @@ func (q *Queries) GetTopListLocations(ctx context.Context, arg GetTopListLocatio
FROM(
SELECT
*,
(SELECT 5 * 4 + COALESCE(critic_score, 0) * COALESCE(critic_count, 0) / 5 + COALESCE(critic_count, 0)) as critic_bayes,
(SELECT 50 + COALESCE(user_score, 0) * COALESCE(user_count, 0) / 50 + COALESCE(user_count, 0)) as user_bayes,
((SELECT 50 + COALESCE(user_score, 0) * COALESCE(user_count, 0) / 50 + COALESCE(user_count, 0)) + (SELECT 5 * 4 + COALESCE(critic_score, 0) * COALESCE(critic_count, 0) / 5 + COALESCE(critic_count, 0)) ) / 2 as avg_bayes
(SELECT 5 * 5 + COALESCE(critic_score, 0) * COALESCE(critic_count, 0) / 5 + COALESCE(critic_count, 0)) as critic_bayes,
(SELECT 5 * 5 + COALESCE(user_score, 0) * COALESCE(user_count, 0) / 5 + COALESCE(user_count, 0)) as user_bayes,
((SELECT 5 * 5 + COALESCE(user_score, 0) * COALESCE(user_count, 0) / 50 + COALESCE(user_count, 0)) + (SELECT 5 * 5 + COALESCE(critic_score, 0) * COALESCE(critic_count, 0) / 5 + COALESCE(critic_count, 0)) ) / 2 as avg_bayes
FROM (
SELECT

View File

@ -206,6 +206,20 @@ type LocationImage struct {
UpdatedAt sql.NullTime `json:"updated_at"`
}
type NewsEvent struct {
ID int32 `json:"id"`
Title string `json:"title"`
Url string `json:"url"`
Source string `json:"source"`
Thumbnail sql.NullString `json:"thumbnail"`
Description sql.NullString `json:"description"`
IsDeleted bool `json:"is_deleted"`
SubmittedBy int32 `json:"submitted_by"`
ApprovedBy sql.NullInt32 `json:"approved_by"`
CreatedAt sql.NullTime `json:"created_at"`
UpdatedAt sql.NullTime `json:"updated_at"`
}
type Province struct {
ID int32 `json:"id"`
ProvinceName string `json:"province_name"`

87
db/sqlc/news_events.go Normal file
View File

@ -0,0 +1,87 @@
package db
import (
"context"
"fmt"
"time"
)
type GetNewsEventsListParams struct {
Limit int32
Offset int32
IsWithApproved string
}
type NewsEventRow struct {
ID int32 `json:"id"`
Title string `json:"title"`
Url string `json:"url"`
Source string `json:"source"`
Thumbnail string `json:"thumbnail"`
Description string `json:"description"`
IsDeleted bool `json:"is_deleted"`
SubmittedBy string `json:"submitted_by"`
ApprovedBy int32 `json:"approved_by"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func (q *Queries) GetNewsEventsList(ctx context.Context, arg GetNewsEventsListParams) ([]NewsEventRow, error) {
getNewsEventsListQ := fmt.Sprintf(`
SELECT
n.id,
n.title,
n.url,
n.source,
COALESCE(n.thumbnail, '') as thumbnail,
COALESCE(n.description, '') as description,
n.is_deleted,
u.username as submitted_by,
COALESCE(n.approved_by, 0) as approved_by,
n.created_at,
n.updated_at
FROM news_events n
JOIN users u ON n.submitted_by = u.id
%s
ORDER BY n.created_at DESC
LIMIT $1
OFFSET $2
`, arg.IsWithApproved)
rows, err := q.db.QueryContext(ctx, getNewsEventsListQ, arg.Limit, arg.Offset)
if err != nil {
return nil, err
}
defer rows.Close()
items := []NewsEventRow{}
for rows.Next() {
var i NewsEventRow
if err := rows.Scan(
&i.ID,
&i.Title,
&i.Url,
&i.Source,
&i.Thumbnail,
&i.Description,
&i.IsDeleted,
&i.SubmittedBy,
&i.ApprovedBy,
&i.CreatedAt,
&i.UpdatedAt,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}

View File

@ -0,0 +1,40 @@
// Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.20.0
// source: news_events.sql
package db
import (
"context"
"database/sql"
)
const createNewsEvents = `-- name: CreateNewsEvents :exec
INSERT INTO news_events(
title,
url,
source,
description,
submitted_by
) VALUES ( $1, $2, $3, $4, $5)
`
type CreateNewsEventsParams struct {
Title string `json:"title"`
Url string `json:"url"`
Source string `json:"source"`
Description sql.NullString `json:"description"`
SubmittedBy int32 `json:"submitted_by"`
}
func (q *Queries) CreateNewsEvents(ctx context.Context, arg CreateNewsEventsParams) error {
_, err := q.db.ExecContext(ctx, createNewsEvents,
arg.Title,
arg.Url,
arg.Source,
arg.Description,
arg.SubmittedBy,
)
return err
}

View File

@ -12,6 +12,7 @@ import (
type Querier interface {
AddFollowUser(ctx context.Context, arg AddFollowUserParams) error
CheckIfReviewExists(ctx context.Context, arg CheckIfReviewExistsParams) (int64, error)
CreateNewsEvents(ctx context.Context, arg CreateNewsEventsParams) error
CreateSession(ctx context.Context, arg CreateSessionParams) (UserSession, error)
CreateUser(ctx context.Context, arg CreateUserParams) (User, error)
GetCountImageByLocation(ctx context.Context, imageOf int32) (int64, error)

View File

@ -21,6 +21,7 @@ type Store interface {
CreateLocation(ctx context.Context, arg CreateLocationParams) (int32, error)
CreateImage(ctx context.Context, arg []CreateImageParams) error
CreateLocationTx(ctx context.Context, arg CreateLocationTxParams) error
GetNewsEventsList(ctx context.Context, arg GetNewsEventsListParams) ([]NewsEventRow, error)
}
type SQLStore struct {