From 5a5d157882e2269981bfba95238e83683f65b647 Mon Sep 17 00:00:00 2001 From: nochill Date: Tue, 3 Oct 2023 14:56:57 +0700 Subject: [PATCH] adjust with multiple image locations --- api/location.go | 65 ++++++++++++++++++++++------------ db/mock/store.go | 21 +++++++++-- db/queries/locations.sql | 11 ------ db/sqlc/images.go | 38 ++++++++++++++++++++ db/sqlc/locations.go | 50 ++++++++++++++++++++++++++ db/sqlc/locations.sql.go | 31 ---------------- db/sqlc/querier.go | 1 - db/sqlc/store.go | 2 ++ db/sqlc/test/locations_test.go | 2 +- util/common.go | 14 ++++++++ 10 files changed, 166 insertions(+), 69 deletions(-) create mode 100644 util/common.go diff --git a/api/location.go b/api/location.go index 2483a4f..205f24c 100644 --- a/api/location.go +++ b/api/location.go @@ -20,6 +20,7 @@ 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"` } @@ -27,40 +28,23 @@ 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, + 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}, } - err := server.Store.CreateLocation(ctx, arg) + id, err := server.Store.CreateLocation(ctx, arg) if err != nil { if pqErr, ok := err.(*pq.Error); ok { @@ -76,10 +60,47 @@ func (server *Server) createLocation(ctx *gin.Context) { } } } - ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong")) + ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to create location")) return } + form, _ := ctx.MultipartForm() + thumbnails := form.File["thumbnail"] + + if len(thumbnails) > 0 { + var tempImg []db.CreateImageParams + 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 := 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 + } + + tempImg = append(tempImg, db.CreateImageParams{ + ImageUrl: imgPath, + UploadedBy: req.SubmittedBy, + ImageType: "locations", + ImageOf: id, + }) + } + + err := server.Store.CreateImage(ctx, tempImg) + + if err != nil { + ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while try to save image")) + return + } + } + ctx.Writer.WriteHeader(http.StatusOK) } diff --git a/db/mock/store.go b/db/mock/store.go index e39f631..cb5df5a 100644 --- a/db/mock/store.go +++ b/db/mock/store.go @@ -50,14 +50,29 @@ 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() diff --git a/db/queries/locations.sql b/db/queries/locations.sql index be647c8..43981f3 100644 --- a/db/queries/locations.sql +++ b/db/queries/locations.sql @@ -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 diff --git a/db/sqlc/images.go b/db/sqlc/images.go index 5453a96..dfa869b 100644 --- a/db/sqlc/images.go +++ b/db/sqlc/images.go @@ -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 +} diff --git a/db/sqlc/locations.go b/db/sqlc/locations.go index da00b03..31f4b58 100644 --- a/db/sqlc/locations.go +++ b/db/sqlc/locations.go @@ -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 +} diff --git a/db/sqlc/locations.sql.go b/db/sqlc/locations.sql.go index c41e94c..bde51d1 100644 --- a/db/sqlc/locations.sql.go +++ b/db/sqlc/locations.sql.go @@ -10,37 +10,6 @@ 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, location_type, submitted_by, total_visited, thumbnail, regency_id, is_deleted, created_at, updated_at, approved_by, approved_at FROM locations ` diff --git a/db/sqlc/querier.go b/db/sqlc/querier.go index ee5e394..e6a258d 100644 --- a/db/sqlc/querier.go +++ b/db/sqlc/querier.go @@ -10,7 +10,6 @@ import ( type Querier interface { CheckIfReviewExists(ctx context.Context, arg CheckIfReviewExistsParams) (int64, error) - CreateLocation(ctx context.Context, arg CreateLocationParams) 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) diff --git a/db/sqlc/store.go b/db/sqlc/store.go index e3fc365..a494c8f 100644 --- a/db/sqlc/store.go +++ b/db/sqlc/store.go @@ -15,6 +15,8 @@ type Store interface { GetUser(ctx context.Context, username string) (GetUserRow, 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 } type SQLStore struct { diff --git a/db/sqlc/test/locations_test.go b/db/sqlc/test/locations_test.go index 7886855..884119d 100644 --- a/db/sqlc/test/locations_test.go +++ b/db/sqlc/test/locations_test.go @@ -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) } diff --git a/util/common.go b/util/common.go new file mode 100644 index 0000000..314814c --- /dev/null +++ b/util/common.go @@ -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 +}