add get recent locations rating

This commit is contained in:
nochill 2023-09-14 22:51:01 +07:00
parent 06d1284bbe
commit d1e5e7deb7
10 changed files with 151 additions and 10 deletions

View File

@ -110,6 +110,26 @@ func (server *Server) getListLocations(ctx *gin.Context) {
ctx.JSON(http.StatusOK, locations)
}
type getListRecentLocationsWithRatingsReq struct {
Page int32 `form:"page_size" binding:"required,min=1"`
}
func (server *Server) getListRecentLocationsWithRatings(ctx *gin.Context) {
var req getListRecentLocationsWithRatingsReq
if err := ctx.ShouldBindQuery(&req); err != nil {
ctx.JSON(http.StatusBadRequest, ValidationErrorResponse(err))
return
}
locations, err := server.Store.GetListRecentLocationsWithRatings(ctx, req.Page)
if err != nil {
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong"))
return
}
ctx.JSON(http.StatusOK, locations)
}
type getLocationReq struct {
ID int32 `uri:"location_id" binding:"required"`
}

19
api/middleware.go Normal file
View File

@ -0,0 +1,19 @@
package api
import "github.com/gin-gonic/gin"
func CORSMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT")
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(204)
return
}
c.Next()
}
}

View File

@ -35,10 +35,13 @@ func NewServer(config util.Config, store db.Store) (*Server, error) {
func (server *Server) getRoutes() {
router := gin.Default()
router.Use(CORSMiddleware())
router.POST("/user/signup", server.createUser)
// LOCATION
router.POST("/locations", server.createLocation)
router.GET("/recent-locations/ratings", server.getListRecentLocationsWithRatings)
router.GET("/locations", server.getListLocations)
router.GET("/location/:location_id", server.getLocation)

View File

@ -8,3 +8,6 @@ id,address,name,submitted_by,thumbnail,regency_id,google_maps_link,approved_by
7#Jl. P. Mangkubumi No.72A Cokrodiningratan Kec. Jetis#Hotel Tentrem Yogyakarta#1#https://cdn.discordapp.com/attachments/743422487882104837/1150987888553623653/image.png#3471#https://www.google.com/maps?q=Hotel+Tentrem+Yogyakarta&source=lmns&entry=mc&bih=1115&biw=2124&hl=en-US&sa=X&ved=2ahUKEwjjl-HHiKSBAxUu5jgGHTU3BiwQ0pQJKAJ6BAgBEAY#1
8#Moluo Kec.Kwandang#Pulau Saronde Gorontalo#1#https://dynamic-media-cdn.tripadvisor.com/media/photo-o/0d/ec/58/21/saronde-island-a-place.jpg?w=700&h=-1&s=1#7505#https://www.google.com/maps/place/Pulau+Saronde+Gorontalo/@0.9263376,122.8613201,17z/data=!3m1!4b1!4m6!3m5!1s0x32795bf34dff4467:0xa8beb2a832ae8176!8m2!3d0.9263376!4d122.863895!16s%2Fg%2F11l241cc1d?hl=id&entry=ttu#1
9#Dusun Katiet Desa Bosua Kecamatan Sipora#Pantai Katiet#1#https://dynamic-media-cdn.tripadvisor.com/media/photo-o/1a/d7/fe/f4/mentawai-islands.jpg?w=500&h=-1&s=1#1301#https://www.google.com/maps/place/Katiet,+Bosua,+Sipora+Selatan,+Mentawai+Islands+Regency,+West+Sumatra/@-2.375793,99.848187,15z/data=!3m1!4b1!4m6!3m5!1s0x2fd27efa8363912f:0x8c9c19bd76cba179!8m2!3d-2.375793!4d99.848187!16s%2Fg%2F1tcwz0mt?hl=en-US&entry=ttu#1
10#Pulau Padar#Pulau Padar#2#https://dynamic-media-cdn.tripadvisor.com/media/photo-o/18/4e/11/f3/padar-island.jpg?w=800&h=-1&s=1#5315#https://www.google.com/maps/place/Pulau+Padar/@-8.6554183,119.570686,14.83z/data=!4m6!3m5!1s0x2db4f84ff6cd01ab:0xf7e6fd33b692a898!8m2!3d-8.6489909!4d119.5832593!16s%2Fg%2F121d16wd?entry=ttu#1
11#Jl. Ahmad Yani 18, Kel. Kelimutu, Kec. Ende Tengah#Sari rasa#3#https://cdn.discordapp.com/attachments/743422487882104837/1151903310295601172/image.png#5315#https://www.google.com/maps/place/Rumah+Makan+Cha+Cha/@-8.6153697,120.4652533,21z/data=!4m6!3m5!1s0x2db37469e54c0dd3:0x36ac988c726ed544!8m2!3d-8.6153631!4d120.465275!16s%2Fg%2F11bwpclp91?entry=ttu#1
12#Danau Sentani#Danau Sentani#3#https://dynamic-media-cdn.tripadvisor.com/media/photo-o/1a/c7/e8/17/20190630-143008-largejpg.jpg?w=800&h=-1&s=1#9403#https://www.google.com/maps/place/Danau+Sentani/@-2.639976,140.3889748,12.46z/data=!4m7!3m6!1s0x686cf33c72660fbf:0x7e5c1e7d20d930d7!4b1!8m2!3d-2.6133004!4d140.518734!16s%2Fm%2F0tkjf6v?entry=ttu#1
1 id,address,name,submitted_by,thumbnail,regency_id,google_maps_link,approved_by
8 7#Jl. P. Mangkubumi No.72A Cokrodiningratan Kec. Jetis#Hotel Tentrem Yogyakarta#1#https://cdn.discordapp.com/attachments/743422487882104837/1150987888553623653/image.png#3471#https://www.google.com/maps?q=Hotel+Tentrem+Yogyakarta&source=lmns&entry=mc&bih=1115&biw=2124&hl=en-US&sa=X&ved=2ahUKEwjjl-HHiKSBAxUu5jgGHTU3BiwQ0pQJKAJ6BAgBEAY#1
9 8#Moluo Kec.Kwandang#Pulau Saronde Gorontalo#1#https://dynamic-media-cdn.tripadvisor.com/media/photo-o/0d/ec/58/21/saronde-island-a-place.jpg?w=700&h=-1&s=1#7505#https://www.google.com/maps/place/Pulau+Saronde+Gorontalo/@0.9263376,122.8613201,17z/data=!3m1!4b1!4m6!3m5!1s0x32795bf34dff4467:0xa8beb2a832ae8176!8m2!3d0.9263376!4d122.863895!16s%2Fg%2F11l241cc1d?hl=id&entry=ttu#1
10 9#Dusun Katiet Desa Bosua Kecamatan Sipora#Pantai Katiet#1#https://dynamic-media-cdn.tripadvisor.com/media/photo-o/1a/d7/fe/f4/mentawai-islands.jpg?w=500&h=-1&s=1#1301#https://www.google.com/maps/place/Katiet,+Bosua,+Sipora+Selatan,+Mentawai+Islands+Regency,+West+Sumatra/@-2.375793,99.848187,15z/data=!3m1!4b1!4m6!3m5!1s0x2fd27efa8363912f:0x8c9c19bd76cba179!8m2!3d-2.375793!4d99.848187!16s%2Fg%2F1tcwz0mt?hl=en-US&entry=ttu#1
11 10#Pulau Padar#Pulau Padar#2#https://dynamic-media-cdn.tripadvisor.com/media/photo-o/18/4e/11/f3/padar-island.jpg?w=800&h=-1&s=1#5315#https://www.google.com/maps/place/Pulau+Padar/@-8.6554183,119.570686,14.83z/data=!4m6!3m5!1s0x2db4f84ff6cd01ab:0xf7e6fd33b692a898!8m2!3d-8.6489909!4d119.5832593!16s%2Fg%2F121d16wd?entry=ttu#1
12 11#Jl. Ahmad Yani 18, Kel. Kelimutu, Kec. Ende Tengah#Sari rasa#3#https://cdn.discordapp.com/attachments/743422487882104837/1151903310295601172/image.png#5315#https://www.google.com/maps/place/Rumah+Makan+Cha+Cha/@-8.6153697,120.4652533,21z/data=!4m6!3m5!1s0x2db37469e54c0dd3:0x36ac988c726ed544!8m2!3d-8.6153631!4d120.465275!16s%2Fg%2F11bwpclp91?entry=ttu#1
13 12#Danau Sentani#Danau Sentani#3#https://dynamic-media-cdn.tripadvisor.com/media/photo-o/1a/c7/e8/17/20190630-143008-largejpg.jpg?w=800&h=-1&s=1#9403#https://www.google.com/maps/place/Danau+Sentani/@-2.639976,140.3889748,12.46z/data=!4m7!3m6!1s0x686cf33c72660fbf:0x7e5c1e7d20d930d7!4b1!8m2!3d-2.6133004!4d140.518734!16s%2Fm%2F0tkjf6v?entry=ttu#1

View File

@ -1,2 +1,4 @@
id,username,password
1,user123,password
2,critics,password
3,plebs,password
1 id username password
2 1 user123 password
3 2 critics password
4 3 plebs password

View File

@ -79,6 +79,21 @@ func (mr *MockStoreMockRecorder) GetListLocations(arg0 interface{}) *gomock.Call
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetListLocations", reflect.TypeOf((*MockStore)(nil).GetListLocations), arg0)
}
// GetListRecentLocationsWithRatings mocks base method.
func (m *MockStore) GetListRecentLocationsWithRatings(arg0 context.Context, arg1 int32) ([]db.GetListRecentLocationsWithRatingsRow, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetListRecentLocationsWithRatings", arg0, arg1)
ret0, _ := ret[0].([]db.GetListRecentLocationsWithRatingsRow)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetListRecentLocationsWithRatings indicates an expected call of GetListRecentLocationsWithRatings.
func (mr *MockStoreMockRecorder) GetListRecentLocationsWithRatings(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetListRecentLocationsWithRatings", reflect.TypeOf((*MockStore)(nil).GetListRecentLocationsWithRatings), arg0, arg1)
}
// GetLocation mocks base method.
func (m *MockStore) GetLocation(arg0 context.Context, arg1 int32) (db.Location, error) {
m.ctrl.T.Helper()

View File

@ -1,6 +1,23 @@
-- name: GetListLocations :many
SELECT * FROM locations;
-- name: GetListRecentLocationsWithRatings :many
SELECT
l.id,
name,
thumbnail,
re.regency_name,
(SELECT COALESCE(SUM(score), -1) from reviews re where re.is_from_critic = true and re.location_id = l.id) as critic_score,
(SELECT COUNT(id) from reviews re where re.is_from_critic = true and re.location_id = l.id) as critic_count,
(SELECT COALESCE(SUM(score), -1) from reviews re where re.is_from_critic = false and re.location_id = l.id) as user_score,
(SELECT COUNT(id) from reviews re where re.is_from_critic = false and re.location_id = l.id) as user_count
FROM locations l
JOIN regencies re on re.id = l.regency_id
WHERE approved_by IS NOT NULL
ORDER BY l.created_at ASC
LIMIT $1;
-- name: GetLocation :one
SELECT * FROM locations
WHERE id = $1;

View File

@ -82,6 +82,66 @@ func (q *Queries) GetListLocations(ctx context.Context) ([]Location, error) {
return items, nil
}
const getListRecentLocationsWithRatings = `-- name: GetListRecentLocationsWithRatings :many
SELECT
l.id,
name,
thumbnail,
re.regency_name,
(SELECT COALESCE(SUM(score), -1) from reviews re where re.is_from_critic = true and re.location_id = l.id) as critic_score,
(SELECT COUNT(id) from reviews re where re.is_from_critic = true and re.location_id = l.id) as critic_count,
(SELECT COALESCE(SUM(score), -1) from reviews re where re.is_from_critic = false and re.location_id = l.id) as user_score,
(SELECT COUNT(id) from reviews re where re.is_from_critic = false and re.location_id = l.id) as user_count
FROM locations l
JOIN regencies re on re.id = l.regency_id
WHERE approved_by IS NOT NULL
ORDER BY l.created_at ASC
LIMIT $1
`
type GetListRecentLocationsWithRatingsRow struct {
ID int32 `json:"id"`
Name string `json:"name"`
Thumbnail sql.NullString `json:"thumbnail"`
RegencyName sql.NullString `json:"regency_name"`
CriticScore interface{} `json:"critic_score"`
CriticCount int64 `json:"critic_count"`
UserScore interface{} `json:"user_score"`
UserCount int64 `json:"user_count"`
}
func (q *Queries) GetListRecentLocationsWithRatings(ctx context.Context, limit int32) ([]GetListRecentLocationsWithRatingsRow, error) {
rows, err := q.db.QueryContext(ctx, getListRecentLocationsWithRatings, limit)
if err != nil {
return nil, err
}
defer rows.Close()
items := []GetListRecentLocationsWithRatingsRow{}
for rows.Next() {
var i GetListRecentLocationsWithRatingsRow
if err := rows.Scan(
&i.ID,
&i.Name,
&i.Thumbnail,
&i.RegencyName,
&i.CriticScore,
&i.CriticCount,
&i.UserScore,
&i.UserCount,
); 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
}
const getLocation = `-- name: GetLocation :one
SELECT id, address, name, google_maps_link, submitted_by, total_visited, thumbnail, regency_id, is_deleted, created_at, updated_at, approved_by, approved_at FROM locations
WHERE id = $1

View File

@ -173,14 +173,15 @@ type Region struct {
}
type Review struct {
ID int32 `json:"id"`
SubmittedBy int32 `json:"submitted_by"`
Comments string `json:"comments"`
Score int16 `json:"score"`
IsHided sql.NullBool `json:"is_hided"`
LocationID int32 `json:"location_id"`
CreatedAt sql.NullTime `json:"created_at"`
UpdatedAt sql.NullTime `json:"updated_at"`
ID int32 `json:"id"`
SubmittedBy int32 `json:"submitted_by"`
Comments string `json:"comments"`
Score int16 `json:"score"`
IsFromCritic bool `json:"is_from_critic"`
IsHided sql.NullBool `json:"is_hided"`
LocationID int32 `json:"location_id"`
CreatedAt sql.NullTime `json:"created_at"`
UpdatedAt sql.NullTime `json:"updated_at"`
}
type Tag struct {

View File

@ -12,6 +12,7 @@ type Querier interface {
CreateLocation(ctx context.Context, arg CreateLocationParams) error
CreateUser(ctx context.Context, arg CreateUserParams) (User, error)
GetListLocations(ctx context.Context) ([]Location, error)
GetListRecentLocationsWithRatings(ctx context.Context, limit int32) ([]GetListRecentLocationsWithRatingsRow, error)
GetLocation(ctx context.Context, id int32) (Location, error)
UpdatePassword(ctx context.Context, arg UpdatePasswordParams) error
UpdateUser(ctx context.Context, arg UpdateUserParams) (User, error)