diff --git a/api/image.go b/api/image.go new file mode 100644 index 0000000..fda361b --- /dev/null +++ b/api/image.go @@ -0,0 +1,44 @@ +package api + +import ( + "net/http" + + db "git.nochill.in/nochill/hiling_go/db/sqlc" + "github.com/gin-gonic/gin" +) + +type getAllImagesReq struct { + Page int32 `form:"page" binding:"required,min=1"` + PageSize int32 `form:"page_size" binding:"required,min=5"` + LocationId int32 `form:"location_id" binding:"required,numeric"` +} + +func (server *Server) getAllImagesByLocation(ctx *gin.Context) { + var req getAllImagesReq + if err := ctx.BindQuery(&req); err != nil { + ctx.JSON(http.StatusBadRequest, ValidationErrorResponse(err)) + return + } + + arg := db.GetImagesByLocationParams{ + Limit: req.PageSize, + Offset: (req.Page - 1) * req.PageSize, + LocationId: req.LocationId, + } + + count, err := server.Store.GetCountImageByLocation(ctx, arg.LocationId) + if err != nil { + ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong")) + return + } + + images, err := server.Store.GetImagesByLocation(ctx, arg) + if err != nil { + ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong")) + return + } + ctx.JSON(http.StatusOK, gin.H{ + "total_image": count, + "images": images, + }) +} diff --git a/api/server.go b/api/server.go index f4f9103..6dd03cd 100644 --- a/api/server.go +++ b/api/server.go @@ -46,6 +46,9 @@ func (server *Server) getRoutes() { router.GET("/locations", server.getListLocations) router.GET("/location/:location_id", server.getLocation) + //IMAGES + router.GET("/images/location", server.getAllImagesByLocation) + server.Router = router } diff --git a/db/csv_seeder/images.csv b/db/csv_seeder/images.csv new file mode 100644 index 0000000..3a875b5 --- /dev/null +++ b/db/csv_seeder/images.csv @@ -0,0 +1,8 @@ +id,image_url,uploaded_by,image_type,image_of +1#https://dynamic-media-cdn.tripadvisor.com/media/photo-o/1b/ee/b0/7b/murni-s-warung-shop-ubud.jpg?w=1200&h=-1&s=1#1#ocations#1 +2#https://dynamic-media-cdn.tripadvisor.com/media/photo-o/1b/4f/fc/65/getlstd-property-photo.jpg?w=1100&h=-1&s=1#1#locations#1 +3#https://dynamic-media-cdn.tripadvisor.com/media/photo-o/1b/ee/b0/43/murni-s-warung-shop-ubud.jpg?w=1200&h=-1&s=1#1#locations#1 +4#https://dynamic-media-cdn.tripadvisor.com/media/photo-o/1b/ee/af/d0/murni-s-warung-shop-ubud.jpg?w=1200&h=-1&s=1#1#locations#1 +5#https://dynamic-media-cdn.tripadvisor.com/media/photo-o/04/0f/7e/53/istiqlal-mosque-mesjid.jpg?w=1200&h=-1&s=1#1#locations#2 +6#https://dynamic-media-cdn.tripadvisor.com/media/photo-o/28/27/1a/5a/istiqlal-mosque.jpg?w=1200&h=-1&s=1#1#locations#2 +7#https://dynamic-media-cdn.tripadvisor.com/media/photo-o/28/27/1a/5a/istiqlal-mosque.jpg?w=1200&h=-1&s=1#1#locations#2 \ No newline at end of file diff --git a/db/migrations/000004_create_images_table.down.sql b/db/migrations/000004_create_images_table.down.sql new file mode 100644 index 0000000..b9f21e6 --- /dev/null +++ b/db/migrations/000004_create_images_table.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS images; \ No newline at end of file diff --git a/db/migrations/000004_create_images_table.up.sql b/db/migrations/000004_create_images_table.up.sql new file mode 100644 index 0000000..4c5fc93 --- /dev/null +++ b/db/migrations/000004_create_images_table.up.sql @@ -0,0 +1,9 @@ +CREATE TABLE images( + id serial primary key not null, + image_url varchar not null, + uploaded_by integer references "users"("id") not null, + image_type varchar not null, -- Locations, Stories + image_of integer not null, -- Location_id, stories_id + created_at timestamp default (now()) not null, + updated_at timestamp default (now()) not null +); \ No newline at end of file diff --git a/db/mock/store.go b/db/mock/store.go index e5b53bc..dfaa23a 100644 --- a/db/mock/store.go +++ b/db/mock/store.go @@ -64,6 +64,36 @@ func (mr *MockStoreMockRecorder) CreateUser(arg0, arg1 interface{}) *gomock.Call return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateUser", reflect.TypeOf((*MockStore)(nil).CreateUser), arg0, arg1) } +// GetCountImageByLocation mocks base method. +func (m *MockStore) GetCountImageByLocation(arg0 context.Context, arg1 int32) (int64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetCountImageByLocation", arg0, arg1) + ret0, _ := ret[0].(int64) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetCountImageByLocation indicates an expected call of GetCountImageByLocation. +func (mr *MockStoreMockRecorder) GetCountImageByLocation(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCountImageByLocation", reflect.TypeOf((*MockStore)(nil).GetCountImageByLocation), arg0, arg1) +} + +// GetImagesByLocation mocks base method. +func (m *MockStore) GetImagesByLocation(arg0 context.Context, arg1 db.GetImagesByLocationParams) ([]db.GetImagesByLocationRow, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetImagesByLocation", arg0, arg1) + ret0, _ := ret[0].([]db.GetImagesByLocationRow) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetImagesByLocation indicates an expected call of GetImagesByLocation. +func (mr *MockStoreMockRecorder) GetImagesByLocation(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetImagesByLocation", reflect.TypeOf((*MockStore)(nil).GetImagesByLocation), arg0, arg1) +} + // GetListLocations mocks base method. func (m *MockStore) GetListLocations(arg0 context.Context) ([]db.Location, error) { m.ctrl.T.Helper() @@ -95,10 +125,10 @@ func (mr *MockStoreMockRecorder) GetListRecentLocationsWithRatings(arg0, arg1 in } // GetLocation mocks base method. -func (m *MockStore) GetLocation(arg0 context.Context, arg1 int32) (db.Location, error) { +func (m *MockStore) GetLocation(arg0 context.Context, arg1 int32) (db.GetLocationRow, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetLocation", arg0, arg1) - ret0, _ := ret[0].(db.Location) + ret0, _ := ret[0].(db.GetLocationRow) ret1, _ := ret[1].(error) return ret0, ret1 } diff --git a/db/queries/images.sql b/db/queries/images.sql new file mode 100644 index 0000000..0896f7e --- /dev/null +++ b/db/queries/images.sql @@ -0,0 +1,6 @@ +-- name: GetCountImageByLocation :one +SELECT + COUNT(id) +FROM images +WHERE image_type = 'locations' +AND image_of = $1; \ No newline at end of file diff --git a/db/sqlc/images.go b/db/sqlc/images.go new file mode 100644 index 0000000..5453a96 --- /dev/null +++ b/db/sqlc/images.go @@ -0,0 +1,61 @@ +package db + +import ( + "context" + "time" +) + +type GetImagesByLocationParams struct { + Limit int32 + Offset int32 + LocationId int32 +} + +type GetImagesByLocationRow struct { + ID int32 `json:"id"` + Src string `json:"src"` + CreatedAt time.Time `json:"created_at"` + UploadedBy string `json:"uploaded_by"` +} + +const getImagesByLocationQ = ` +SELECT + i.id, + i.image_url as src, + i.created_at, + u.username as uploaded_by +FROM images i +JOIN users u on i.uploaded_by = u.id +WHERE i.image_type = 'locations' AND image_of = $1 +LIMIT $2 +OFFSET $3 +` + +func (q *Queries) GetImagesByLocation(ctx context.Context, arg GetImagesByLocationParams) ([]GetImagesByLocationRow, error) { + rows, err := q.db.QueryContext(ctx, getImagesByLocationQ, arg.LocationId, arg.Limit, arg.Offset) + if err != nil { + return nil, err + } + defer rows.Close() + items := []GetImagesByLocationRow{} + for rows.Next() { + var i GetImagesByLocationRow + if err := rows.Scan( + &i.ID, + &i.Src, + &i.CreatedAt, + &i.UploadedBy, + ); 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 +} diff --git a/db/sqlc/images.sql.go b/db/sqlc/images.sql.go new file mode 100644 index 0000000..8d80a25 --- /dev/null +++ b/db/sqlc/images.sql.go @@ -0,0 +1,25 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.20.0 +// source: images.sql + +package db + +import ( + "context" +) + +const getCountImageByLocation = `-- name: GetCountImageByLocation :one +SELECT + COUNT(id) +FROM images +WHERE image_type = 'locations' +AND image_of = $1 +` + +func (q *Queries) GetCountImageByLocation(ctx context.Context, imageOf int32) (int64, error) { + row := q.db.QueryRowContext(ctx, getCountImageByLocation, imageOf) + var count int64 + err := row.Scan(&count) + return count, err +} diff --git a/db/sqlc/models.go b/db/sqlc/models.go index c2dbca8..5bf65a1 100644 --- a/db/sqlc/models.go +++ b/db/sqlc/models.go @@ -124,6 +124,16 @@ type Comment struct { UpdatedAt sql.NullTime `json:"updated_at"` } +type Image struct { + ID int32 `json:"id"` + ImageUrl string `json:"image_url"` + UploadedBy int32 `json:"uploaded_by"` + ImageType string `json:"image_type"` + ImageOf int32 `json:"image_of"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + type Location struct { ID int32 `json:"id"` Address string `json:"address"` @@ -185,12 +195,12 @@ type Review struct { } type Tag struct { - ID int32 `json:"id"` - Name string `json:"name"` - TargetID sql.NullInt32 `json:"target_id"` - TagsType sql.NullString `json:"tags_type"` - CreatedAt sql.NullTime `json:"created_at"` - UpdatedAt sql.NullTime `json:"updated_at"` + ID int32 `json:"id"` + Name string `json:"name"` + SubmittedBy int32 `json:"submitted_by"` + TargetID sql.NullInt32 `json:"target_id"` + TagsType sql.NullString `json:"tags_type"` + ApprovedBy int32 `json:"approved_by"` } type User struct { @@ -213,6 +223,17 @@ type User struct { UpdatedAt sql.NullTime `json:"updated_at"` } +type UserActivity struct { + ID int32 `json:"id"` + TargetID int32 `json:"target_id"` + Target string `json:"target"` + Action string `json:"action"` + Link sql.NullString `json:"link"` + Comment sql.NullString `json:"comment"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + type UserReport struct { ID int32 `json:"id"` Message string `json:"message"` diff --git a/db/sqlc/querier.go b/db/sqlc/querier.go index af16368..349c0ec 100644 --- a/db/sqlc/querier.go +++ b/db/sqlc/querier.go @@ -11,9 +11,10 @@ import ( type Querier interface { CreateLocation(ctx context.Context, arg CreateLocationParams) error CreateUser(ctx context.Context, arg CreateUserParams) (User, error) + GetCountImageByLocation(ctx context.Context, imageOf int32) (int64, error) GetListLocations(ctx context.Context) ([]Location, error) GetListRecentLocationsWithRatings(ctx context.Context, limit int32) ([]GetListRecentLocationsWithRatingsRow, error) - GetLocation(ctx context.Context, id int32) (Location, error) + GetLocation(ctx context.Context, id int32) (GetLocationRow, error) UpdatePassword(ctx context.Context, arg UpdatePasswordParams) error UpdateUser(ctx context.Context, arg UpdateUserParams) (User, error) } diff --git a/db/sqlc/store.go b/db/sqlc/store.go index acd6b8f..403e206 100644 --- a/db/sqlc/store.go +++ b/db/sqlc/store.go @@ -10,6 +10,7 @@ import ( type Store interface { Querier GetTopListLocations(ctx context.Context, arg GetTopListLocationsParams) ([]GetTopListLocationsRow, error) + GetImagesByLocation(ctx context.Context, arg GetImagesByLocationParams) ([]GetImagesByLocationRow, error) } type SQLStore struct {