package api import ( "database/sql" "fmt" "net/http" "os" "path/filepath" "time" 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" ) type createLocationReq struct { Address string `form:"address" binding:"required"` Name string `form:"name" binding:"required"` SubmittedBy int32 `form:"submitted_by" binding:"required,number"` RegencyID int16 `form:"regency_id" binding:"required,number"` GoogleMapsLink string `form:"google_maps_link"` } func (server *Server) createLocation(ctx *gin.Context) { var req createLocationReq var imgPath string var thumbnail, _ = ctx.FormFile("thumbnail") 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) if _, err := os.Stat(dir); os.IsNotExist(err) { os.Mkdir(dir, 0775) } if err := ctx.SaveUploadedFile(img, imgPath); err != nil { ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Error while try to save thumbnail image")) return } } 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 } } } ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong")) return } ctx.Writer.WriteHeader(http.StatusOK) } type getListLocationsReq struct { Page int32 `form:"page" binding:"required,min=1"` PageSize int32 `form:"page_size" binding:"required,min=5"` } func (server *Server) getListLocations(ctx *gin.Context) { var req getListLocationsReq if err := ctx.ShouldBindQuery(&req); err != nil { ctx.JSON(http.StatusBadRequest, ValidationErrorResponse(err)) return } locations, err := server.Store.GetListLocations(ysqlc.Build(ctx, func(builder *ysqlc.Builder) { builder.Limit(int(req.PageSize)) builder.Offset(int(req.Page-1) * int(req.PageSize)) builder.Order("created_at ASC") })) if err != nil { ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong")) return } ctx.JSON(http.StatusOK, locations) } type getTopListLocationsReq struct { Page int32 `form:"page" binding:"required,min=1"` PageSize int32 `form:"page_size" binding:"required,min=5"` OrderBy int16 `form:"order_by" binding:"numeric,min=1,max=3"` RegionType int16 `form:"region_type" binding:"numeric,min=0,max=7"` } func (server *Server) getTopListLocations(ctx *gin.Context) { var req getTopListLocationsReq var orderby string if err := ctx.ShouldBindQuery(&req); err != nil { ctx.JSON(http.StatusBadRequest, ValidationErrorResponse(err)) return } switch req.OrderBy { case 1: orderby = "iq2.avg_bayes" case 2: orderby = "iq2.critic_bayes" case 3: orderby = "iq2.user_bayes" } arg := db.GetTopListLocationsParams{ Limit: req.PageSize, Offset: (req.Page - 1) * req.PageSize, OrderBy: orderby, RegionType: sql.NullInt16{Valid: req.RegionType > 0, Int16: req.RegionType}, } locations, err := server.Store.GetTopListLocations(ctx, arg) if err != nil { ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong")) return } 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"` } func (server *Server) getLocation(ctx *gin.Context) { var req getLocationReq if err := ctx.ShouldBindUri(&req); err != nil { ctx.JSON(http.StatusBadRequest, ValidationErrorResponse(err)) return } location, err := server.Store.GetLocation(ctx, req.ID) if err != nil { if err == sql.ErrNoRows { ctx.JSON(http.StatusNotFound, ErrorResponse(err, "")) return } ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while retrieving location detail")) return } tags, err := server.Store.GetLocationTag(ctx, req.ID) if err != nil { ctx.JSON(http.StatusBadRequest, ErrorResponse(err, "Something went wrong while retrieving location tags")) return } res := gin.H{ "tags": tags, "detail": location, } ctx.JSON(http.StatusOK, res) }