Compare commits
14 Commits
675abcdb18
...
2f05a2f8e7
Author | SHA1 | Date | |
---|---|---|---|
2f05a2f8e7 | |||
f8ecd20220 | |||
eb9774cce5 | |||
b31bc76a24 | |||
a2d4dfb282 | |||
8dbebd9f17 | |||
641831a3ae | |||
5a5d157882 | |||
110cbfeec0 | |||
47e692bf30 | |||
6cd35d7ffa | |||
ddca119e45 | |||
198e311407 | |||
1f055e92d4 |
10
TODO
10
TODO
@ -1,3 +1,9 @@
|
||||
- COMMENTS FLOW
|
||||
- USER PROFILE FLOW
|
||||
- BEST LOCATIONS FILTER ( MIN REVIEWS AND TAGS)
|
||||
- ADMIN FLOW (SUBMISSION, USER REPORTS)
|
||||
- BEST LOCATIONS FILTER ( MIN REVIEWS AND TAGS)
|
||||
- PAGINATION
|
||||
- OPEN GRAPH PARSER
|
||||
- USERS FEEED
|
||||
|
||||
## IF THERES TIME LEFT
|
||||
- STORIES FLOW
|
6
api/api.go
Normal file
6
api/api.go
Normal 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"`
|
||||
}
|
@ -11,7 +11,6 @@ import (
|
||||
db "git.nochill.in/nochill/hiling_go/db/sqlc"
|
||||
"git.nochill.in/nochill/hiling_go/util"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/lib/pq"
|
||||
ysqlc "github.com/yiplee/sqlc"
|
||||
)
|
||||
|
||||
@ -20,64 +19,60 @@ type createLocationReq struct {
|
||||
Name string `form:"name" binding:"required"`
|
||||
SubmittedBy int32 `form:"submitted_by" binding:"required,number"`
|
||||
RegencyID int16 `form:"regency_id" binding:"required,number"`
|
||||
LocationType string `form:"location_type" binding:"required"`
|
||||
GoogleMapsLink string `form:"google_maps_link"`
|
||||
}
|
||||
|
||||
func (server *Server) createLocation(ctx *gin.Context) {
|
||||
var req createLocationReq
|
||||
var imgPath string
|
||||
|
||||
var thumbnail, _ = ctx.FormFile("thumbnail")
|
||||
var tempImg []db.CreateImageParams
|
||||
|
||||
if err := ctx.Bind(&req); err != nil {
|
||||
ctx.JSON(http.StatusBadRequest, ValidationErrorResponse(err))
|
||||
return
|
||||
}
|
||||
|
||||
if thumbnail != nil {
|
||||
img := thumbnail
|
||||
fileExt := filepath.Ext(img.Filename)
|
||||
now := time.Now()
|
||||
dir := fmt.Sprintf("public/upload/images/locations/%s/thumbnail", req.Name)
|
||||
osFilename := fmt.Sprintf("%s%s%s", util.RandomString(5), fmt.Sprintf("%v", now.Unix()), fileExt)
|
||||
imgPath = fmt.Sprintf("%s%s", dir, osFilename)
|
||||
form, _ := ctx.MultipartForm()
|
||||
thumbnails := form.File["thumbnail"]
|
||||
|
||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||
os.Mkdir(dir, 0775)
|
||||
}
|
||||
if len(thumbnails) > 0 {
|
||||
for _, img := range thumbnails {
|
||||
fileExt := filepath.Ext(img.Filename)
|
||||
now := time.Now()
|
||||
dir := fmt.Sprintf("public/upload/images/locations/%s/thumbnail", req.Name)
|
||||
osFilename := fmt.Sprintf("%s%s%s", util.RandomString(5), fmt.Sprintf("%v", now.Unix()), fileExt)
|
||||
imgPath = fmt.Sprintf("%s/%s", dir, osFilename)
|
||||
|
||||
if err := ctx.SaveUploadedFile(img, imgPath); err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Error while try to save thumbnail image"))
|
||||
return
|
||||
}
|
||||
}
|
||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||
os.Mkdir(dir, 0775)
|
||||
}
|
||||
|
||||
arg := db.CreateLocationParams{
|
||||
Address: req.Address,
|
||||
Name: req.Name,
|
||||
SubmittedBy: req.SubmittedBy,
|
||||
RegencyID: req.RegencyID,
|
||||
GoogleMapsLink: sql.NullString{Valid: len(req.GoogleMapsLink) > 0, String: req.GoogleMapsLink},
|
||||
}
|
||||
|
||||
err := server.Store.CreateLocation(ctx, arg)
|
||||
|
||||
if err != nil {
|
||||
if pqErr, ok := err.(*pq.Error); ok {
|
||||
switch pqErr.Code.Name() {
|
||||
case "foreign_key_violation", "unique_violation":
|
||||
if pqErr.Constraint == "locations_regency_id_fkey" {
|
||||
ctx.JSON(http.StatusConflict, ErrorResponse(err, fmt.Sprintf("Failed to submit location, there's no regency with id: %d", arg.RegencyID)))
|
||||
return
|
||||
}
|
||||
if pqErr.Constraint == "submitted_by_fkey" {
|
||||
ctx.JSON(http.StatusConflict, ErrorResponse(err, fmt.Sprintf("Failed to submit location, there's no user with id: %d", arg.SubmittedBy)))
|
||||
return
|
||||
}
|
||||
if err := ctx.SaveUploadedFile(img, imgPath); err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Error while try to save thumbnail image"))
|
||||
return
|
||||
}
|
||||
}
|
||||
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong"))
|
||||
return
|
||||
|
||||
arg := db.CreateLocationTxParams{
|
||||
Address: req.Address,
|
||||
Name: req.Name,
|
||||
LocationType: db.LocationType(req.LocationType),
|
||||
SubmittedBy: req.SubmittedBy,
|
||||
RegencyID: req.RegencyID,
|
||||
IsDeleted: false,
|
||||
ApprovedBy: sql.NullInt32{Int32: 0, Valid: false},
|
||||
GoogleMapsLink: sql.NullString{Valid: len(req.GoogleMapsLink) > 0, String: req.GoogleMapsLink},
|
||||
Thumbnail: tempImg,
|
||||
}
|
||||
|
||||
err := server.Store.CreateLocationTx(ctx, arg)
|
||||
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to save Location"))
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ctx.Writer.WriteHeader(http.StatusOK)
|
||||
|
71
api/news_event.go
Normal file
71
api/news_event.go
Normal 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)
|
||||
}
|
37
api/region.go
Normal file
37
api/region.go
Normal file
@ -0,0 +1,37 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func (server *Server) getListRegions(ctx *gin.Context) {
|
||||
regions, err := server.Store.GetListRegions(ctx)
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to get regions"))
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, regions)
|
||||
}
|
||||
|
||||
func (server *Server) getListProvinces(ctx *gin.Context) {
|
||||
provinces, err := server.Store.GetListProvinces(ctx)
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to get regions"))
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, provinces)
|
||||
}
|
||||
|
||||
func (server *Server) getListRegencies(ctx *gin.Context) {
|
||||
regencies, err := server.Store.GetListRegencies(ctx)
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to get regions"))
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, regencies)
|
||||
}
|
@ -39,15 +39,16 @@ func (server *Server) getRoutes() {
|
||||
router.Use(cors.New(cors.Config{
|
||||
AllowOrigins: []string{"http://localhost:5173"},
|
||||
AllowCredentials: true,
|
||||
AllowHeaders: []string{"Content-Type", "Content-Length", "Accept-Encoding", "X-CSRF-Token", "Authorization", "accept", "origin", "Cache-Control", "X-Requested-With"},
|
||||
AllowHeaders: []string{"Content-Type", "Content-Length", "Accept-Encoding", "Authorization", "accept", "origin", "Cache-Control", "X-Requested-With", "X-XSRF-TOKEN"},
|
||||
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)
|
||||
router.GET("/regions", server.getListRegions)
|
||||
router.GET("/region/provinces", server.getListProvinces)
|
||||
router.GET("/region/regencies", server.getListRegencies)
|
||||
|
||||
// LOCATION
|
||||
router.POST("/locations", server.createLocation)
|
||||
@ -61,10 +62,18 @@ 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)
|
||||
authRoutes.PATCH("/user", server.updateUser)
|
||||
authRoutes.GET("/user/review/location/:location_id", server.getUserReviewByLocation)
|
||||
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
|
||||
}
|
||||
|
@ -157,7 +157,7 @@ func randomLocation() db.Location {
|
||||
TotalVisited: sql.NullInt32{Valid: true, Int32: int32(util.RandomInt(0, 32))},
|
||||
Thumbnail: sql.NullString{Valid: false, String: ""},
|
||||
RegencyID: 1305,
|
||||
IsDeleted: sql.NullBool{Valid: true, Bool: false},
|
||||
IsDeleted: false,
|
||||
CreatedAt: sql.NullTime{Valid: true, Time: time.Now()},
|
||||
UpdatedAt: sql.NullTime{Valid: true, Time: time.Now()},
|
||||
ApprovedBy: sql.NullInt32{Valid: true, Int32: 1},
|
||||
|
136
api/user.go
136
api/user.go
@ -2,13 +2,19 @@ package api
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
db "git.nochill.in/nochill/hiling_go/db/sqlc"
|
||||
"git.nochill.in/nochill/hiling_go/util"
|
||||
"git.nochill.in/nochill/hiling_go/util/token"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/lib/pq"
|
||||
"github.com/sqlc-dev/pqtype"
|
||||
)
|
||||
|
||||
type createUserRequest struct {
|
||||
@ -124,6 +130,134 @@ func (server *Server) createUser(ctx *gin.Context) {
|
||||
ctx.JSON(http.StatusOK, res)
|
||||
}
|
||||
|
||||
func (server *Server) getUserStats(ctx *gin.Context) {
|
||||
var scoreDistribution []map[string]int8
|
||||
authPayload := ctx.MustGet(authorizationPayloadKey).(*token.Payload)
|
||||
|
||||
userStats, err := server.Store.GetUserStats(ctx, int32(authPayload.UserID))
|
||||
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to get user stats"))
|
||||
return
|
||||
}
|
||||
|
||||
err = json.Unmarshal(userStats.ScoresDistribution, &scoreDistribution)
|
||||
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong whilet try to parse score distribution"))
|
||||
return
|
||||
}
|
||||
|
||||
var userReviews []map[string]any
|
||||
if userStats.Reviews != nil {
|
||||
err = json.Unmarshal(userStats.Reviews, &userReviews)
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something wrong while try to parse user reviwes stats"))
|
||||
return
|
||||
}
|
||||
} else {
|
||||
userReviews = make([]map[string]any, 0)
|
||||
}
|
||||
ctx.JSON(http.StatusOK, gin.H{
|
||||
"reviews": userReviews,
|
||||
"scores_distribution": scoreDistribution,
|
||||
"user_stats": userStats,
|
||||
})
|
||||
}
|
||||
|
||||
type UpdateUserRequest struct {
|
||||
About string `json:"about"`
|
||||
Website string `json:"website"`
|
||||
SocialMedia []map[string]string `json:"social_media"`
|
||||
}
|
||||
|
||||
func (server *Server) updateUser(ctx *gin.Context) {
|
||||
var req UpdateUserRequest
|
||||
|
||||
if err := ctx.ShouldBindJSON(&req); err != nil {
|
||||
ctx.JSON(http.StatusBadRequest, ValidationErrorResponse(err))
|
||||
return
|
||||
}
|
||||
|
||||
authPayload := ctx.MustGet(authorizationPayloadKey).(*token.Payload)
|
||||
|
||||
fmt.Println(req.About)
|
||||
|
||||
social, _ := json.Marshal(req.SocialMedia)
|
||||
socialArr := pqtype.NullRawMessage{
|
||||
RawMessage: social,
|
||||
Valid: len(req.SocialMedia) > 0,
|
||||
}
|
||||
|
||||
user, err := server.Store.UpdateUser(ctx, db.UpdateUserParams{
|
||||
About: sql.NullString{String: req.About, Valid: true},
|
||||
SocialMedia: socialArr,
|
||||
Website: sql.NullString{String: req.Website, Valid: len(req.Website) > 0},
|
||||
ID: int32(authPayload.UserID),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to update user"))
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, user)
|
||||
}
|
||||
|
||||
func (server *Server) updateUserAvatar(ctx *gin.Context) {
|
||||
file, err := ctx.FormFile("file")
|
||||
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to parse file"))
|
||||
return
|
||||
}
|
||||
|
||||
authPayload := ctx.MustGet(authorizationPayloadKey).(*token.Payload)
|
||||
|
||||
fileExt := filepath.Ext(file.Filename)
|
||||
now := time.Now()
|
||||
dir := fmt.Sprintf("public/upload/images/user/%d/avatar", authPayload.UserID)
|
||||
osFilename := fmt.Sprintf("%s%s%s", util.RandomString(5), fmt.Sprintf("%v", now.Unix()), fileExt)
|
||||
imgPath := fmt.Sprintf("%s/%s", dir, osFilename)
|
||||
|
||||
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
||||
os.Mkdir(dir, 0775)
|
||||
}
|
||||
|
||||
if err := ctx.SaveUploadedFile(file, imgPath); err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Error while try to save thumbnail image"))
|
||||
return
|
||||
}
|
||||
|
||||
url, err := server.Store.UpdateAvatar(ctx, db.UpdateAvatarParams{
|
||||
ID: int32(authPayload.UserID),
|
||||
AvatarPicture: sql.NullString{Valid: true, String: imgPath},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to save image to database"))
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, gin.H{"image_url": url.String})
|
||||
}
|
||||
|
||||
func (server *Server) removeAvatar(ctx *gin.Context) {
|
||||
authPayload := ctx.MustGet(authorizationPayloadKey).(*token.Payload)
|
||||
|
||||
_, err := server.Store.UpdateAvatar(ctx, db.UpdateAvatarParams{
|
||||
AvatarPicture: sql.NullString{String: "", Valid: false},
|
||||
ID: int32(authPayload.UserID),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to update user avatar"))
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Writer.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func (server *Server) login(ctx *gin.Context) {
|
||||
var req createUserRequest
|
||||
|
||||
@ -135,7 +269,7 @@ func (server *Server) login(ctx *gin.Context) {
|
||||
user, err := server.Store.GetUser(ctx, req.Username)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
ctx.JSON(http.StatusNotFound, ErrorResponse(err, ""))
|
||||
ctx.JSON(http.StatusNotFound, ErrorResponse(err, "User not found"))
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong whlie try to get user"))
|
||||
|
@ -1,13 +1,13 @@
|
||||
id,address,name,submitted_by,thumbnail,regency_id,google_maps_link,approved_by
|
||||
1#Jalan Raya Beside the bridge Ubud#Murni’s Warung#1#https://cdn.discordapp.com/attachments/743422487882104837/1150972798320267304/image.png#5104#https://www.google.com/maps/place/Murni's+Warung/@-8.5048696,115.2553417,19z/data=!4m6!3m5!1s0x2dd23d3e0ffaa071:0xf2fa69b4cb211e41!8m2!3d-8.5051184!4d115.2547196!16s%2Fg%2F1tdsmcq7?entry=ttu#1
|
||||
2#Jl.Taman Wijaya Kusuma Ps. Baru Kecamatan Sawah Besar#Masjid Istiqlal#1#https://dynamic-media-cdn.tripadvisor.com/media/photo-o/04/11/31/33/istiqlal-mosque-mesjid.jpg?w=500&h=-1&s=1#3173#https://www.google.com/maps/place/Masjid+Istiqlal/@-6.1703155,106.8308434,19z/data=!4m15!1m7!3m6!1s0x2e69f5ce68b5e01d:0xcafaf042d5840c6c!2sMasjid+Istiqlal!8m2!3d-6.17017!4d106.83139!16zL20vMDRzam1q!3m6!1s0x2e69f5ce68b5e01d:0xcafaf042d5840c6c!8m2!3d-6.17017!4d106.83139!15sCg9tYXNqaWQgaXN0aXFsYWxaESIPbWFzamlkIGlzdGlxbGFskgEGbW9zcXVl4AEA!16zL20vMDRzam1q?entry=ttu#1
|
||||
3#Jl. Mayjend Sungkono no. 89#Hotel Ciputra World Surabaya#1#https://lh5.googleusercontent.com/p/AF1QipOvHDO-M6riRoqBrWU3MskhwL_bue8JmN9faq7Q=w500-h500-k-no#3578#https://www.google.com/maps/place/Ciputra+World+Hotel+Surabaya/@-7.2923061,112.7191552,15z/data=!4m2!3m1!1s0x0:0x736a9c49dcc2ac42?sa=X&ved=2ahUKEwjJlbf8gqSBAxWtzzgGHUIkBFYQ_BJ6BAgVEAA&ved=2ahUKEwjJlbf8gqSBAxWtzzgGHUIkBFYQ_BJ6BAgjEAc#1
|
||||
4#Jl. Taman Safari No.101 . B Cibeureum Kec. Cisarua#Club Huis#1#https://media-cdn.tripadvisor.com/media/photo-o/0d/6a/5d/63/our-peaceful-backyard.jpg#3201#https://www.google.com/maps/place/Club+Huis/@-6.7027857,106.9453741,17z/data=!3m1!4b1!4m6!3m5!1s0x2e69b679d7a09e01:0xf9fc2df396f09977!8m2!3d-6.7027857!4d106.947949!16s%2Fg%2F11c57lh8ky?entry=ttu#1
|
||||
5#Desa Tambakrejo Kecamatan Sumbermanjing Wetan#Pulau Sempu#1#https://dynamic-media-cdn.tripadvisor.com/media/photo-o/11/3b/06/a5/pulau-sempu.jpg?w=500&h=-1&s=1#3507#https://www.google.com/maps/place/Pulau+Sempu/@-8.446621,112.6746143,14z/data=!3m1!4b1!4m6!3m5!1s0x2dd60120edbc901f:0x8efd89687a308993!8m2!3d-8.4428564!4d112.6973355!16s%2Fm%2F0r8k540?entry=ttu#1
|
||||
6#Jl. Bukit Golf I BSD Sektor VI Lengkong Karya Kec. Serpong Utara#Damai Indah Golf#1#https://lh3.googleusercontent.com/p/AF1QipN5Z-0J6vIfIO6gqPO0z5HDWlNKqp0t816XIJPS=s680-w500-h500#3674#https://www.google.com/maps/place/Damai+Indah+Golf+-+BSD+Course/@-6.2815644,106.6496566,17z/data=!3m1!4b1!4m6!3m5!1s0x2e69fb152983d973:0x89e58e219f8b93ef!8m2!3d-6.2815644!4d106.6522315!16s%2Fg%2F11c54c9r94?entry=ttu#1
|
||||
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
|
||||
id,location_type,address,name,submitted_by,thumbnail,regency_id,google_maps_link,approved_by,is_deleted
|
||||
1#culinary#Jalan Raya Beside the bridge Ubud#Murni’s Warung#1#https://cdn.discordapp.com/attachments/743422487882104837/1150972798320267304/image.png#5104#https://www.google.com/maps/place/Murni's+Warung/@-8.5048696,115.2553417,19z/data=!4m6!3m5!1s0x2dd23d3e0ffaa071:0xf2fa69b4cb211e41!8m2!3d-8.5051184!4d115.2547196!16s%2Fg%2F1tdsmcq7?entry=ttu#1#false
|
||||
2#other#Jl.Taman Wijaya Kusuma Ps. Baru Kecamatan Sawah Besar#Masjid Istiqlal#1#https://dynamic-media-cdn.tripadvisor.com/media/photo-o/04/11/31/33/istiqlal-mosque-mesjid.jpg?w=500&h=-1&s=1#3173#https://www.google.com/maps/place/Masjid+Istiqlal/@-6.1703155,106.8308434,19z/data=!4m15!1m7!3m6!1s0x2e69f5ce68b5e01d:0xcafaf042d5840c6c!2sMasjid+Istiqlal!8m2!3d-6.17017!4d106.83139!16zL20vMDRzam1q!3m6!1s0x2e69f5ce68b5e01d:0xcafaf042d5840c6c!8m2!3d-6.17017!4d106.83139!15sCg9tYXNqaWQgaXN0aXFsYWxaESIPbWFzamlkIGlzdGlxbGFskgEGbW9zcXVl4AEA!16zL20vMDRzam1q?entry=ttu#1#false
|
||||
3#other#Jl. Mayjend Sungkono no. 89#Hotel Ciputra World Surabaya#1#https://lh5.googleusercontent.com/p/AF1QipOvHDO-M6riRoqBrWU3MskhwL_bue8JmN9faq7Q=w500-h500-k-no#3578#https://www.google.com/maps/place/Ciputra+World+Hotel+Surabaya/@-7.2923061,112.7191552,15z/data=!4m2!3m1!1s0x0:0x736a9c49dcc2ac42?sa=X&ved=2ahUKEwjJlbf8gqSBAxWtzzgGHUIkBFYQ_BJ6BAgVEAA&ved=2ahUKEwjJlbf8gqSBAxWtzzgGHUIkBFYQ_BJ6BAgjEAc#1#false
|
||||
4#amusement park#Jl. Taman Safari No.101 . B Cibeureum Kec. Cisarua#Club Huis#1#https://media-cdn.tripadvisor.com/media/photo-o/0d/6a/5d/63/our-peaceful-backyard.jpg#3201#https://www.google.com/maps/place/Club+Huis/@-6.7027857,106.9453741,17z/data=!3m1!4b1!4m6!3m5!1s0x2e69b679d7a09e01:0xf9fc2df396f09977!8m2!3d-6.7027857!4d106.947949!16s%2Fg%2F11c57lh8ky?entry=ttu#1#false
|
||||
5#other#Desa Tambakrejo Kecamatan Sumbermanjing Wetan#Pulau Sempu#1#https://dynamic-media-cdn.tripadvisor.com/media/photo-o/11/3b/06/a5/pulau-sempu.jpg?w=500&h=-1&s=1#3507#https://www.google.com/maps/place/Pulau+Sempu/@-8.446621,112.6746143,14z/data=!3m1!4b1!4m6!3m5!1s0x2dd60120edbc901f:0x8efd89687a308993!8m2!3d-8.4428564!4d112.6973355!16s%2Fm%2F0r8k540?entry=ttu#1#false
|
||||
6#other#Jl. Bukit Golf I BSD Sektor VI Lengkong Karya Kec. Serpong Utara#Damai Indah Golf#1#https://lh3.googleusercontent.com/p/AF1QipN5Z-0J6vIfIO6gqPO0z5HDWlNKqp0t816XIJPS=s680-w500-h500#3674#https://www.google.com/maps/place/Damai+Indah+Golf+-+BSD+Course/@-6.2815644,106.6496566,17z/data=!3m1!4b1!4m6!3m5!1s0x2e69fb152983d973:0x89e58e219f8b93ef!8m2!3d-6.2815644!4d106.6522315!16s%2Fg%2F11c54c9r94?entry=ttu#1#false
|
||||
7#other#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#false
|
||||
8#other#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#false
|
||||
9#beach#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#false
|
||||
10#other#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#false
|
||||
11#culinary#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#false
|
||||
12#other#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#false
|
|
@ -1,4 +1,4 @@
|
||||
id,username,password
|
||||
1,user123,password
|
||||
2,critics,password
|
||||
3,plebs,password
|
||||
1,user123,$2a$10$5jbejbMXCs7noqPBt1v9K.TCjnqDp.GtTkMiRRRwU8/t78GiGjY9a
|
||||
2,critics,$2a$10$5jbejbMXCs7noqPBt1v9K.TCjnqDp.GtTkMiRRRwU8/t78GiGjY9a
|
||||
3,plebs,$2a$10$5jbejbMXCs7noqPBt1v9K.TCjnqDp.GtTkMiRRRwU8/t78GiGjY9a
|
|
@ -12,4 +12,5 @@ DROP TABLE IF EXISTS users;
|
||||
|
||||
|
||||
DROP TYPE IF EXISTS user_reports_type;
|
||||
DROP TYPE IF EXISTS comment_type;
|
||||
DROP TYPE IF EXISTS comment_type;
|
||||
DROP TYPE IF EXISTS location_type;
|
@ -53,14 +53,14 @@ CREATE TABLE user_reports(
|
||||
|
||||
CREATE TABLE regions(
|
||||
"id" serial primary key not null,
|
||||
"region_name" varchar,
|
||||
"region_name" varchar not null,
|
||||
"created_at" timestamp default(now()),
|
||||
"updated_at" timestamp default(now())
|
||||
);
|
||||
|
||||
CREATE TABLE provinces(
|
||||
"id" serial primary key not null,
|
||||
"province_name" varchar,
|
||||
"province_name" varchar not null,
|
||||
"region_id" smallint references "regions"("id") not null,
|
||||
"created_at" timestamp default(now()),
|
||||
"updated_at" timestamp default(now())
|
||||
@ -68,22 +68,31 @@ CREATE TABLE provinces(
|
||||
|
||||
CREATE TABLE regencies(
|
||||
"id" serial primary key not null,
|
||||
"regency_name" varchar,
|
||||
"regency_name" varchar not null,
|
||||
"province_id" smallint references "provinces"("id") not null,
|
||||
"created_at" timestamp default(now()),
|
||||
"updated_at" timestamp default(now())
|
||||
);
|
||||
|
||||
CREATE TYPE location_type AS ENUM(
|
||||
'beach',
|
||||
'amusement park',
|
||||
'culinary',
|
||||
'hiking / camping',
|
||||
'other'
|
||||
);
|
||||
|
||||
CREATE TABLE locations(
|
||||
"id" serial primary key not null,
|
||||
"address" varchar not null,
|
||||
"name" varchar not null,
|
||||
"google_maps_link" varchar,
|
||||
"location_type" location_type not null,
|
||||
"submitted_by" integer references "users"("id") not null,
|
||||
"total_visited" integer,
|
||||
"thumbnail" varchar,
|
||||
"regency_id" smallint references "regencies"("id") not null,
|
||||
"is_deleted" boolean,
|
||||
"is_deleted" boolean not null,
|
||||
"created_at" timestamp default(now()),
|
||||
"updated_at" timestamp default(now())
|
||||
);
|
||||
@ -112,6 +121,7 @@ CREATE TABLE reviews (
|
||||
"comments" text not null,
|
||||
"score" smallint not null,
|
||||
"is_from_critic" boolean not null,
|
||||
"cost_approx" integer,
|
||||
"is_hided" boolean not null default(false), -- if comments violate TOS just hide the reviews
|
||||
"location_id" integer references "locations"("id") not null,
|
||||
"created_at" timestamp default(now()),
|
||||
|
1
db/migrations/000007_create_user_follow_table.down.sql
Normal file
1
db/migrations/000007_create_user_follow_table.down.sql
Normal file
@ -0,0 +1 @@
|
||||
DROP TABLE IF EXISTS user_follow;
|
7
db/migrations/000007_create_user_follow_table.up.sql
Normal file
7
db/migrations/000007_create_user_follow_table.up.sql
Normal file
@ -0,0 +1,7 @@
|
||||
CREATE TABLE user_follow(
|
||||
id serial primary key not null,
|
||||
follower_id integer references "users"("id") not null,
|
||||
followee_id integer references "users"("id") not null,
|
||||
created_at timestamp default (now()) not null,
|
||||
updated_at timestamp default (now()) not null
|
||||
)
|
@ -0,0 +1 @@
|
||||
ALTER TABLE users DROP COLUMN about;
|
1
db/migrations/000008_alter_users_add_about_column.up.sql
Normal file
1
db/migrations/000008_alter_users_add_about_column.up.sql
Normal file
@ -0,0 +1 @@
|
||||
ALTER TABLE users ADD COLUMN about text;
|
@ -0,0 +1 @@
|
||||
ALTER TABLE users DROP COLUMN IF EXISTS website;
|
@ -0,0 +1 @@
|
||||
ALTER TABLE users ADD COLUMN website varchar;
|
1
db/migrations/000010_create_news_event_table.down.sql
Normal file
1
db/migrations/000010_create_news_event_table.down.sql
Normal file
@ -0,0 +1 @@
|
||||
DROP TABLE IF EXISTS news_events;
|
13
db/migrations/000010_create_news_event_table.up.sql
Normal file
13
db/migrations/000010_create_news_event_table.up.sql
Normal 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())
|
||||
)
|
186
db/mock/store.go
186
db/mock/store.go
@ -6,6 +6,7 @@ package mockdb
|
||||
|
||||
import (
|
||||
context "context"
|
||||
sql "database/sql"
|
||||
reflect "reflect"
|
||||
|
||||
db "git.nochill.in/nochill/hiling_go/db/sqlc"
|
||||
@ -35,6 +36,20 @@ func (m *MockStore) EXPECT() *MockStoreMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// AddFollowUser mocks base method.
|
||||
func (m *MockStore) AddFollowUser(arg0 context.Context, arg1 db.AddFollowUserParams) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "AddFollowUser", arg0, arg1)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// AddFollowUser indicates an expected call of AddFollowUser.
|
||||
func (mr *MockStoreMockRecorder) AddFollowUser(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddFollowUser", reflect.TypeOf((*MockStore)(nil).AddFollowUser), arg0, arg1)
|
||||
}
|
||||
|
||||
// CheckIfReviewExists mocks base method.
|
||||
func (m *MockStore) CheckIfReviewExists(arg0 context.Context, arg1 db.CheckIfReviewExistsParams) (int64, error) {
|
||||
m.ctrl.T.Helper()
|
||||
@ -50,20 +65,63 @@ func (mr *MockStoreMockRecorder) CheckIfReviewExists(arg0, arg1 interface{}) *go
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CheckIfReviewExists", reflect.TypeOf((*MockStore)(nil).CheckIfReviewExists), arg0, arg1)
|
||||
}
|
||||
|
||||
// CreateLocation mocks base method.
|
||||
func (m *MockStore) CreateLocation(arg0 context.Context, arg1 db.CreateLocationParams) error {
|
||||
// CreateImage mocks base method.
|
||||
func (m *MockStore) CreateImage(arg0 context.Context, arg1 []db.CreateImageParams) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "CreateLocation", arg0, arg1)
|
||||
ret := m.ctrl.Call(m, "CreateImage", arg0, arg1)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// CreateImage indicates an expected call of CreateImage.
|
||||
func (mr *MockStoreMockRecorder) CreateImage(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateImage", reflect.TypeOf((*MockStore)(nil).CreateImage), arg0, arg1)
|
||||
}
|
||||
|
||||
// CreateLocation mocks base method.
|
||||
func (m *MockStore) CreateLocation(arg0 context.Context, arg1 db.CreateLocationParams) (int32, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "CreateLocation", arg0, arg1)
|
||||
ret0, _ := ret[0].(int32)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// CreateLocation indicates an expected call of CreateLocation.
|
||||
func (mr *MockStoreMockRecorder) CreateLocation(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateLocation", reflect.TypeOf((*MockStore)(nil).CreateLocation), arg0, arg1)
|
||||
}
|
||||
|
||||
// CreateLocationTx mocks base method.
|
||||
func (m *MockStore) CreateLocationTx(arg0 context.Context, arg1 db.CreateLocationTxParams) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "CreateLocationTx", arg0, arg1)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// CreateLocationTx indicates an expected call of CreateLocationTx.
|
||||
func (mr *MockStoreMockRecorder) CreateLocationTx(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
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()
|
||||
@ -169,6 +227,21 @@ func (mr *MockStoreMockRecorder) GetListLocations(arg0 interface{}) *gomock.Call
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetListLocations", reflect.TypeOf((*MockStore)(nil).GetListLocations), arg0)
|
||||
}
|
||||
|
||||
// GetListProvinces mocks base method.
|
||||
func (m *MockStore) GetListProvinces(arg0 context.Context) ([]db.GetListProvincesRow, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetListProvinces", arg0)
|
||||
ret0, _ := ret[0].([]db.GetListProvincesRow)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetListProvinces indicates an expected call of GetListProvinces.
|
||||
func (mr *MockStoreMockRecorder) GetListProvinces(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetListProvinces", reflect.TypeOf((*MockStore)(nil).GetListProvinces), arg0)
|
||||
}
|
||||
|
||||
// GetListRecentLocationsWithRatings mocks base method.
|
||||
func (m *MockStore) GetListRecentLocationsWithRatings(arg0 context.Context, arg1 int32) ([]db.GetListRecentLocationsWithRatingsRow, error) {
|
||||
m.ctrl.T.Helper()
|
||||
@ -184,6 +257,36 @@ func (mr *MockStoreMockRecorder) GetListRecentLocationsWithRatings(arg0, arg1 in
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetListRecentLocationsWithRatings", reflect.TypeOf((*MockStore)(nil).GetListRecentLocationsWithRatings), arg0, arg1)
|
||||
}
|
||||
|
||||
// GetListRegencies mocks base method.
|
||||
func (m *MockStore) GetListRegencies(arg0 context.Context) ([]db.GetListRegenciesRow, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetListRegencies", arg0)
|
||||
ret0, _ := ret[0].([]db.GetListRegenciesRow)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetListRegencies indicates an expected call of GetListRegencies.
|
||||
func (mr *MockStoreMockRecorder) GetListRegencies(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetListRegencies", reflect.TypeOf((*MockStore)(nil).GetListRegencies), arg0)
|
||||
}
|
||||
|
||||
// GetListRegions mocks base method.
|
||||
func (m *MockStore) GetListRegions(arg0 context.Context) ([]db.GetListRegionsRow, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetListRegions", arg0)
|
||||
ret0, _ := ret[0].([]db.GetListRegionsRow)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetListRegions indicates an expected call of GetListRegions.
|
||||
func (mr *MockStoreMockRecorder) GetListRegions(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetListRegions", reflect.TypeOf((*MockStore)(nil).GetListRegions), arg0)
|
||||
}
|
||||
|
||||
// GetLocation mocks base method.
|
||||
func (m *MockStore) GetLocation(arg0 context.Context, arg1 int32) (db.GetLocationRow, error) {
|
||||
m.ctrl.T.Helper()
|
||||
@ -214,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()
|
||||
@ -274,6 +392,64 @@ func (mr *MockStoreMockRecorder) GetUserReviewByLocation(arg0, arg1 interface{})
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserReviewByLocation", reflect.TypeOf((*MockStore)(nil).GetUserReviewByLocation), arg0, arg1)
|
||||
}
|
||||
|
||||
// GetUserStats mocks base method.
|
||||
func (m *MockStore) GetUserStats(arg0 context.Context, arg1 int32) (db.GetUserStatsRow, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetUserStats", arg0, arg1)
|
||||
ret0, _ := ret[0].(db.GetUserStatsRow)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetUserStats indicates an expected call of GetUserStats.
|
||||
func (mr *MockStoreMockRecorder) GetUserStats(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserStats", reflect.TypeOf((*MockStore)(nil).GetUserStats), arg0, arg1)
|
||||
}
|
||||
|
||||
// RemoveFollowUser mocks base method.
|
||||
func (m *MockStore) RemoveFollowUser(arg0 context.Context, arg1 db.RemoveFollowUserParams) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "RemoveFollowUser", arg0, arg1)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// RemoveFollowUser indicates an expected call of RemoveFollowUser.
|
||||
func (mr *MockStoreMockRecorder) RemoveFollowUser(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveFollowUser", reflect.TypeOf((*MockStore)(nil).RemoveFollowUser), arg0, arg1)
|
||||
}
|
||||
|
||||
// UpdateAvatar mocks base method.
|
||||
func (m *MockStore) UpdateAvatar(arg0 context.Context, arg1 db.UpdateAvatarParams) (sql.NullString, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "UpdateAvatar", arg0, arg1)
|
||||
ret0, _ := ret[0].(sql.NullString)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// UpdateAvatar indicates an expected call of UpdateAvatar.
|
||||
func (mr *MockStoreMockRecorder) UpdateAvatar(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateAvatar", reflect.TypeOf((*MockStore)(nil).UpdateAvatar), arg0, arg1)
|
||||
}
|
||||
|
||||
// UpdateLocationThumbnail mocks base method.
|
||||
func (m *MockStore) UpdateLocationThumbnail(arg0 context.Context, arg1 db.UpdateLocationThumbnailParams) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "UpdateLocationThumbnail", arg0, arg1)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// UpdateLocationThumbnail indicates an expected call of UpdateLocationThumbnail.
|
||||
func (mr *MockStoreMockRecorder) UpdateLocationThumbnail(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateLocationThumbnail", reflect.TypeOf((*MockStore)(nil).UpdateLocationThumbnail), arg0, arg1)
|
||||
}
|
||||
|
||||
// UpdatePassword mocks base method.
|
||||
func (m *MockStore) UpdatePassword(arg0 context.Context, arg1 db.UpdatePasswordParams) error {
|
||||
m.ctrl.T.Helper()
|
||||
@ -289,10 +465,10 @@ func (mr *MockStoreMockRecorder) UpdatePassword(arg0, arg1 interface{}) *gomock.
|
||||
}
|
||||
|
||||
// UpdateUser mocks base method.
|
||||
func (m *MockStore) UpdateUser(arg0 context.Context, arg1 db.UpdateUserParams) (db.User, error) {
|
||||
func (m *MockStore) UpdateUser(arg0 context.Context, arg1 db.UpdateUserParams) (db.UpdateUserRow, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "UpdateUser", arg0, arg1)
|
||||
ret0, _ := ret[0].(db.User)
|
||||
ret0, _ := ret[0].(db.UpdateUserRow)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
10
db/queries/follow.sql
Normal file
10
db/queries/follow.sql
Normal file
@ -0,0 +1,10 @@
|
||||
-- name: AddFollowUser :exec
|
||||
INSERT INTO user_follow (
|
||||
follower_id,
|
||||
followee_id
|
||||
) VALUES($1, $2);
|
||||
|
||||
-- name: RemoveFollowUser :exec
|
||||
DELETE FROM
|
||||
user_follow
|
||||
WHERE follower_id = $1 AND followee_id = $2;
|
@ -19,17 +19,6 @@ WHERE approved_by IS NOT NULL
|
||||
ORDER BY l.created_at ASC
|
||||
LIMIT $1;
|
||||
|
||||
-- name: CreateLocation :exec
|
||||
INSERT INTO locations(
|
||||
address,
|
||||
name,
|
||||
submitted_by,
|
||||
regency_id,
|
||||
google_maps_link
|
||||
) values (
|
||||
$1, $2, $3, $4, $5
|
||||
);
|
||||
|
||||
-- name: GetLocationTag :many
|
||||
SELECT
|
||||
name
|
||||
@ -38,4 +27,10 @@ WHERE tags_type = 'location'
|
||||
AND
|
||||
target_id = $1
|
||||
AND
|
||||
approved_by IS NOT NULL;
|
||||
approved_by IS NOT NULL;
|
||||
|
||||
|
||||
-- name: UpdateLocationThumbnail :exec
|
||||
UPDATE locations
|
||||
SET thumbnail = $1
|
||||
WHERE id = $2;
|
8
db/queries/news_events.sql
Normal file
8
db/queries/news_events.sql
Normal file
@ -0,0 +1,8 @@
|
||||
-- name: CreateNewsEvents :exec
|
||||
INSERT INTO news_events(
|
||||
title,
|
||||
url,
|
||||
source,
|
||||
description,
|
||||
submitted_by
|
||||
) VALUES ( $1, $2, $3, $4, $5);
|
6
db/queries/provinces.sql
Normal file
6
db/queries/provinces.sql
Normal file
@ -0,0 +1,6 @@
|
||||
-- name: GetListProvinces :many
|
||||
SELECT
|
||||
id,
|
||||
province_name,
|
||||
region_id
|
||||
FROM PROVINCES;
|
6
db/queries/regencies.sql
Normal file
6
db/queries/regencies.sql
Normal file
@ -0,0 +1,6 @@
|
||||
-- name: GetListRegencies :many
|
||||
SELECT
|
||||
id,
|
||||
regency_name,
|
||||
province_id
|
||||
FROM regencies;
|
5
db/queries/regions.sql
Normal file
5
db/queries/regions.sql
Normal file
@ -0,0 +1,5 @@
|
||||
-- name: GetListRegions :many
|
||||
SELECT
|
||||
id,
|
||||
region_name
|
||||
FROM regions;
|
@ -5,17 +5,13 @@ INSERT INTO users (
|
||||
) VALUES ($1, $2)
|
||||
RETURNING *;
|
||||
|
||||
-- name: UpdateUser :one
|
||||
UPDATE users
|
||||
SET
|
||||
email = COALESCE(sqlc.narg(email), email),
|
||||
username = COALESCE(sqlc.narg(username), username),
|
||||
avatar_picture = COALESCE(sqlc.narg(avatar_picture), avatar_picture)
|
||||
WHERE
|
||||
id = sqlc.arg(id)
|
||||
RETURNING *;
|
||||
|
||||
-- name: UpdatePassword :exec
|
||||
UPDATE users
|
||||
SET password = $1
|
||||
WHERE id = $2;
|
||||
|
||||
-- name: UpdateAvatar :one
|
||||
UPDATE users
|
||||
SET avatar_picture = $1
|
||||
WHERE id = $2
|
||||
RETURNING avatar_picture;
|
||||
|
43
db/sqlc/follow.sql.go
Normal file
43
db/sqlc/follow.sql.go
Normal file
@ -0,0 +1,43 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.20.0
|
||||
// source: follow.sql
|
||||
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
const addFollowUser = `-- name: AddFollowUser :exec
|
||||
INSERT INTO user_follow (
|
||||
follower_id,
|
||||
followee_id
|
||||
) VALUES($1, $2)
|
||||
`
|
||||
|
||||
type AddFollowUserParams struct {
|
||||
FollowerID int32 `json:"follower_id"`
|
||||
FolloweeID int32 `json:"followee_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) AddFollowUser(ctx context.Context, arg AddFollowUserParams) error {
|
||||
_, err := q.db.ExecContext(ctx, addFollowUser, arg.FollowerID, arg.FolloweeID)
|
||||
return err
|
||||
}
|
||||
|
||||
const removeFollowUser = `-- name: RemoveFollowUser :exec
|
||||
DELETE FROM
|
||||
user_follow
|
||||
WHERE follower_id = $1 AND followee_id = $2
|
||||
`
|
||||
|
||||
type RemoveFollowUserParams struct {
|
||||
FollowerID int32 `json:"follower_id"`
|
||||
FolloweeID int32 `json:"followee_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) RemoveFollowUser(ctx context.Context, arg RemoveFollowUserParams) error {
|
||||
_, err := q.db.ExecContext(ctx, removeFollowUser, arg.FollowerID, arg.FolloweeID)
|
||||
return err
|
||||
}
|
@ -2,7 +2,10 @@ package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"git.nochill.in/nochill/hiling_go/util"
|
||||
)
|
||||
|
||||
type GetImagesByLocationParams struct {
|
||||
@ -59,3 +62,38 @@ func (q *Queries) GetImagesByLocation(ctx context.Context, arg GetImagesByLocati
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
type CreateImageParams struct {
|
||||
ImageUrl string `json:"url"`
|
||||
UploadedBy int32 `json:"uploaded_by"`
|
||||
ImageType string `json:"image_type"`
|
||||
ImageOf int32 `json:"image_of"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateImage(ctx context.Context, arg []CreateImageParams) error {
|
||||
|
||||
values := []interface{}{}
|
||||
|
||||
queryStr := ` INSERT INTO
|
||||
images(image_url, uploaded_by, image_type, image_of)
|
||||
VALUES
|
||||
`
|
||||
|
||||
for _, row := range arg {
|
||||
queryStr += "(?, ?, ?, ?),"
|
||||
values = append(values, row.ImageUrl, row.UploadedBy, row.ImageType, row.ImageOf)
|
||||
}
|
||||
|
||||
queryStr = strings.TrimSuffix(queryStr, ",")
|
||||
|
||||
// Replacing ? with $n for postgres
|
||||
queryStr = util.ReplaceSQL(queryStr, "?")
|
||||
|
||||
// prepare the statement
|
||||
stmt, _ := q.db.PrepareContext(ctx, queryStr)
|
||||
|
||||
// format all vals at once
|
||||
_, err := stmt.ExecContext(ctx, values...)
|
||||
|
||||
return err
|
||||
}
|
||||
|
@ -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
|
||||
@ -171,3 +171,53 @@ func (q *Queries) GetLocation(ctx context.Context, location_id int32) (GetLocati
|
||||
|
||||
return i, err
|
||||
}
|
||||
|
||||
const createLocation = `-- name: CreateLocation :exec
|
||||
INSERT INTO locations(
|
||||
address,
|
||||
name,
|
||||
submitted_by,
|
||||
location_type,
|
||||
regency_id,
|
||||
google_maps_link,
|
||||
approved_by,
|
||||
is_deleted
|
||||
) values (
|
||||
$1, $2, $3, $4, $5, $6, $7, $8
|
||||
)
|
||||
RETURNING id
|
||||
`
|
||||
|
||||
type CreateLocationParams struct {
|
||||
Address string `json:"address"`
|
||||
Name string `json:"name"`
|
||||
SubmittedBy int32 `json:"submitted_by"`
|
||||
LocationType LocationType `json:"location_type"`
|
||||
RegencyID int16 `json:"regency_id"`
|
||||
GoogleMapsLink sql.NullString `json:"google_maps_link"`
|
||||
IsDeleted bool `json:"is_deleted"`
|
||||
ApprovedBy sql.NullInt32 `json:"approved_by"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateLocation(ctx context.Context, arg CreateLocationParams) (int32, error) {
|
||||
row := q.db.QueryRowContext(ctx, createLocation,
|
||||
arg.Address,
|
||||
arg.Name,
|
||||
arg.SubmittedBy,
|
||||
arg.LocationType,
|
||||
arg.RegencyID,
|
||||
arg.GoogleMapsLink,
|
||||
arg.ApprovedBy,
|
||||
arg.IsDeleted,
|
||||
)
|
||||
|
||||
var i int32
|
||||
|
||||
err := row.Scan(
|
||||
&i,
|
||||
)
|
||||
|
||||
fmt.Println(i)
|
||||
|
||||
return i, err
|
||||
}
|
||||
|
@ -10,39 +10,8 @@ import (
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
const createLocation = `-- name: CreateLocation :exec
|
||||
INSERT INTO locations(
|
||||
address,
|
||||
name,
|
||||
submitted_by,
|
||||
regency_id,
|
||||
google_maps_link
|
||||
) values (
|
||||
$1, $2, $3, $4, $5
|
||||
)
|
||||
`
|
||||
|
||||
type CreateLocationParams struct {
|
||||
Address string `json:"address"`
|
||||
Name string `json:"name"`
|
||||
SubmittedBy int32 `json:"submitted_by"`
|
||||
RegencyID int16 `json:"regency_id"`
|
||||
GoogleMapsLink sql.NullString `json:"google_maps_link"`
|
||||
}
|
||||
|
||||
func (q *Queries) CreateLocation(ctx context.Context, arg CreateLocationParams) error {
|
||||
_, err := q.db.ExecContext(ctx, createLocation,
|
||||
arg.Address,
|
||||
arg.Name,
|
||||
arg.SubmittedBy,
|
||||
arg.RegencyID,
|
||||
arg.GoogleMapsLink,
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
const getListLocations = `-- name: GetListLocations :many
|
||||
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
|
||||
SELECT id, address, name, google_maps_link, location_type, submitted_by, total_visited, thumbnail, regency_id, is_deleted, created_at, updated_at, approved_by, approved_at FROM locations
|
||||
`
|
||||
|
||||
func (q *Queries) GetListLocations(ctx context.Context) ([]Location, error) {
|
||||
@ -59,6 +28,7 @@ func (q *Queries) GetListLocations(ctx context.Context) ([]Location, error) {
|
||||
&i.Address,
|
||||
&i.Name,
|
||||
&i.GoogleMapsLink,
|
||||
&i.LocationType,
|
||||
&i.SubmittedBy,
|
||||
&i.TotalVisited,
|
||||
&i.Thumbnail,
|
||||
@ -179,3 +149,19 @@ func (q *Queries) GetLocationTag(ctx context.Context, targetID int32) ([]string,
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const updateLocationThumbnail = `-- name: UpdateLocationThumbnail :exec
|
||||
UPDATE locations
|
||||
SET thumbnail = $1
|
||||
WHERE id = $2
|
||||
`
|
||||
|
||||
type UpdateLocationThumbnailParams struct {
|
||||
Thumbnail sql.NullString `json:"thumbnail"`
|
||||
ID int32 `json:"id"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateLocationThumbnail(ctx context.Context, arg UpdateLocationThumbnailParams) error {
|
||||
_, err := q.db.ExecContext(ctx, updateLocationThumbnail, arg.Thumbnail, arg.ID)
|
||||
return err
|
||||
}
|
||||
|
@ -57,6 +57,51 @@ func (ns NullCommentType) Value() (driver.Value, error) {
|
||||
return string(ns.CommentType), nil
|
||||
}
|
||||
|
||||
type LocationType string
|
||||
|
||||
const (
|
||||
LocationTypeBeach LocationType = "beach"
|
||||
LocationTypeAmusementpark LocationType = "amusement park"
|
||||
LocationTypeCulinary LocationType = "culinary"
|
||||
LocationTypeHikingCamping LocationType = "hiking / camping"
|
||||
LocationTypeOther LocationType = "other"
|
||||
)
|
||||
|
||||
func (e *LocationType) Scan(src interface{}) error {
|
||||
switch s := src.(type) {
|
||||
case []byte:
|
||||
*e = LocationType(s)
|
||||
case string:
|
||||
*e = LocationType(s)
|
||||
default:
|
||||
return fmt.Errorf("unsupported scan type for LocationType: %T", src)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type NullLocationType struct {
|
||||
LocationType LocationType `json:"location_type"`
|
||||
Valid bool `json:"valid"` // Valid is true if LocationType is not NULL
|
||||
}
|
||||
|
||||
// Scan implements the Scanner interface.
|
||||
func (ns *NullLocationType) Scan(value interface{}) error {
|
||||
if value == nil {
|
||||
ns.LocationType, ns.Valid = "", false
|
||||
return nil
|
||||
}
|
||||
ns.Valid = true
|
||||
return ns.LocationType.Scan(value)
|
||||
}
|
||||
|
||||
// Value implements the driver Valuer interface.
|
||||
func (ns NullLocationType) Value() (driver.Value, error) {
|
||||
if !ns.Valid {
|
||||
return nil, nil
|
||||
}
|
||||
return string(ns.LocationType), nil
|
||||
}
|
||||
|
||||
type UserReportsType string
|
||||
|
||||
const (
|
||||
@ -140,11 +185,12 @@ type Location struct {
|
||||
Address string `json:"address"`
|
||||
Name string `json:"name"`
|
||||
GoogleMapsLink sql.NullString `json:"google_maps_link"`
|
||||
LocationType LocationType `json:"location_type"`
|
||||
SubmittedBy int32 `json:"submitted_by"`
|
||||
TotalVisited sql.NullInt32 `json:"total_visited"`
|
||||
Thumbnail sql.NullString `json:"thumbnail"`
|
||||
RegencyID int16 `json:"regency_id"`
|
||||
IsDeleted sql.NullBool `json:"is_deleted"`
|
||||
IsDeleted bool `json:"is_deleted"`
|
||||
CreatedAt sql.NullTime `json:"created_at"`
|
||||
UpdatedAt sql.NullTime `json:"updated_at"`
|
||||
ApprovedBy sql.NullInt32 `json:"approved_by"`
|
||||
@ -160,39 +206,54 @@ type LocationImage struct {
|
||||
UpdatedAt sql.NullTime `json:"updated_at"`
|
||||
}
|
||||
|
||||
type Province struct {
|
||||
ID int32 `json:"id"`
|
||||
ProvinceName sql.NullString `json:"province_name"`
|
||||
RegionID int16 `json:"region_id"`
|
||||
CreatedAt sql.NullTime `json:"created_at"`
|
||||
UpdatedAt sql.NullTime `json:"updated_at"`
|
||||
}
|
||||
|
||||
type Regency struct {
|
||||
type NewsEvent struct {
|
||||
ID int32 `json:"id"`
|
||||
RegencyName sql.NullString `json:"regency_name"`
|
||||
ProvinceID int16 `json:"province_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"`
|
||||
RegionID int16 `json:"region_id"`
|
||||
CreatedAt sql.NullTime `json:"created_at"`
|
||||
UpdatedAt sql.NullTime `json:"updated_at"`
|
||||
}
|
||||
|
||||
type Regency struct {
|
||||
ID int32 `json:"id"`
|
||||
RegencyName string `json:"regency_name"`
|
||||
ProvinceID int16 `json:"province_id"`
|
||||
CreatedAt sql.NullTime `json:"created_at"`
|
||||
UpdatedAt sql.NullTime `json:"updated_at"`
|
||||
}
|
||||
|
||||
type Region struct {
|
||||
ID int32 `json:"id"`
|
||||
RegionName sql.NullString `json:"region_name"`
|
||||
CreatedAt sql.NullTime `json:"created_at"`
|
||||
UpdatedAt sql.NullTime `json:"updated_at"`
|
||||
ID int32 `json:"id"`
|
||||
RegionName string `json:"region_name"`
|
||||
CreatedAt sql.NullTime `json:"created_at"`
|
||||
UpdatedAt sql.NullTime `json:"updated_at"`
|
||||
}
|
||||
|
||||
type Review struct {
|
||||
ID int32 `json:"id"`
|
||||
SubmittedBy int32 `json:"submitted_by"`
|
||||
Comments string `json:"comments"`
|
||||
Score int16 `json:"score"`
|
||||
IsFromCritic bool `json:"is_from_critic"`
|
||||
IsHided bool `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"`
|
||||
CostApprox sql.NullInt32 `json:"cost_approx"`
|
||||
IsHided bool `json:"is_hided"`
|
||||
LocationID int32 `json:"location_id"`
|
||||
CreatedAt sql.NullTime `json:"created_at"`
|
||||
UpdatedAt sql.NullTime `json:"updated_at"`
|
||||
}
|
||||
|
||||
type Tag struct {
|
||||
@ -222,6 +283,8 @@ type User struct {
|
||||
SocialMedia pqtype.NullRawMessage `json:"social_media"`
|
||||
CreatedAt sql.NullTime `json:"created_at"`
|
||||
UpdatedAt sql.NullTime `json:"updated_at"`
|
||||
About sql.NullString `json:"about"`
|
||||
Website sql.NullString `json:"website"`
|
||||
}
|
||||
|
||||
type UserActivity struct {
|
||||
@ -235,6 +298,14 @@ type UserActivity struct {
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
type UserFollow struct {
|
||||
ID int32 `json:"id"`
|
||||
FollowerID int32 `json:"follower_id"`
|
||||
FolloweeID int32 `json:"followee_id"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
type UserReport struct {
|
||||
ID int32 `json:"id"`
|
||||
Message string `json:"message"`
|
||||
|
87
db/sqlc/news_events.go
Normal file
87
db/sqlc/news_events.go
Normal 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
|
||||
}
|
40
db/sqlc/news_events.sql.go
Normal file
40
db/sqlc/news_events.sql.go
Normal 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
|
||||
}
|
47
db/sqlc/provinces.sql.go
Normal file
47
db/sqlc/provinces.sql.go
Normal file
@ -0,0 +1,47 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.20.0
|
||||
// source: provinces.sql
|
||||
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
const getListProvinces = `-- name: GetListProvinces :many
|
||||
SELECT
|
||||
id,
|
||||
province_name,
|
||||
region_id
|
||||
FROM PROVINCES
|
||||
`
|
||||
|
||||
type GetListProvincesRow struct {
|
||||
ID int32 `json:"id"`
|
||||
ProvinceName string `json:"province_name"`
|
||||
RegionID int16 `json:"region_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetListProvinces(ctx context.Context) ([]GetListProvincesRow, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getListProvinces)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []GetListProvincesRow{}
|
||||
for rows.Next() {
|
||||
var i GetListProvincesRow
|
||||
if err := rows.Scan(&i.ID, &i.ProvinceName, &i.RegionID); 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
|
||||
}
|
@ -6,21 +6,28 @@ package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
type Querier interface {
|
||||
AddFollowUser(ctx context.Context, arg AddFollowUserParams) error
|
||||
CheckIfReviewExists(ctx context.Context, arg CheckIfReviewExistsParams) (int64, error)
|
||||
CreateLocation(ctx context.Context, arg CreateLocationParams) 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)
|
||||
GetListLocations(ctx context.Context) ([]Location, error)
|
||||
GetListProvinces(ctx context.Context) ([]GetListProvincesRow, error)
|
||||
GetListRecentLocationsWithRatings(ctx context.Context, limit int32) ([]GetListRecentLocationsWithRatingsRow, error)
|
||||
GetListRegencies(ctx context.Context) ([]GetListRegenciesRow, error)
|
||||
GetListRegions(ctx context.Context) ([]GetListRegionsRow, error)
|
||||
GetLocationTag(ctx context.Context, targetID int32) ([]string, error)
|
||||
GetSession(ctx context.Context, id int32) (UserSession, error)
|
||||
GetUserReviewByLocation(ctx context.Context, arg GetUserReviewByLocationParams) (GetUserReviewByLocationRow, error)
|
||||
RemoveFollowUser(ctx context.Context, arg RemoveFollowUserParams) error
|
||||
UpdateAvatar(ctx context.Context, arg UpdateAvatarParams) (sql.NullString, error)
|
||||
UpdateLocationThumbnail(ctx context.Context, arg UpdateLocationThumbnailParams) error
|
||||
UpdatePassword(ctx context.Context, arg UpdatePasswordParams) error
|
||||
UpdateUser(ctx context.Context, arg UpdateUserParams) (User, error)
|
||||
}
|
||||
|
||||
var _ Querier = (*Queries)(nil)
|
||||
|
47
db/sqlc/regencies.sql.go
Normal file
47
db/sqlc/regencies.sql.go
Normal file
@ -0,0 +1,47 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.20.0
|
||||
// source: regencies.sql
|
||||
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
const getListRegencies = `-- name: GetListRegencies :many
|
||||
SELECT
|
||||
id,
|
||||
regency_name,
|
||||
province_id
|
||||
FROM regencies
|
||||
`
|
||||
|
||||
type GetListRegenciesRow struct {
|
||||
ID int32 `json:"id"`
|
||||
RegencyName string `json:"regency_name"`
|
||||
ProvinceID int16 `json:"province_id"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetListRegencies(ctx context.Context) ([]GetListRegenciesRow, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getListRegencies)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []GetListRegenciesRow{}
|
||||
for rows.Next() {
|
||||
var i GetListRegenciesRow
|
||||
if err := rows.Scan(&i.ID, &i.RegencyName, &i.ProvinceID); 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
|
||||
}
|
45
db/sqlc/regions.sql.go
Normal file
45
db/sqlc/regions.sql.go
Normal file
@ -0,0 +1,45 @@
|
||||
// Code generated by sqlc. DO NOT EDIT.
|
||||
// versions:
|
||||
// sqlc v1.20.0
|
||||
// source: regions.sql
|
||||
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
const getListRegions = `-- name: GetListRegions :many
|
||||
SELECT
|
||||
id,
|
||||
region_name
|
||||
FROM regions
|
||||
`
|
||||
|
||||
type GetListRegionsRow struct {
|
||||
ID int32 `json:"id"`
|
||||
RegionName string `json:"region_name"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetListRegions(ctx context.Context) ([]GetListRegionsRow, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getListRegions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
items := []GetListRegionsRow{}
|
||||
for rows.Next() {
|
||||
var i GetListRegionsRow
|
||||
if err := rows.Scan(&i.ID, &i.RegionName); 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
|
||||
}
|
@ -3,6 +3,7 @@ package db
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
|
||||
"github.com/yiplee/sqlc"
|
||||
)
|
||||
@ -13,8 +14,14 @@ type Store interface {
|
||||
GetImagesByLocation(ctx context.Context, arg GetImagesByLocationParams) ([]GetImagesByLocationRow, error)
|
||||
GetLocation(ctx context.Context, location_id int32) (GetLocationRow, error)
|
||||
GetUser(ctx context.Context, username string) (GetUserRow, error)
|
||||
UpdateUser(ctx context.Context, arg UpdateUserParams) (UpdateUserRow, error)
|
||||
GetUserStats(ctx context.Context, user_id int32) (GetUserStatsRow, error)
|
||||
CreateReview(ctx context.Context, arg CreateReviewParams) (Review, error)
|
||||
GetListLocationReviews(ctx context.Context, arg GetListLocationReviewsParams) ([]GetListLocationReviewsRow, error)
|
||||
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 {
|
||||
@ -30,19 +37,19 @@ func NewStore(db *sql.DB) Store {
|
||||
}
|
||||
|
||||
// TRANSACTION QUERY FUNCTION
|
||||
// func (store *SQLStore) execTx(ctx context.Context, fn func(*Queries) error) error {
|
||||
// tx, err := store.db.BeginTx(ctx, nil)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
func (store *SQLStore) execTx(ctx context.Context, fn func(*Queries) error) error {
|
||||
tx, err := store.db.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// q := New(tx)
|
||||
// err = fn(q)
|
||||
// if err != nil {
|
||||
// if rbErr := tx.Rollback(); rbErr != nil {
|
||||
// return fmt.Errorf("tx err: %v, rb err : %v", err, rbErr)
|
||||
// }
|
||||
// return err
|
||||
// }
|
||||
// return tx.Commit()
|
||||
// }
|
||||
q := New(tx)
|
||||
err = fn(q)
|
||||
if err != nil {
|
||||
if rbErr := tx.Rollback(); rbErr != nil {
|
||||
return fmt.Errorf("tx err: %v, rb err : %v", err, rbErr)
|
||||
}
|
||||
return err
|
||||
}
|
||||
return tx.Commit()
|
||||
}
|
||||
|
@ -31,6 +31,6 @@ func TestCreateLocation(t *testing.T) {
|
||||
GoogleMapsLink: sql.NullString{Valid: true, String: util.RandomString(10)},
|
||||
}
|
||||
|
||||
err := testQueries.CreateLocation(context.Background(), arg)
|
||||
_, err := testQueries.CreateLocation(context.Background(), arg)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
59
db/sqlc/tx_location.go
Normal file
59
db/sqlc/tx_location.go
Normal file
@ -0,0 +1,59 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
type CreateLocationTxParams struct {
|
||||
Address string `json:"address"`
|
||||
Name string `json:"name"`
|
||||
SubmittedBy int32 `json:"submitted_by"`
|
||||
LocationType LocationType `json:"location_type"`
|
||||
RegencyID int16 `json:"regency_id"`
|
||||
GoogleMapsLink sql.NullString `json:"google_maps_link"`
|
||||
IsDeleted bool `json:"is_deleted"`
|
||||
ApprovedBy sql.NullInt32 `json:"approved_by"`
|
||||
Thumbnail []CreateImageParams `json:"thumbnails"`
|
||||
}
|
||||
|
||||
func (store *SQLStore) CreateLocationTx(ctx context.Context, arg CreateLocationTxParams) error {
|
||||
err := store.execTx(ctx, func(q *Queries) error {
|
||||
var err error
|
||||
|
||||
location_id, err := q.CreateLocation(ctx, CreateLocationParams{
|
||||
Address: arg.Address,
|
||||
Name: arg.Name,
|
||||
SubmittedBy: arg.SubmittedBy,
|
||||
LocationType: arg.LocationType,
|
||||
RegencyID: arg.RegencyID,
|
||||
GoogleMapsLink: arg.GoogleMapsLink,
|
||||
IsDeleted: arg.IsDeleted,
|
||||
ApprovedBy: arg.ApprovedBy,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(arg.Thumbnail) > 0 {
|
||||
err := q.CreateImage(ctx, arg.Thumbnail)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = q.UpdateLocationThumbnail(ctx, UpdateLocationThumbnailParams{
|
||||
Thumbnail: sql.NullString{Valid: true, String: arg.Thumbnail[0].ImageUrl},
|
||||
ID: location_id,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
202
db/sqlc/users.go
202
db/sqlc/users.go
@ -3,16 +3,21 @@ package db
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/sqlc-dev/pqtype"
|
||||
)
|
||||
|
||||
const getUser = `-- name: GetUser :one
|
||||
const getUserQ = `-- name: GetUser :one
|
||||
SELECT
|
||||
id,
|
||||
COALESCE(email, '') as email,
|
||||
password,
|
||||
username,
|
||||
COALESCE(google_sign_in_payload, '') as google_sign_in_payload,
|
||||
COALESCE(about, '') as about,
|
||||
COALESCE(website, '') as website,
|
||||
COALESCE(avatar_picture, '') as avatar_picture,
|
||||
banned_at,
|
||||
banned_until,
|
||||
@ -21,35 +26,45 @@ SELECT
|
||||
is_admin,
|
||||
is_critics,
|
||||
is_verified,
|
||||
social_media
|
||||
COALESCE(social_media, '[]'),
|
||||
created_at,
|
||||
updated_at
|
||||
FROM USERS
|
||||
WHERE username = $1
|
||||
`
|
||||
|
||||
type GetUserRow struct {
|
||||
ID int32 `json:"id"`
|
||||
Email string `json:"email"`
|
||||
Password string `json:"-"`
|
||||
Username string `json:"username"`
|
||||
AvatarPicture string `json:"avatar_picture"`
|
||||
BannedAt sql.NullTime `json:"banned_at"`
|
||||
BannedUntil sql.NullTime `json:"banned_until"`
|
||||
BanReason string `json:"ban_reason"`
|
||||
IsPermaban bool `json:"is_permaban"`
|
||||
IsAdmin bool `json:"is_admin"`
|
||||
IsCritics bool `json:"is_critics"`
|
||||
IsVerified bool `json:"is_verified"`
|
||||
SocialMedia pqtype.NullRawMessage `json:"social_media"`
|
||||
ID int32 `json:"id"`
|
||||
Email string `json:"email"`
|
||||
Password string `json:"-"`
|
||||
About string `json:"about"`
|
||||
Website string `json:"website"`
|
||||
Username string `json:"username"`
|
||||
GoogleSignInPayload string `json:"google_sign_in_payload"`
|
||||
AvatarPicture string `json:"avatar_picture"`
|
||||
BannedAt sql.NullTime `json:"banned_at"`
|
||||
BannedUntil sql.NullTime `json:"banned_until"`
|
||||
BanReason string `json:"ban_reason"`
|
||||
IsPermaban bool `json:"is_permaban"`
|
||||
IsAdmin bool `json:"is_admin"`
|
||||
IsCritics bool `json:"is_critics"`
|
||||
IsVerified bool `json:"is_verified"`
|
||||
SocialMedia json.RawMessage `json:"social_media"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetUser(ctx context.Context, username string) (GetUserRow, error) {
|
||||
row := q.db.QueryRowContext(ctx, getUser, username)
|
||||
row := q.db.QueryRowContext(ctx, getUserQ, username)
|
||||
var i GetUserRow
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Email,
|
||||
&i.Password,
|
||||
&i.Username,
|
||||
&i.GoogleSignInPayload,
|
||||
&i.About,
|
||||
&i.Website,
|
||||
&i.AvatarPicture,
|
||||
&i.BannedAt,
|
||||
&i.BannedUntil,
|
||||
@ -59,6 +74,161 @@ func (q *Queries) GetUser(ctx context.Context, username string) (GetUserRow, err
|
||||
&i.IsCritics,
|
||||
&i.IsVerified,
|
||||
&i.SocialMedia,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getUserStatsQ = `
|
||||
SELECT
|
||||
json_agg(ur.*) AS reviews,
|
||||
( SELECT COUNT(id) FROM user_follow u WHERE u.followee_id = $1) as followers,
|
||||
( SELECT COUNT(id) FROM reviews r WHERE r.submitted_by = $1 ) as score_count,
|
||||
( SELECT
|
||||
json_agg(r1.*) AS scores_distribution
|
||||
FROM
|
||||
(
|
||||
SELECT
|
||||
*
|
||||
FROM
|
||||
( SELECT COUNT(id) as "0" FROM reviews r WHERE r.score >= 0 AND r.score <= 9 AND r.submitted_by = $1) as score0,
|
||||
( SELECT COUNT(id) as "1" FROM reviews r WHERE r.score >= 10 AND r.score <= 19 AND r.submitted_by = $1) as score1,
|
||||
( SELECT COUNT(id) as "2" FROM reviews r WHERE r.score >= 20 AND r.score <= 29 AND r.submitted_by = $1) as score2,
|
||||
( SELECT COUNT(id) as "3" FROM reviews r WHERE r.score >= 30 AND r.score <= 39 AND r.submitted_by = $1) as score3,
|
||||
( SELECT COUNT(id) as "4" FROM reviews r WHERE r.score >= 40 AND r.score <= 49 AND r.submitted_by = $1) as score4,
|
||||
( SELECT COUNT(id) as "5" FROM reviews r WHERE r.score >= 50 AND r.score <= 59 AND r.submitted_by = $1) as score5,
|
||||
( SELECT COUNT(id) as "6" FROM reviews r WHERE r.score >= 60 AND r.score <= 69 AND r.submitted_by = $1) as score6,
|
||||
( SELECT COUNT(id) as "7" FROM reviews r WHERE r.score >= 70 AND r.score <= 79 AND r.submitted_by = $1) as score7,
|
||||
( SELECT COUNT(id) as "8" FROM reviews r WHERE r.score >= 80 AND r.score <= 89 AND r.submitted_by = $1) as score8,
|
||||
( SELECT COUNT(id) as "9" FROM reviews r WHERE r.score >= 90 AND r.score <= 99 AND r.submitted_by = $1) as score9,
|
||||
( SELECT COUNT(id) as "99" FROM reviews r WHERE r.score = 100 AND r.submitted_by = $1) as score10
|
||||
) r1
|
||||
)
|
||||
FROM (
|
||||
SELECT
|
||||
reviews.id,
|
||||
reviews.comments,
|
||||
score,
|
||||
l.name,
|
||||
p.province_name,
|
||||
COALESCE(l.thumbnail, '') as thumbnail
|
||||
FROM
|
||||
reviews
|
||||
JOIN locations l on reviews.location_id = l.id
|
||||
JOIN regencies r on l.regency_id = r.id
|
||||
JOIN provinces p on r.province_id = p.id
|
||||
WHERE reviews.submitted_by = $1
|
||||
ORDER BY score ASC
|
||||
LIMIT 10
|
||||
) ur
|
||||
`
|
||||
|
||||
type GetUserStatsRow struct {
|
||||
Reviews []byte `json:"-"`
|
||||
Followers int32 `json:"followers"`
|
||||
ScoreCount int32 `json:"score_count"`
|
||||
ScoresDistribution []byte `json:"-"`
|
||||
}
|
||||
|
||||
func (q *Queries) GetUserStats(ctx context.Context, user_id int32) (GetUserStatsRow, error) {
|
||||
var i GetUserStatsRow
|
||||
row := q.db.QueryRowContext(ctx, getUserStatsQ, user_id)
|
||||
|
||||
err := row.Scan(
|
||||
&i.Reviews,
|
||||
&i.Followers,
|
||||
&i.ScoreCount,
|
||||
&i.ScoresDistribution,
|
||||
)
|
||||
return i, err
|
||||
|
||||
}
|
||||
|
||||
const updateUser = `-- name: UpdateUser :one
|
||||
UPDATE users
|
||||
SET
|
||||
about = $1,
|
||||
social_media = $2,
|
||||
website = $3
|
||||
WHERE
|
||||
id = $4
|
||||
RETURNING
|
||||
id,
|
||||
COALESCE(email, ''),
|
||||
username,
|
||||
COALESCE(avatar_picture, ''),
|
||||
COALESCE(about, ''),
|
||||
COALESCE(website, ''),
|
||||
COALESCE(google_sign_in_payload, ''),
|
||||
banned_at,
|
||||
banned_until,
|
||||
COALESCE(ban_reason, ''),
|
||||
is_permaban,
|
||||
is_admin,
|
||||
is_critics,
|
||||
is_verified,
|
||||
is_active,
|
||||
COALESCE(social_media, '[]'),
|
||||
created_at,
|
||||
updated_at
|
||||
`
|
||||
|
||||
type UpdateUserParams struct {
|
||||
About sql.NullString `json:"about"`
|
||||
SocialMedia pqtype.NullRawMessage `json:"social_media"`
|
||||
Website sql.NullString `json:"website"`
|
||||
ID int32 `json:"id"`
|
||||
}
|
||||
|
||||
type UpdateUserRow struct {
|
||||
ID int32 `json:"id"`
|
||||
Email string `json:"email"`
|
||||
Username string `json:"username"`
|
||||
AvatarPicture string `json:"avatar_picture"`
|
||||
About string `json:"about"`
|
||||
Website string `json:"website"`
|
||||
GoogleSignInPayload string `json:"google_sign_in_payload"`
|
||||
BannedAt sql.NullTime `json:"banned_at"`
|
||||
BannedUntil sql.NullTime `json:"banned_until"`
|
||||
BanReason string `json:"ban_reason"`
|
||||
IsPermaban bool `json:"is_permaban"`
|
||||
IsAdmin bool `json:"is_admin"`
|
||||
IsCritics bool `json:"is_critics"`
|
||||
IsVerified bool `json:"is_verified"`
|
||||
IsActive bool `json:"is_active"`
|
||||
SocialMedia json.RawMessage `json:"social_media"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateUser(ctx context.Context, arg UpdateUserParams) (UpdateUserRow, error) {
|
||||
row := q.db.QueryRowContext(ctx, updateUser,
|
||||
arg.About,
|
||||
arg.SocialMedia,
|
||||
arg.Website,
|
||||
arg.ID,
|
||||
)
|
||||
var i UpdateUserRow
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Email,
|
||||
&i.Username,
|
||||
&i.AvatarPicture,
|
||||
&i.About,
|
||||
&i.Website,
|
||||
&i.GoogleSignInPayload,
|
||||
&i.BannedAt,
|
||||
&i.BannedUntil,
|
||||
&i.BanReason,
|
||||
&i.IsPermaban,
|
||||
&i.IsAdmin,
|
||||
&i.IsCritics,
|
||||
&i.IsVerified,
|
||||
&i.IsActive,
|
||||
&i.SocialMedia,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ INSERT INTO users (
|
||||
username,
|
||||
password
|
||||
) VALUES ($1, $2)
|
||||
RETURNING id, email, username, password, avatar_picture, google_sign_in_payload, banned_at, banned_until, ban_reason, is_permaban, is_admin, is_critics, is_verified, is_active, social_media, created_at, updated_at
|
||||
RETURNING id, email, username, password, avatar_picture, google_sign_in_payload, banned_at, banned_until, ban_reason, is_permaban, is_admin, is_critics, is_verified, is_active, social_media, created_at, updated_at, about, website
|
||||
`
|
||||
|
||||
type CreateUserParams struct {
|
||||
@ -44,10 +44,31 @@ func (q *Queries) CreateUser(ctx context.Context, arg CreateUserParams) (User, e
|
||||
&i.SocialMedia,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.About,
|
||||
&i.Website,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const updateAvatar = `-- name: UpdateAvatar :one
|
||||
UPDATE users
|
||||
SET avatar_picture = $1
|
||||
WHERE id = $2
|
||||
RETURNING avatar_picture
|
||||
`
|
||||
|
||||
type UpdateAvatarParams struct {
|
||||
AvatarPicture sql.NullString `json:"avatar_picture"`
|
||||
ID int32 `json:"id"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateAvatar(ctx context.Context, arg UpdateAvatarParams) (sql.NullString, error) {
|
||||
row := q.db.QueryRowContext(ctx, updateAvatar, arg.AvatarPicture, arg.ID)
|
||||
var avatar_picture sql.NullString
|
||||
err := row.Scan(&avatar_picture)
|
||||
return avatar_picture, err
|
||||
}
|
||||
|
||||
const updatePassword = `-- name: UpdatePassword :exec
|
||||
UPDATE users
|
||||
SET password = $1
|
||||
@ -63,51 +84,3 @@ func (q *Queries) UpdatePassword(ctx context.Context, arg UpdatePasswordParams)
|
||||
_, err := q.db.ExecContext(ctx, updatePassword, arg.Password, arg.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
const updateUser = `-- name: UpdateUser :one
|
||||
UPDATE users
|
||||
SET
|
||||
email = COALESCE($1, email),
|
||||
username = COALESCE($2, username),
|
||||
avatar_picture = COALESCE($3, avatar_picture)
|
||||
WHERE
|
||||
id = $4
|
||||
RETURNING id, email, username, password, avatar_picture, google_sign_in_payload, banned_at, banned_until, ban_reason, is_permaban, is_admin, is_critics, is_verified, is_active, social_media, created_at, updated_at
|
||||
`
|
||||
|
||||
type UpdateUserParams struct {
|
||||
Email sql.NullString `json:"email"`
|
||||
Username sql.NullString `json:"username"`
|
||||
AvatarPicture sql.NullString `json:"avatar_picture"`
|
||||
ID int32 `json:"id"`
|
||||
}
|
||||
|
||||
func (q *Queries) UpdateUser(ctx context.Context, arg UpdateUserParams) (User, error) {
|
||||
row := q.db.QueryRowContext(ctx, updateUser,
|
||||
arg.Email,
|
||||
arg.Username,
|
||||
arg.AvatarPicture,
|
||||
arg.ID,
|
||||
)
|
||||
var i User
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.Email,
|
||||
&i.Username,
|
||||
&i.Password,
|
||||
&i.AvatarPicture,
|
||||
&i.GoogleSignInPayload,
|
||||
&i.BannedAt,
|
||||
&i.BannedUntil,
|
||||
&i.BanReason,
|
||||
&i.IsPermaban,
|
||||
&i.IsAdmin,
|
||||
&i.IsCritics,
|
||||
&i.IsVerified,
|
||||
&i.IsActive,
|
||||
&i.SocialMedia,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ sudo -u postgres psql \
|
||||
-c '\copy regions(id, region_name) FROM '"'/tmp/regions.csv'"' DELIMITER '"','"' CSV HEADER;' \
|
||||
-c '\copy provinces(id, province_name, region_id) FROM '"'/tmp/provinsi.csv'"' DELIMITER '"','"' CSV HEADER;' \
|
||||
-c '\copy regencies(id, province_id, regency_name) FROM '"'/tmp/kabupaten.csv'"' DELIMITER '"','"' CSV HEADER;' \
|
||||
-c '\copy locations(id,address,name,submitted_by,thumbnail,regency_id,google_maps_link,approved_by) FROM '"'/tmp/locations.csv'"' DELIMITER '"'#'"' CSV HEADER;' \
|
||||
-c '\copy locations(id,location_type,address,name,submitted_by,thumbnail,regency_id,google_maps_link,approved_by,is_deleted) FROM '"'/tmp/locations.csv'"' DELIMITER '"'#'"' CSV HEADER;' \
|
||||
-c '\copy reviews(id,submitted_by,is_from_critic,comments,score,is_hided,location_id) FROM '"'/tmp/reviews.csv'"' DELIMITER '"'#'"' CSV HEADER;' \
|
||||
-c '\copy images(id,image_url,uploaded_by,image_type,image_of) FROM '"'/tmp/images.csv'"' DELIMITER '"'#'"' CSV HEADER;' \
|
||||
-c '\copy tags(id,name,submitted_by,target_id,tags_type) FROM '"'/tmp/tags.csv'"' DELIMITER '"','"' CSV HEADER;' \
|
||||
@ -15,7 +15,7 @@ sudo -u postgres psql \
|
||||
-c '\copy regions(id, region_name) FROM '"'/tmp/regions.csv'"' DELIMITER '"','"' CSV HEADER;' \
|
||||
-c '\copy provinces(id, province_name, region_id) FROM '"'/tmp/provinsi.csv'"' DELIMITER '"','"' CSV HEADER;' \
|
||||
-c '\copy regencies(id, province_id, regency_name) FROM '"'/tmp/kabupaten.csv'"' DELIMITER '"','"' CSV HEADER;' \
|
||||
-c '\copy locations(id,address,name,submitted_by,thumbnail,regency_id,google_maps_link,approved_by) FROM '"'/tmp/locations.csv'"' DELIMITER '"'#'"' CSV HEADER;' \
|
||||
-c '\copy locations(id,location_type,address,name,submitted_by,thumbnail,regency_id,google_maps_link,approved_by,is_deleted) FROM '"'/tmp/locations.csv'"' DELIMITER '"'#'"' CSV HEADER;' \
|
||||
-c '\copy reviews(id,submitted_by,is_from_critic,comments,score,is_hided,location_id) FROM '"'/tmp/reviews.csv'"' DELIMITER '"'#'"' CSV HEADER;' \
|
||||
-c '\copy images(id,image_url,uploaded_by,image_type,image_of) FROM '"'/tmp/images.csv'"' DELIMITER '"'#'"' CSV HEADER;' \
|
||||
-c '\copy tags(id,name,submitted_by,target_id,tags_type) FROM '"'/tmp/tags.csv'"' DELIMITER '"','"' CSV HEADER;' \
|
||||
|
11
notes
11
notes
@ -104,4 +104,15 @@ PAGINATION
|
||||
|
||||
http://andreyzavadskiy.com/2016/12/03/pagination-and-total-number-of-rows-from-one-select/
|
||||
|
||||
|
||||
##########################################################################################
|
||||
|
||||
|
||||
##########################################################################################
|
||||
|
||||
PAGINATION
|
||||
|
||||
http://andreyzavadskiy.com/2016/12/03/pagination-and-total-number-of-rows-from-one-select/
|
||||
https://medium.easyread.co/how-to-do-pagination-in-postgres-with-golang-in-4-common-ways-12365b9fb528?gi=a4683590d1d2
|
||||
|
||||
##########################################################################################
|
14
util/common.go
Normal file
14
util/common.go
Normal file
@ -0,0 +1,14 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ReplaceSQL(old, searchPattern string) string {
|
||||
tmpCount := strings.Count(old, searchPattern)
|
||||
for m := 1; m <= tmpCount; m++ {
|
||||
old = strings.Replace(old, searchPattern, "$"+strconv.Itoa(m), 1)
|
||||
}
|
||||
return old
|
||||
}
|
Loading…
Reference in New Issue
Block a user