Skip to content

Commit

Permalink
Added pagination, Filtering and Sorting and implemented count endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
AbdulWahab3181 committed May 26, 2024
1 parent d4a0dd3 commit c95e88d
Show file tree
Hide file tree
Showing 5 changed files with 251 additions and 74 deletions.
129 changes: 119 additions & 10 deletions db/features.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,20 +181,129 @@ func (db database) DeleteFeatureStoryByUuid(featureUuid, storyUuid string) error
return nil
}

func (db database) GetBountyByFeatureAndPhaseUuid(featureUuid string, phaseUuid string) (Bounty, error) {
bounty := Bounty{}
result := db.db.Model(&Bounty{}).
func (db database) GetBountiesByFeatureAndPhaseUuid(featureUuid string, phaseUuid string, r *http.Request) ([]NewBounty, error) {
keys := r.URL.Query()
tags := keys.Get("tags")
offset, limit, sortBy, direction, search := utils.GetPaginationParams(r)
open := keys.Get("Open")
assigned := keys.Get("Assigned")
completed := keys.Get("Completed")
paid := keys.Get("Paid")
languages := keys.Get("languages")
languageArray := strings.Split(languages, ",")

var bounties []NewBounty

// Initialize the query with the necessary joins and initial filters
query := db.db.Model(&Bounty{}).
Select("bounty.*").
Joins(`INNER JOIN "feature_phases" ON "feature_phases"."uuid" = "bounty"."phase_uuid" `).
Where(`"feature_phases"."feature_uuid" = ? AND "feature_phases"."uuid" = ?`, featureUuid, phaseUuid).
Order(`"bounty"."id"`).
Limit(1).
First(&bounty)
Joins(`INNER JOIN "feature_phases" ON "feature_phases"."uuid" = "bounty"."phase_uuid"`).
Where(`"feature_phases"."feature_uuid" = ? AND "feature_phases"."uuid" = ?`, featureUuid, phaseUuid)

// Add pagination if applicable
if limit > 0 {
query = query.Limit(limit).Offset(offset)
}

// Add sorting if applicable
if sortBy != "" && direction != "" {
query = query.Order(fmt.Sprintf("%s %s", sortBy, direction))
} else {
query = query.Order("created_at DESC")
}

// Add search filter
if search != "" {
searchQuery := fmt.Sprintf("LOWER(title) LIKE ?", "%"+strings.ToLower(search)+"%")
query = query.Where(searchQuery)
}

// Add language filter
if len(languageArray) > 0 {
for _, lang := range languageArray {
if lang != "" {
query = query.Where("coding_languages @> ARRAY[?]::varchar[]", lang)
}
}
}

// Add status filters
var statusConditions []string

if open == "true" {
statusConditions = append(statusConditions, "assignee = '' AND paid != true")
}
if assigned == "true" {
statusConditions = append(statusConditions, "assignee != '' AND paid = false")
}
if completed == "true" {
statusConditions = append(statusConditions, "assignee != '' AND completed = true AND paid = false")
}
if paid == "true" {
statusConditions = append(statusConditions, "paid = true")
}

if len(statusConditions) > 0 {
query = query.Where(strings.Join(statusConditions, " OR "))
}

// Execute the query
result := query.Find(&bounties)

if result.RowsAffected == 0 {
return bounty, errors.New("no bounty found")
return bounties, errors.New("no bounty found")
}
return bounty, nil

// Handle tags if any
if tags != "" {
t := strings.Split(tags, ",")
for _, s := range t {
query = query.Where("? = ANY (tags)", s)
}
query.Scan(&bounties)
}

return bounties, nil
}

func (db database) GetBountiesCountByFeatureAndPhaseUuid(featureUuid string, phaseUuid string, r *http.Request) int64 {
keys := r.URL.Query()
open := keys.Get("Open")
assigned := keys.Get("Assigned")
completed := keys.Get("Completed")
paid := keys.Get("Paid")

// Initialize the query with the necessary joins and initial filters
query := db.db.Model(&Bounty{}).
Select("COUNT(*)").
Joins(`INNER JOIN "feature_phases" ON "feature_phases"."uuid" = "bounty"."phase_uuid"`).
Where(`"feature_phases"."feature_uuid" = ? AND "feature_phases"."uuid" = ?`, featureUuid, phaseUuid)

// Add status filters
var statusConditions []string

if open == "true" {
statusConditions = append(statusConditions, "assignee = '' AND paid != true")
}
if assigned == "true" {
statusConditions = append(statusConditions, "assignee != '' AND paid = false")
}
if completed == "true" {
statusConditions = append(statusConditions, "assignee != '' AND completed = true AND paid = false")
}
if paid == "true" {
statusConditions = append(statusConditions, "paid = true")
}

if len(statusConditions) > 0 {
query = query.Where(strings.Join(statusConditions, " OR "))
}

var count int64

query.Count(&count)

return count
}

func (db database) GetPhaseByUuid(phaseUuid string) (FeaturePhase, error) {
Expand Down
3 changes: 2 additions & 1 deletion db/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ type Database interface {
GetFeatureStoryByUuid(featureUuid, storyUuid string) (FeatureStory, error)
DeleteFeatureStoryByUuid(featureUuid, storyUuid string) error
DeleteFeatureByUuid(uuid string) error
GetBountyByFeatureAndPhaseUuid(featureUuid string, phaseUuid string) (Bounty, error)
GetBountiesByFeatureAndPhaseUuid(featureUuid string, phaseUuid string, r *http.Request) ([]NewBounty, error)
GetBountiesCountByFeatureAndPhaseUuid(featureUuid string, phaseUuid string, r *http.Request) int64
GetPhaseByUuid(phaseUuid string) (FeaturePhase, error)
}
25 changes: 20 additions & 5 deletions handlers/features.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@ import (
)

type featureHandler struct {
db db.Database
db db.Database
generateBountyHandler func(bounties []db.NewBounty) []db.BountyResponse
}

func NewFeatureHandler(database db.Database) *featureHandler {
bHandler := NewBountyHandler(http.DefaultClient, database)
return &featureHandler{
db: database,
db: database,
generateBountyHandler: bHandler.GenerateBountyResponse,
}
}

Expand Down Expand Up @@ -315,16 +318,28 @@ func (oh *featureHandler) DeleteStory(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(map[string]string{"message": "Story deleted successfully"})
}

func (oh *featureHandler) GetBountyByFeatureAndPhaseUuid(w http.ResponseWriter, r *http.Request) {
func (oh *featureHandler) GetBountiesByFeatureAndPhaseUuid(w http.ResponseWriter, r *http.Request) {
featureUuid := chi.URLParam(r, "feature_uuid")
phaseUuid := chi.URLParam(r, "phase_uuid")

bounty, err := oh.db.GetBountyByFeatureAndPhaseUuid(featureUuid, phaseUuid)
bounties, err := oh.db.GetBountiesByFeatureAndPhaseUuid(featureUuid, phaseUuid, r)
if err != nil {
w.WriteHeader(http.StatusNotFound)
return
}

var bountyResponse []db.BountyResponse = oh.generateBountyHandler(bounties)

w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(bountyResponse)
}

func (oh *featureHandler) GetBountiesCountByFeatureAndPhaseUuid(w http.ResponseWriter, r *http.Request) {
featureUuid := chi.URLParam(r, "feature_uuid")
phaseUuid := chi.URLParam(r, "phase_uuid")

bountiesCount := oh.db.GetBountiesCountByFeatureAndPhaseUuid(featureUuid, phaseUuid, r)

w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(bounty)
json.NewEncoder(w).Encode(bountiesCount)
}
165 changes: 108 additions & 57 deletions mocks/Database.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion routes/features.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ func FeatureRoutes() chi.Router {
r.Get("/{feature_uuid}/story", featureHandlers.GetStoriesByFeatureUuid)
r.Get("/{feature_uuid}/story/{story_uuid}", featureHandlers.GetStoryByUuid)
r.Delete("/{feature_uuid}/story/{story_uuid}", featureHandlers.DeleteStory)
r.Get("/{feature_uuid}/phase/{phase_uuid}/bounty", featureHandlers.GetBountyByFeatureAndPhaseUuid)
r.Get("/{feature_uuid}/phase/{phase_uuid}/bounty", featureHandlers.GetBountiesByFeatureAndPhaseUuid)
r.Get("/{feature_uuid}/phase/{phase_uuid}/bounty/count", featureHandlers.GetBountiesCountByFeatureAndPhaseUuid)

})
return r
Expand Down

0 comments on commit c95e88d

Please sign in to comment.