diff --git a/api/location.go b/api/location.go index d80565f..61a3bbd 100644 --- a/api/location.go +++ b/api/location.go @@ -188,9 +188,20 @@ func (server *Server) getLocation(ctx *gin.Context) { ctx.JSON(http.StatusNotFound, ErrorResponse(err, "")) return } - ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong")) + ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong while retrieving location detail")) return } - ctx.JSON(http.StatusOK, location) + 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) } diff --git a/api/server.go b/api/server.go index 6dd03cd..87aede7 100644 --- a/api/server.go +++ b/api/server.go @@ -45,6 +45,7 @@ func (server *Server) getRoutes() { router.GET("/locations/top-ratings", server.getTopListLocations) router.GET("/locations", server.getListLocations) router.GET("/location/:location_id", server.getLocation) + router.GET("/location/tags/:location_id", server.getTagsByLocation) //IMAGES router.GET("/images/location", server.getAllImagesByLocation) diff --git a/api/tags.go b/api/tags.go new file mode 100644 index 0000000..d4df584 --- /dev/null +++ b/api/tags.go @@ -0,0 +1,28 @@ +package api + +import ( + "net/http" + + "github.com/gin-gonic/gin" +) + +type getTagsByLocationReq struct { + LocationID int32 `uri:"location_id" binding:"required,numeric"` +} + +func (server *Server) getTagsByLocation(ctx *gin.Context) { + var req getTagsByLocationReq + + if err := ctx.BindUri(&req); err != nil { + ctx.JSON(http.StatusBadRequest, ValidationErrorResponse(err)) + return + } + + tags, err := server.Store.GetLocationTag(ctx, req.LocationID) + if err != nil { + ctx.JSON(http.StatusInternalServerError, ErrorResponse(err, "Something went wrong")) + return + } + + ctx.JSON(http.StatusOK, tags) +} diff --git a/db/migrations/000001_init_schema.up.sql b/db/migrations/000001_init_schema.up.sql index 5219e7d..78818a0 100644 --- a/db/migrations/000001_init_schema.up.sql +++ b/db/migrations/000001_init_schema.up.sql @@ -92,9 +92,9 @@ CREATE TABLE tags ( "id" serial primary key not null, "name" varchar(50) not null, "submitted_by" integer references "users"("id") not null, - "target_id" integer, -- location_id, story_id - "tags_type" varchar(20), -- locations, stories - "approved_by" integer references "users"("id") not null + "target_id" integer not null, -- location_id, story_id + "tags_type" varchar(20) not null, -- locations, stories + "approved_by" integer references "users"("id") ); CREATE TABLE location_images ( diff --git a/db/migrations/000005_alter_tags_column_approvedby_nullable.down.sql b/db/migrations/000005_alter_tags_column_approvedby_nullable.down.sql new file mode 100644 index 0000000..e69de29 diff --git a/db/migrations/000005_alter_tags_column_approvedby_nullable.up.sql b/db/migrations/000005_alter_tags_column_approvedby_nullable.up.sql new file mode 100644 index 0000000..a6de801 --- /dev/null +++ b/db/migrations/000005_alter_tags_column_approvedby_nullable.up.sql @@ -0,0 +1 @@ +ALTER TABLE tags ALTER COLUMN approved_by DROP NOT NULL; \ No newline at end of file diff --git a/db/mock/store.go b/db/mock/store.go index dfaa23a..3cf6432 100644 --- a/db/mock/store.go +++ b/db/mock/store.go @@ -139,6 +139,21 @@ func (mr *MockStoreMockRecorder) GetLocation(arg0, arg1 interface{}) *gomock.Cal return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLocation", reflect.TypeOf((*MockStore)(nil).GetLocation), arg0, arg1) } +// GetLocationTag mocks base method. +func (m *MockStore) GetLocationTag(arg0 context.Context, arg1 int32) ([]string, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetLocationTag", arg0, arg1) + ret0, _ := ret[0].([]string) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetLocationTag indicates an expected call of GetLocationTag. +func (mr *MockStoreMockRecorder) GetLocationTag(arg0, arg1 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetLocationTag", reflect.TypeOf((*MockStore)(nil).GetLocationTag), arg0, arg1) +} + // GetTopListLocations mocks base method. func (m *MockStore) GetTopListLocations(arg0 context.Context, arg1 db.GetTopListLocationsParams) ([]db.GetTopListLocationsRow, error) { m.ctrl.T.Helper() diff --git a/db/queries/locations.sql b/db/queries/locations.sql index 1007dd0..1a15813 100644 --- a/db/queries/locations.sql +++ b/db/queries/locations.sql @@ -6,13 +6,15 @@ SELECT l.id, name, thumbnail, - re.regency_name, - (SELECT COALESCE(SUM(score), -1) from reviews re where re.is_from_critic = true and re.location_id = l.id) as critic_score, + COALESCE(re.regency_name, '') as regency_name, + COALESCE(pr.province_name, '') as province_name, + (SELECT COALESCE(SUM(score), 0) from reviews re where re.is_from_critic = true and re.location_id = l.id) as critic_score, (SELECT COUNT(id) from reviews re where re.is_from_critic = true and re.location_id = l.id) as critic_count, - (SELECT COALESCE(SUM(score), -1) from reviews re where re.is_from_critic = false and re.location_id = l.id) as user_score, + (SELECT COALESCE(SUM(score), 0) from reviews re where re.is_from_critic = false and re.location_id = l.id) as user_score, (SELECT COUNT(id) from reviews re where re.is_from_critic = false and re.location_id = l.id) as user_count FROM locations l JOIN regencies re on re.id = l.regency_id +JOIN provinces pr on re.province_id = pr.id WHERE approved_by IS NOT NULL ORDER BY l.created_at ASC LIMIT $1; @@ -44,4 +46,14 @@ INSERT INTO locations( google_maps_link ) values ( $1, $2, $3, $4, $5 -); \ No newline at end of file +); + +-- name: GetLocationTag :many +SELECT + name +FROM tags +WHERE tags_type = 'location' +AND +target_id = $1 +AND +approved_by IS NOT NULL; \ No newline at end of file diff --git a/db/sqlc/locations.sql.go b/db/sqlc/locations.sql.go index 53df913..c974938 100644 --- a/db/sqlc/locations.sql.go +++ b/db/sqlc/locations.sql.go @@ -87,27 +87,30 @@ SELECT l.id, name, thumbnail, - re.regency_name, - (SELECT COALESCE(SUM(score), -1) from reviews re where re.is_from_critic = true and re.location_id = l.id) as critic_score, + COALESCE(re.regency_name, '') as regency_name, + COALESCE(pr.province_name, '') as province_name, + (SELECT COALESCE(SUM(score), 0) from reviews re where re.is_from_critic = true and re.location_id = l.id) as critic_score, (SELECT COUNT(id) from reviews re where re.is_from_critic = true and re.location_id = l.id) as critic_count, - (SELECT COALESCE(SUM(score), -1) from reviews re where re.is_from_critic = false and re.location_id = l.id) as user_score, + (SELECT COALESCE(SUM(score), 0) from reviews re where re.is_from_critic = false and re.location_id = l.id) as user_score, (SELECT COUNT(id) from reviews re where re.is_from_critic = false and re.location_id = l.id) as user_count FROM locations l JOIN regencies re on re.id = l.regency_id +JOIN provinces pr on re.province_id = pr.id WHERE approved_by IS NOT NULL ORDER BY l.created_at ASC LIMIT $1 ` type GetListRecentLocationsWithRatingsRow struct { - ID int32 `json:"id"` - Name string `json:"name"` - Thumbnail sql.NullString `json:"thumbnail"` - RegencyName sql.NullString `json:"regency_name"` - CriticScore interface{} `json:"critic_score"` - CriticCount int64 `json:"critic_count"` - UserScore interface{} `json:"user_score"` - UserCount int64 `json:"user_count"` + ID int32 `json:"id"` + Name string `json:"name"` + Thumbnail sql.NullString `json:"thumbnail"` + RegencyName string `json:"regency_name"` + ProvinceName string `json:"province_name"` + CriticScore interface{} `json:"critic_score"` + CriticCount int64 `json:"critic_count"` + UserScore interface{} `json:"user_score"` + UserCount int64 `json:"user_count"` } func (q *Queries) GetListRecentLocationsWithRatings(ctx context.Context, limit int32) ([]GetListRecentLocationsWithRatingsRow, error) { @@ -124,6 +127,7 @@ func (q *Queries) GetListRecentLocationsWithRatings(ctx context.Context, limit i &i.Name, &i.Thumbnail, &i.RegencyName, + &i.ProvinceName, &i.CriticScore, &i.CriticCount, &i.UserScore, @@ -189,3 +193,37 @@ func (q *Queries) GetLocation(ctx context.Context, id int32) (GetLocationRow, er ) return i, err } + +const getLocationTag = `-- name: GetLocationTag :many +SELECT + name +FROM tags +WHERE tags_type = 'location' +AND +target_id = $1 +AND +approved_by IS NOT NULL +` + +func (q *Queries) GetLocationTag(ctx context.Context, targetID int32) ([]string, error) { + rows, err := q.db.QueryContext(ctx, getLocationTag, targetID) + if err != nil { + return nil, err + } + defer rows.Close() + items := []string{} + for rows.Next() { + var name string + if err := rows.Scan(&name); err != nil { + return nil, err + } + items = append(items, name) + } + 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/models.go b/db/sqlc/models.go index 5bf65a1..ee4f957 100644 --- a/db/sqlc/models.go +++ b/db/sqlc/models.go @@ -195,12 +195,12 @@ type Review struct { } type Tag struct { - 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"` + ID int32 `json:"id"` + Name string `json:"name"` + SubmittedBy int32 `json:"submitted_by"` + TargetID int32 `json:"target_id"` + TagsType string `json:"tags_type"` + ApprovedBy sql.NullInt32 `json:"approved_by"` } type User struct { diff --git a/db/sqlc/querier.go b/db/sqlc/querier.go index 349c0ec..47bea7a 100644 --- a/db/sqlc/querier.go +++ b/db/sqlc/querier.go @@ -15,6 +15,7 @@ type Querier interface { GetListLocations(ctx context.Context) ([]Location, error) GetListRecentLocationsWithRatings(ctx context.Context, limit int32) ([]GetListRecentLocationsWithRatingsRow, error) GetLocation(ctx context.Context, id int32) (GetLocationRow, error) + GetLocationTag(ctx context.Context, targetID int32) ([]string, error) UpdatePassword(ctx context.Context, arg UpdatePasswordParams) error UpdateUser(ctx context.Context, arg UpdateUserParams) (User, error) }