diff --git a/filter.go b/filter.go index 99fd166..666153a 100644 --- a/filter.go +++ b/filter.go @@ -38,6 +38,84 @@ type GetMyFiltersQueryOptions struct { Expand string `url:"expand,omitempty"` } +// FiltersList reflects a list of filters +type FiltersList struct { + MaxResults int `json:"maxResults" structs:"maxResults"` + StartAt int `json:"startAt" structs:"startAt"` + Total int `json:"total" structs:"total"` + IsLast bool `json:"isLast" structs:"isLast"` + Values []FiltersListItem `json:"values" structs:"values"` +} + +// FiltersListItem represents a Filter of FiltersList in Jira +type FiltersListItem struct { + Self string `json:"self"` + ID string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + Owner User `json:"owner"` + Jql string `json:"jql"` + ViewURL string `json:"viewUrl"` + SearchURL string `json:"searchUrl"` + Favourite bool `json:"favourite"` + FavouritedCount int `json:"favouritedCount"` + SharePermissions []interface{} `json:"sharePermissions"` + Subscriptions []struct { + ID int `json:"id"` + User User `json:"user"` + } `json:"subscriptions"` +} + +// FilterSearchOptions specifies the optional parameters for the Search method +// https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-rest-api-3-filter-search-get +type FilterSearchOptions struct { + // String used to perform a case-insensitive partial match with name. + FilterName string `url:"filterName,omitempty"` + + // User account ID used to return filters with the matching owner.accountId. This parameter cannot be used with owner. + AccountID string `url:"accountId,omitempty"` + + // Group name used to returns filters that are shared with a group that matches sharePermissions.group.groupname. + GroupName string `url:"groupname,omitempty"` + + // Project ID used to returns filters that are shared with a project that matches sharePermissions.project.id. + // Format: int64 + ProjectID int64 `url:"projectId,omitempty"` + + // Orders the results using one of these filter properties. + // - `description` Orders by filter `description`. Note that this ordering works independently of whether the expand to display the description field is in use. + // - `favourite_count` Orders by `favouritedCount`. + // - `is_favourite` Orders by `favourite`. + // - `id` Orders by filter `id`. + // - `name` Orders by filter `name`. + // - `owner` Orders by `owner.accountId`. + // + // Default: `name` + // + // Valid values: id, name, description, owner, favorite_count, is_favorite, -id, -name, -description, -owner, -favorite_count, -is_favorite + OrderBy string `url:"orderBy,omitempty"` + + // The index of the first item to return in a page of results (page offset). + // Default: 0, Format: int64 + StartAt int64 `url:"startAt,omitempty"` + + // The maximum number of items to return per page. The maximum is 100. + // Default: 50, Format: int32 + MaxResults int32 `url:"maxResults,omitempty"` + + // Use expand to include additional information about filter in the response. This parameter accepts multiple values separated by a comma: + // - description Returns the description of the filter. + // - favourite Returns an indicator of whether the user has set the filter as a favorite. + // - favouritedCount Returns a count of how many users have set this filter as a favorite. + // - jql Returns the JQL query that the filter uses. + // - owner Returns the owner of the filter. + // - searchUrl Returns a URL to perform the filter's JQL query. + // - sharePermissions Returns the share permissions defined for the filter. + // - subscriptions Returns the users that are subscribed to the filter. + // - viewUrl Returns a URL to view the filter. + Expand string `url:"expand,omitempty"` +} + // GetList retrieves all filters from Jira func (fs *FilterService) GetList() ([]*Filter, *Response, error) { @@ -118,5 +196,29 @@ func (fs *FilterService) GetMyFilters(opts *GetMyFiltersQueryOptions) ([]*Filter jerr := NewJiraError(resp, err) return nil, resp, jerr } + return filters, resp, nil +} + +// Search will search for filter according to the search options +// +// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/#api-rest-api-3-filter-search-get +func (fs *FilterService) Search(opt *FilterSearchOptions) (*FiltersList, *Response, error) { + apiEndpoint := "rest/api/3/filter/search" + url, err := addOptions(apiEndpoint, opt) + if err != nil { + return nil, nil, err + } + req, err := fs.client.NewRequest("GET", url, nil) + if err != nil { + return nil, nil, err + } + + filters := new(FiltersList) + resp, err := fs.client.Do(req, filters) + if err != nil { + jerr := NewJiraError(resp, err) + return nil, resp, jerr + } + return filters, resp, err } diff --git a/filter_test.go b/filter_test.go index 644af60..dca8747 100644 --- a/filter_test.go +++ b/filter_test.go @@ -100,3 +100,27 @@ func TestFilterService_GetMyFilters(t *testing.T) { t.Errorf("Expected Filters, got nil") } } + +func TestFilterService_Search(t *testing.T) { + setup() + defer teardown() + testAPIEndpoint := "/rest/api/3/filter/search" + raw, err := ioutil.ReadFile("./mocks/search_filters.json") + if err != nil { + t.Error(err.Error()) + } + testMux.HandleFunc(testAPIEndpoint, func(writer http.ResponseWriter, request *http.Request) { + testMethod(t, request, "GET") + testRequestURL(t, request, testAPIEndpoint) + fmt.Fprint(writer, string(raw)) + }) + + opt := FilterSearchOptions{} + filters, _, err := testClient.Filter.Search(&opt) + if err != nil { + t.Errorf("Error given: %s", err) + } + if filters == nil { + t.Errorf("Expected Filters, got nil") + } +} diff --git a/mocks/search_filters.json b/mocks/search_filters.json new file mode 100644 index 0000000..0514f0e --- /dev/null +++ b/mocks/search_filters.json @@ -0,0 +1,120 @@ +{ + "self": "b", + "maxResults": 100, + "startAt": 0, + "total": 2, + "isLast": true, + "values": [ + { + "self": "http://test.jira.org/rest/api/3/filter/10000", + "id": "10000", + "name": "All Open Bugs", + "description": "Lists all open bugs", + "owner": { + "self": "http://test.jira.org/rest/api/3/user?accountId=99:27935d01-92a7-4687-8272-a9b8d3b2ae2e", + "key": "", + "accountId": "99:27935d01-92a7-4687-8272-a9b8d3b2ae2e", + "name": "", + "avatarUrls": { + "48x48": "http://test.jira.org/secure/useravatar?size=large&ownerId=mia", + "24x24": "http://test.jira.org/secure/useravatar?size=small&ownerId=mia", + "16x16": "http://test.jira.org/secure/useravatar?size=xsmall&ownerId=mia", + "32x32": "http://test.jira.org/secure/useravatar?size=medium&ownerId=mia" + }, + "displayName": "Mia Krystof", + "active": false + }, + "jql": "type = Bug and resolution is empty", + "viewUrl": "http://test.jira.org/issues/?filter=10000", + "searchUrl": "http://test.jira.org/rest/api/3/search?jql=type%20%3D%20Bug%20and%20resolutino%20is%20empty", + "favourite": true, + "favouritedCount": 0, + "sharePermissions": [], + "subscriptions": [] + }, + { + "self": "http://test.jira.org/rest/api/3/filter/10010", + "id": "10010", + "name": "My issues", + "description": "Issues assigned to me", + "owner": { + "self": "http://test.jira.org/rest/api/3/user?accountId=99:27935d01-92a7-4687-8272-a9b8d3b2ae2e", + "key": "", + "accountId": "99:27935d01-92a7-4687-8272-a9b8d3b2ae2e", + "name": "", + "avatarUrls": { + "48x48": "http://test.jira.org/secure/useravatar?size=large&ownerId=mia", + "24x24": "http://test.jira.org/secure/useravatar?size=small&ownerId=mia", + "16x16": "http://test.jira.org/secure/useravatar?size=xsmall&ownerId=mia", + "32x32": "http://test.jira.org/secure/useravatar?size=medium&ownerId=mia" + }, + "displayName": "Mia Krystof", + "active": false + }, + "jql": "assignee = currentUser() and resolution is empty", + "viewUrl": "http://test.jira.org/issues/?filter=10010", + "searchUrl": "http://test.jira.org/rest/api/3/search?jql=assignee+in+%28currentUser%28%29%29+and+resolution+is+empty", + "favourite": true, + "favouritedCount": 0, + "sharePermissions": [ + { + "id": 10000, + "type": "global" + }, + { + "id": 10010, + "type": "project", + "project": { + "self": "http://test.jira.org/rest/api/3/project/EX", + "id": "10000", + "key": "EX", + "name": "Example", + "avatarUrls": { + "48x48": "http://test.jira.org/secure/projectavatar?size=large&pid=10000", + "24x24": "http://test.jira.org/secure/projectavatar?size=small&pid=10000", + "16x16": "http://test.jira.org/secure/projectavatar?size=xsmall&pid=10000", + "32x32": "http://test.jira.org/secure/projectavatar?size=medium&pid=10000" + }, + "projectCategory": { + "self": "http://test.jira.org/rest/api/3/projectCategory/10000", + "id": "10000", + "name": "FIRST", + "description": "First Project Category" + }, + "simplified": false, + "style": "classic" + } + } + ], + "subscriptions": [ + { + "id": 1, + "user": { + "self": "http://test.jira.org/rest/api/3/user?accountId=99:27935d01-92a7-4687-8272-a9b8d3b2ae2e", + "key": "", + "accountId": "99:27935d01-92a7-4687-8272-a9b8d3b2ae2e", + "name": "", + "emailAddress": "mia@example.com", + "avatarUrls": { + "48x48": "http://test.jira.org/secure/useravatar?size=large&ownerId=mia", + "24x24": "http://test.jira.org/secure/useravatar?size=small&ownerId=mia", + "16x16": "http://test.jira.org/secure/useravatar?size=xsmall&ownerId=mia", + "32x32": "http://test.jira.org/secure/useravatar?size=medium&ownerId=mia" + }, + "displayName": "Mia Krystof", + "active": true, + "timeZone": "Australia/Sydney", + "groups": { + "size": 3, + "items": [] + }, + "applicationRoles": { + "size": 1, + "items": [] + } + } + } + ] + } + ] +}