Skip to content

Commit

Permalink
Added timestamp filter to list work packages
Browse files Browse the repository at this point in the history
- introduced new design of how to write filters
  • Loading branch information
Kharonus committed May 27, 2024
1 parent c7f42ca commit d80d350
Show file tree
Hide file tree
Showing 7 changed files with 165 additions and 4 deletions.
31 changes: 30 additions & 1 deletion cmd/list/work_packages.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ package list

import (
"fmt"
"github.com/opf/openproject-cli/components/requests"
"github.com/opf/openproject-cli/components/resources"
"github.com/opf/openproject-cli/components/resources/work_packages/filters"
"os"
"regexp"
"strconv"
Expand All @@ -23,6 +26,10 @@ var typeFilter string
var includeSubProjects bool
var subProject string

var activeFilters = []resources.Filter{
filters.NewTimestampFilter(),
}

var workPackagesCmd = &cobra.Command{
Use: "workpackages",
Aliases: []string{"wps"},
Expand All @@ -32,12 +39,19 @@ var workPackagesCmd = &cobra.Command{
}

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

collection, err := work_packages.All(filterOptions(), showTotal)
query, err := buildQuery()
if err != nil {
printer.ErrorText(err.Error())
return
}

collection, err := work_packages.All(filterOptions(), query, showTotal)
switch {
case err == nil && showTotal:
printer.Number(collection.Total)
Expand All @@ -59,6 +73,21 @@ func validateCommandFlags() (errorText string) {
}
}

func buildQuery() (requests.Query, error) {
var q requests.Query

for _, filter := range activeFilters {
err := filter.ValidateInput()
if err != nil {
return requests.NewEmptyQuery(), err
}

q = q.Merge(filter.Query())
}

return q, nil
}

func filterOptions() *map[work_packages.FilterOption]string {
options := make(map[work_packages.FilterOption]string)

Expand Down
17 changes: 17 additions & 0 deletions cmd/list/work_packages_flags.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package list

import (
"github.com/opf/openproject-cli/components/resources"
)

func initWorkPackagesFlags() {
workPackagesCmd.Flags().StringVarP(
&assignee,
Expand Down Expand Up @@ -71,4 +75,17 @@ the default is false.`)
"",
false,
"Show only the total number of work packages matching the filter options.")

for _, filter := range activeFilters {
switch filter.(type) {
case resources.StringValueFilter:
workPackagesCmd.Flags().StringVarP(
filter.Value(),
filter.Name(),
filter.ShortHand(),
filter.(resources.StringValueFilter).DefaultValue(),
filter.Usage(),
)
}
}
}
22 changes: 22 additions & 0 deletions components/requests/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,24 @@ func (filter Filter) String() string {
)
}

func (query Query) Merge(another Query) Query {
filters := append(query.filters, another.filters...)

attributes := query.attributes
if attributes == nil {
attributes = make(map[string]string)
}

for key, value := range another.attributes {
attributes[key] = value
}

return Query{
attributes: attributes,
filters: filters,
}
}

func (query Query) String() string {
queryStr := filtersQueryAttribute(query.filters)
for key, value := range query.attributes {
Expand Down Expand Up @@ -60,6 +78,10 @@ func NewQuery(attributes map[string]string, filters []Filter) Query {
return Query{attributes: attributes, filters: filters}
}

func NewEmptyQuery() Query {
return Query{attributes: make(map[string]string), filters: []Filter{}}
}

func NewFilterQuery(filters []Filter) Query {
attributes := map[string]string{
"pageSize": "100",
Expand Down
28 changes: 28 additions & 0 deletions components/requests/query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,31 @@ func TestQuery_String(t *testing.T) {
t.Errorf("Expected %s to contain %s", queryString, expected)
}
}

func TestQuery_Merge(t *testing.T) {
attributes1 := map[string]string{
"pageSize": "20",
"timestamps": "PT0S",
}
filters1 := []requests.Filter{
work_packages.StatusFilter("1,3"),
}

attributes2 := map[string]string{
"pageSize": "25",
"includeSubprojects": "true",
}
filters2 := []requests.Filter{
work_packages.TypeFilter("!1"),
}

query1 := requests.NewQuery(attributes1, filters1)
query2 := requests.NewQuery(attributes2, filters2)

result := query1.Merge(query2)
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"

if result.String() != expected {
t.Errorf("Expected %s, but got %s", expected, result.String())
}
}
13 changes: 13 additions & 0 deletions components/resources/filters.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,16 @@ func TypeAheadFilter(input string) requests.Filter {
Values: []string{input},
}
}

type Filter interface {
Value() *string
Name() string
ShortHand() string
Usage() string
ValidateInput() error
Query() requests.Query
}

type StringValueFilter interface {
DefaultValue() string
}
51 changes: 51 additions & 0 deletions components/resources/work_packages/filters/timestamp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package filters

import (
"time"

"github.com/opf/openproject-cli/components/requests"
)

type TimestampFilter struct {
value string
}

func (f *TimestampFilter) Value() *string {
return &f.value
}

func (f *TimestampFilter) Name() string {
return "timestamp"
}

func (f *TimestampFilter) ShortHand() string {
return ""
}

func (f *TimestampFilter) Usage() string {
return `Returns the list of work packages at a specific timestamp. The timestamp should
be in the format of 'YYYY-MM-DDTHH:MM:SSZ' or date only 'YYYY-MM-DD', which
then assumes the time being 00:00:00Z.`
}

func (f *TimestampFilter) ValidateInput() error {
_, err := time.Parse(time.DateOnly, f.value)
if err == nil {
return nil
}

_, err = time.Parse(time.RFC3339, f.value)
return err
}

func (f *TimestampFilter) DefaultValue() string {
return ""
}

func (f *TimestampFilter) Query() requests.Query {
return requests.NewQuery(map[string]string{"timestamps": f.value}, nil)
}

func NewTimestampFilter() *TimestampFilter {
return &TimestampFilter{}
}
7 changes: 4 additions & 3 deletions components/resources/work_packages/read.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ func Lookup(id uint64) (*models.WorkPackage, error) {
return workPackage.Convert(), nil
}

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

query := requests.NewQuery(queryAttributes, filters)
legacyQuery := requests.NewQuery(queryAttributes, filters)
newQuery := legacyQuery.Merge(query)

requestUrl := paths.WorkPackages()

if projectId != nil {
requestUrl = paths.ProjectWorkPackages(*projectId)
}

response, err := requests.Get(requestUrl, &query)
response, err := requests.Get(requestUrl, &newQuery)
if err != nil {
return nil, err
}
Expand Down

0 comments on commit d80d350

Please sign in to comment.