Skip to content

Commit

Permalink
feat: Allow project filter in raw jql (#395)
Browse files Browse the repository at this point in the history
  • Loading branch information
ankitpokhrel committed Jun 20, 2022
1 parent d75d29d commit f32f2d8
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 9 deletions.
5 changes: 4 additions & 1 deletion internal/cmd/issue/list/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,10 @@ $ jira issue list --plain --no-truncate
$ jira issue list -tEpic -sDone
# List issues in status other than "Open" and is assigned to no one
$ jira issue list -s~Open -ax`
$ jira issue list -s~Open -ax
# List issues from all projects
$ jira issue list -q"project IS NOT EMPTY"`
)

// NewCmdList is a list command.
Expand Down
21 changes: 15 additions & 6 deletions internal/query/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,25 @@ func NewIssue(project string, flags FlagParser) (*Issue, error) {

// Get returns constructed jql query.
func (i *Issue) Get() string {
var q *jql.JQL

defer func() {
if i.params.debug {
fmt.Printf("JQL: %s\n", q.String())
}
}()

q, obf := jql.NewJQL(i.Project), i.params.OrderBy
if obf == "created" &&
(i.params.Updated != "" || i.params.UpdatedBefore != "" || i.params.UpdatedAfter != "") &&
(i.params.Created == "" && i.params.CreatedBefore == "" && i.params.CreatedAfter == "") {
obf = "updated"
}

if i.params.jql != "" {
q.Raw(i.params.jql)
}

q.And(func() {
if i.params.Latest {
q.History()
Expand All @@ -65,17 +78,13 @@ func (i *Issue) Get() string {
q.In("labels", i.params.Labels...)
}
})

if i.params.Reverse {
q.OrderBy(obf, jql.DirectionAscending)
} else {
q.OrderBy(obf, jql.DirectionDescending)
}
if i.params.debug {
fmt.Printf("JQL: %s\n", q.String())
}
if i.params.jql != "" {
q.And(func() { q.Raw(i.params.jql) })
}

return q.String()
}

Expand Down
4 changes: 2 additions & 2 deletions internal/query/issue_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -396,9 +396,9 @@ func TestIssueGet(t *testing.T) {
assert.NoError(t, err)
return i
},
expected: `project="TEST" AND issue IN issueHistory() AND issue IN watchedIssues() AND ` +
expected: `project="TEST" AND summary ~ cli OR x = y AND issue IN issueHistory() AND issue IN watchedIssues() AND ` +
`type="test" AND resolution="test" AND status="test" AND priority="test" AND reporter="test" ` +
`AND assignee="test" AND component="test" AND parent="test" AND summary ~ cli OR x = y ORDER BY lastViewed ASC`,
`AND assignee="test" AND component="test" AND parent="test" ORDER BY lastViewed ASC`,
},
}

Expand Down
12 changes: 12 additions & 0 deletions pkg/jql/jql.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package jql

import (
"fmt"
"regexp"
"strings"
)

Expand Down Expand Up @@ -158,9 +159,13 @@ func (j *JQL) Or(fn GroupFunc) *JQL {

// Raw sets the passed JQL query along with project context.
func (j *JQL) Raw(q string) *JQL {
q = strings.TrimSpace(q)
if q == "" {
return j
}
if hasProjectFilter(q) {
j.filters = j.filters[1:]
}
j.filters = append(j.filters, q)
return j
}
Expand Down Expand Up @@ -196,5 +201,12 @@ func (j *JQL) compile() string {
if j.orderBy != "" {
q += " " + j.orderBy
}

return q
}

func hasProjectFilter(str string) bool {
regx := "(?i)((project)[\\s]*?={0,1}\\b)[^'.']"
m, _ := regexp.MatchString(regx, str)
return m
}
96 changes: 96 additions & 0 deletions pkg/jql/jql_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,20 @@ func TestJQL(t *testing.T) {
},
expected: "project=\"TEST\" AND type=\"Story\" AND resolution=\"Done\" AND summary !~ cli AND priority = high",
},
{
name: "it queries with raw jql and project filter",
initialize: func() *JQL {
jql := NewJQL("TEST")
jql.And(func() {
jql.
FilterBy("type", "Story").
FilterBy("resolution", "Done").
Raw("summary !~ cli AND project = TEST1")
})
return jql
},
expected: "type=\"Story\" AND resolution=\"Done\" AND summary !~ cli AND project = TEST1",
},
{
name: "it queries with raw jql and or filter",
initialize: func() *JQL {
Expand All @@ -236,6 +250,18 @@ func TestJQL(t *testing.T) {
},
expected: "project=\"TEST\" OR type=\"Story\" OR summary ~ cli",
},
{
name: "it queries with raw jql and project filter in or condition",
initialize: func() *JQL {
jql := NewJQL("TEST")
jql.Or(func() {
jql.FilterBy("type", "Story").
Raw("summary ~ cli AND project IN (TEST1,TEST2)")
})
return jql
},
expected: "type=\"Story\" OR summary ~ cli AND project IN (TEST1,TEST2)",
},
}

for _, tc := range cases {
Expand All @@ -249,3 +275,73 @@ func TestJQL(t *testing.T) {
})
}
}

func TestHasProject(t *testing.T) {
cases := []struct {
input string
expected bool
}{
{
input: "project=",
expected: true,
},
{
input: "project = TEST",
expected: true,
},
{
input: "project = TEST",
expected: true,
},
{
input: " assigned = abc and PROJECT = TEST ",
expected: true,
},
{
input: "assigned = abc and project = TEST and project.property=abc",
expected: true,
},
{
input: "PROJECT IS NOT EMPTY AND assignee IN (currentUser())",
expected: true,
},
{
input: "PROJECT IN (TEST, TEST1) AND assignee IN (currentUser())",
expected: true,
},
{
input: "PROJECT NOT IN (TEST,TEST1) AND assignee IN (currentUser())",
expected: true,
},
{
input: "PROJECT != TEST AND projectType=\"classic\" AND assignee IS EMPTY",
expected: true,
},
{
input: "project",
expected: false,
},
{
input: "projectType",
expected: false,
},
{
input: "project.property = ABC",
expected: false,
},
{
input: "projectType=\"classic\" AND type=\"Story\" AND assignee IS EMPTY",
expected: false,
},
}

for _, tc := range cases {
tc := tc

t.Run("", func(t *testing.T) {
t.Parallel()

assert.Equal(t, tc.expected, hasProjectFilter(tc.input))
})
}
}

0 comments on commit f32f2d8

Please sign in to comment.