Skip to content

Commit

Permalink
🐛 Fix task filtering and sorting. (#659)
Browse files Browse the repository at this point in the history
Fix issues reported
[here](konveyor/tackle2-ui#1931 (comment)).
1. sorting seems to work only for `id` field i.e. 
2. filtering not supported for `createUser` and `id` fields( both listed
in the enhancement)
3. application name is missing in the response and in filtering.
Filtering by application ID works but this is not so nice.
4. `priority` is missing in the response and in the filtering
5. for `kind` the filtering is supported by the server but it seems it's
not included in the response

---

Filter and sorting: needed to support GORM join queries which qualifies
fields by table.

Example: For a task query joined with application, produces all of the
task fields qualified as table.column:
```
SELECT task.id, task.name ...
```
And the (joined) application fields prefixed with `APPLICATION__`
```
SELECT APPLICATION__ID, APPLICATION__NAME ...
```

---------

Signed-off-by: Jeff Ortel <jortel@redhat.com>
  • Loading branch information
jortel committed Jun 13, 2024
1 parent 609bcac commit 1366d95
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 12 deletions.
5 changes: 5 additions & 0 deletions api/filter/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -332,13 +332,18 @@ func (f *Field) Expand() (expanded []Field) {
// split field name.
// format: resource.name
// The resource may be "" (anonymous).
// The (.) separator is escaped when preceded by (\).
func (f *Field) split() (relation string, name string) {
s := f.Field.Value
mark := strings.Index(s, ".")
if mark == -1 {
name = s
return
}
if mark > 0 && s[mark-1] == '\\' {
name = s[:mark-1] + s[mark:]
return
}
relation = s[:mark]
name = s[mark+1:]
return
Expand Down
53 changes: 44 additions & 9 deletions api/sort/sort.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,29 @@ type Clause struct {

// Sort provides sorting.
type Sort struct {
fields map[string]interface{}
fields map[string]any
alias map[string]string
clauses []Clause
}

// Add adds virtual field and aliases.
func (r *Sort) Add(name string, aliases ...string) {
r.init()
for _, alias := range aliases {
alias = strings.ToLower(alias)
r.fields[alias] = 0
r.alias[alias] = name
}
}

// With context.
func (r *Sort) With(ctx *gin.Context, m interface{}) (err error) {
func (r *Sort) With(ctx *gin.Context, m any) (err error) {
param := ctx.Query("sort")
if param == "" {
return
}
r.fields = r.inspect(m)
r.init()
r.inspect(m)
for _, s := range strings.Split(param, ",") {
clause := Clause{}
s = strings.TrimSpace(s)
Expand All @@ -38,7 +50,7 @@ func (r *Sort) With(ctx *gin.Context, m interface{}) (err error) {
err = &SortError{s}
return
}
clause.name = s
clause.name = r.resolved(s)
r.clauses = append(
r.clauses,
clause)
Expand All @@ -55,7 +67,7 @@ func (r *Sort) With(ctx *gin.Context, m interface{}) (err error) {
err = &SortError{s}
return
}
clause.name = s
clause.name = r.resolved(s)
r.clauses = append(
r.clauses,
clause)
Expand All @@ -66,6 +78,7 @@ func (r *Sort) With(ctx *gin.Context, m interface{}) (err error) {

// Sorted returns sorted DB.
func (r *Sort) Sorted(in *gorm.DB) (out *gorm.DB) {
r.init()
out = in
if len(r.clauses) == 0 {
return
Expand All @@ -78,11 +91,33 @@ func (r *Sort) Sorted(in *gorm.DB) (out *gorm.DB) {
return
}

// init allocate maps.
func (r *Sort) init() {
if r.fields == nil {
r.fields = make(map[string]any)
}
if r.alias == nil {
r.alias = make(map[string]string)
}
}

// inspect object and return fields.
func (r *Sort) inspect(m interface{}) (fields map[string]interface{}) {
fields = reflect.Fields(m)
for key, v := range fields {
fields[strings.ToLower(key)] = v
func (r *Sort) inspect(m any) {
r.init()
for key, v := range reflect.Fields(m) {
key = strings.ToLower(key)
r.fields[key] = v
}
return
}

// resolved returns field names with alias resolved.
func (r *Sort) resolved(in string) (out string) {
r.init()
out = in
alias, found := r.alias[in]
if found {
out = alias
}
return
}
23 changes: 20 additions & 3 deletions api/task.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,26 +114,32 @@ func (h TaskHandler) Get(ctx *gin.Context) {
// @description List all tasks.
// @description Filters:
// @description - kind
// @description - createUser
// @description - addon
// @description - name
// @description - locator
// @description - state
// @description - application.id
// @description - application.name
// @description The state=queued is an alias for queued states.
// @tags tasks
// @produce json
// @success 200 {object} []api.Task
// @router /tasks [get]
func (h TaskHandler) List(ctx *gin.Context) {
resources := []Task{}
// filter
filter, err := qf.New(ctx,
[]qf.Assert{
{Field: "id", Kind: qf.LITERAL},
{Field: "createUser", Kind: qf.STRING},
{Field: "kind", Kind: qf.STRING},
{Field: "addon", Kind: qf.STRING},
{Field: "name", Kind: qf.STRING},
{Field: "locator", Kind: qf.STRING},
{Field: "state", Kind: qf.STRING},
{Field: "application.id", Kind: qf.STRING},
{Field: "application.name", Kind: qf.STRING},
})
if err != nil {
_ = ctx.Error(err)
Expand All @@ -157,17 +163,28 @@ func (h TaskHandler) List(ctx *gin.Context) {
values = values.Join(qf.OR)
filter = filter.Revalued("state", values)
}
filter = filter.Renamed("application.id", "application__id")
filter = filter.Renamed("application.name", "application__name")
filter = filter.Renamed("createUser", "task\\.createUser")
filter = filter.Renamed("id", "task\\.id")
filter = filter.Renamed("name", "task\\.name")
// sort
sort := Sort{}
err = sort.With(ctx, &model.Issue{})
sort.Add("task.id", "id")
sort.Add("task.createUser", "createUser")
sort.Add("task.name", "name")
sort.Add("application__id", "application.id")
sort.Add("application__name", "application.name")
err = sort.With(ctx, &model.Task{})
if err != nil {
_ = ctx.Error(err)
return
}
// Fetch
db := h.DB(ctx)
db = db.Model(&model.Task{})
db = db.Preload(clause.Associations)
db = db.Joins("Application")
db = sort.Sorted(db)
filter = filter.Renamed("application.id", "applicationId")
db = filter.Where(db)
var m model.Task
var list []model.Task
Expand Down

0 comments on commit 1366d95

Please sign in to comment.