Skip to content

Commit d80d350

Browse files
committed
Added timestamp filter to list work packages
- introduced new design of how to write filters
1 parent c7f42ca commit d80d350

File tree

7 files changed

+165
-4
lines changed

7 files changed

+165
-4
lines changed

cmd/list/work_packages.go

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ package list
22

33
import (
44
"fmt"
5+
"github.com/opf/openproject-cli/components/requests"
6+
"github.com/opf/openproject-cli/components/resources"
7+
"github.com/opf/openproject-cli/components/resources/work_packages/filters"
58
"os"
69
"regexp"
710
"strconv"
@@ -23,6 +26,10 @@ var typeFilter string
2326
var includeSubProjects bool
2427
var subProject string
2528

29+
var activeFilters = []resources.Filter{
30+
filters.NewTimestampFilter(),
31+
}
32+
2633
var workPackagesCmd = &cobra.Command{
2734
Use: "workpackages",
2835
Aliases: []string{"wps"},
@@ -32,12 +39,19 @@ var workPackagesCmd = &cobra.Command{
3239
}
3340

3441
func listWorkPackages(_ *cobra.Command, _ []string) {
42+
// This needs to be removed, once all filters are built the "new" way
3543
if errorText := validateCommandFlags(); len(errorText) > 0 {
3644
printer.ErrorText(errorText)
3745
return
3846
}
3947

40-
collection, err := work_packages.All(filterOptions(), showTotal)
48+
query, err := buildQuery()
49+
if err != nil {
50+
printer.ErrorText(err.Error())
51+
return
52+
}
53+
54+
collection, err := work_packages.All(filterOptions(), query, showTotal)
4155
switch {
4256
case err == nil && showTotal:
4357
printer.Number(collection.Total)
@@ -59,6 +73,21 @@ func validateCommandFlags() (errorText string) {
5973
}
6074
}
6175

76+
func buildQuery() (requests.Query, error) {
77+
var q requests.Query
78+
79+
for _, filter := range activeFilters {
80+
err := filter.ValidateInput()
81+
if err != nil {
82+
return requests.NewEmptyQuery(), err
83+
}
84+
85+
q = q.Merge(filter.Query())
86+
}
87+
88+
return q, nil
89+
}
90+
6291
func filterOptions() *map[work_packages.FilterOption]string {
6392
options := make(map[work_packages.FilterOption]string)
6493

cmd/list/work_packages_flags.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
package list
22

3+
import (
4+
"github.com/opf/openproject-cli/components/resources"
5+
)
6+
37
func initWorkPackagesFlags() {
48
workPackagesCmd.Flags().StringVarP(
59
&assignee,
@@ -71,4 +75,17 @@ the default is false.`)
7175
"",
7276
false,
7377
"Show only the total number of work packages matching the filter options.")
78+
79+
for _, filter := range activeFilters {
80+
switch filter.(type) {
81+
case resources.StringValueFilter:
82+
workPackagesCmd.Flags().StringVarP(
83+
filter.Value(),
84+
filter.Name(),
85+
filter.ShortHand(),
86+
filter.(resources.StringValueFilter).DefaultValue(),
87+
filter.Usage(),
88+
)
89+
}
90+
}
7491
}

components/requests/query.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,24 @@ func (filter Filter) String() string {
2626
)
2727
}
2828

29+
func (query Query) Merge(another Query) Query {
30+
filters := append(query.filters, another.filters...)
31+
32+
attributes := query.attributes
33+
if attributes == nil {
34+
attributes = make(map[string]string)
35+
}
36+
37+
for key, value := range another.attributes {
38+
attributes[key] = value
39+
}
40+
41+
return Query{
42+
attributes: attributes,
43+
filters: filters,
44+
}
45+
}
46+
2947
func (query Query) String() string {
3048
queryStr := filtersQueryAttribute(query.filters)
3149
for key, value := range query.attributes {
@@ -60,6 +78,10 @@ func NewQuery(attributes map[string]string, filters []Filter) Query {
6078
return Query{attributes: attributes, filters: filters}
6179
}
6280

81+
func NewEmptyQuery() Query {
82+
return Query{attributes: make(map[string]string), filters: []Filter{}}
83+
}
84+
6385
func NewFilterQuery(filters []Filter) Query {
6486
attributes := map[string]string{
6587
"pageSize": "100",

components/requests/query_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,3 +99,31 @@ func TestQuery_String(t *testing.T) {
9999
t.Errorf("Expected %s to contain %s", queryString, expected)
100100
}
101101
}
102+
103+
func TestQuery_Merge(t *testing.T) {
104+
attributes1 := map[string]string{
105+
"pageSize": "20",
106+
"timestamps": "PT0S",
107+
}
108+
filters1 := []requests.Filter{
109+
work_packages.StatusFilter("1,3"),
110+
}
111+
112+
attributes2 := map[string]string{
113+
"pageSize": "25",
114+
"includeSubprojects": "true",
115+
}
116+
filters2 := []requests.Filter{
117+
work_packages.TypeFilter("!1"),
118+
}
119+
120+
query1 := requests.NewQuery(attributes1, filters1)
121+
query2 := requests.NewQuery(attributes2, filters2)
122+
123+
result := query1.Merge(query2)
124+
expected := "filters=%5B%7B%22status%22%3A%7B%22operator%22%3A%22%3D%22%2C%22values%22%3A%5B%221%22%2C%223%22%5D%7D%7D%2C%7B%22type%22%3A%7B%22operator%22%3A%22%21%22%2C%22values%22%3A%5B%221%22%5D%7D%7D%5D&pageSize=25&timestamps=PT0S&includeSubprojects=true"
125+
126+
if result.String() != expected {
127+
t.Errorf("Expected %s, but got %s", expected, result.String())
128+
}
129+
}

components/resources/filters.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,16 @@ func TypeAheadFilter(input string) requests.Filter {
99
Values: []string{input},
1010
}
1111
}
12+
13+
type Filter interface {
14+
Value() *string
15+
Name() string
16+
ShortHand() string
17+
Usage() string
18+
ValidateInput() error
19+
Query() requests.Query
20+
}
21+
22+
type StringValueFilter interface {
23+
DefaultValue() string
24+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package filters
2+
3+
import (
4+
"time"
5+
6+
"github.com/opf/openproject-cli/components/requests"
7+
)
8+
9+
type TimestampFilter struct {
10+
value string
11+
}
12+
13+
func (f *TimestampFilter) Value() *string {
14+
return &f.value
15+
}
16+
17+
func (f *TimestampFilter) Name() string {
18+
return "timestamp"
19+
}
20+
21+
func (f *TimestampFilter) ShortHand() string {
22+
return ""
23+
}
24+
25+
func (f *TimestampFilter) Usage() string {
26+
return `Returns the list of work packages at a specific timestamp. The timestamp should
27+
be in the format of 'YYYY-MM-DDTHH:MM:SSZ' or date only 'YYYY-MM-DD', which
28+
then assumes the time being 00:00:00Z.`
29+
}
30+
31+
func (f *TimestampFilter) ValidateInput() error {
32+
_, err := time.Parse(time.DateOnly, f.value)
33+
if err == nil {
34+
return nil
35+
}
36+
37+
_, err = time.Parse(time.RFC3339, f.value)
38+
return err
39+
}
40+
41+
func (f *TimestampFilter) DefaultValue() string {
42+
return ""
43+
}
44+
45+
func (f *TimestampFilter) Query() requests.Query {
46+
return requests.NewQuery(map[string]string{"timestamps": f.value}, nil)
47+
}
48+
49+
func NewTimestampFilter() *TimestampFilter {
50+
return &TimestampFilter{}
51+
}

components/resources/work_packages/read.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ func Lookup(id uint64) (*models.WorkPackage, error) {
1919
return workPackage.Convert(), nil
2020
}
2121

22-
func All(filterOptions *map[FilterOption]string, showOnlyTotal bool) (*models.WorkPackageCollection, error) {
22+
func All(filterOptions *map[FilterOption]string, query requests.Query, showOnlyTotal bool) (*models.WorkPackageCollection, error) {
2323
var filters []requests.Filter
2424
var projectId *uint64
2525
var queryAttributes = make(map[string]string)
@@ -50,15 +50,16 @@ func All(filterOptions *map[FilterOption]string, showOnlyTotal bool) (*models.Wo
5050
queryAttributes["pageSize"] = "-1"
5151
}
5252

53-
query := requests.NewQuery(queryAttributes, filters)
53+
legacyQuery := requests.NewQuery(queryAttributes, filters)
54+
newQuery := legacyQuery.Merge(query)
5455

5556
requestUrl := paths.WorkPackages()
5657

5758
if projectId != nil {
5859
requestUrl = paths.ProjectWorkPackages(*projectId)
5960
}
6061

61-
response, err := requests.Get(requestUrl, &query)
62+
response, err := requests.Get(requestUrl, &newQuery)
6263
if err != nil {
6364
return nil, err
6465
}

0 commit comments

Comments
 (0)