Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[GH-862] Added issue status field in subscription modal #976

Merged
merged 10 commits into from
Feb 9, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion server/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ type ProjectService interface {
GetProject(key string) (*jira.Project, error)
ListProjects(query string, limit int, expandIssueTypes bool) (jira.ProjectList, error)
GetIssueTypes(projectID string) ([]jira.IssueType, error)
ListProjectStatuses(projectID string) ([]*IssueType, error)
ListProjectStatuses(projectID string) ([]*IssueTypeWithStatuses, error)
}

// SearchService is the interface for search-related APIs.
Expand Down
14 changes: 7 additions & 7 deletions server/client_cloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ type jiraCloudClient struct {
JiraClient
}

type IssueTypeWithStatuses struct {
*jira.IssueType
Statuses []*jira.Status `json:"statuses"`
}

func newCloudClient(jiraClient *jira.Client) Client {
return &jiraCloudClient{
JiraClient: JiraClient{
Expand Down Expand Up @@ -134,13 +139,8 @@ func (client jiraCloudClient) GetIssueTypes(projectID string) ([]jira.IssueType,
return result, nil
}

type IssueType struct {
*jira.IssueType
Statuses []*jira.Status `json:"statuses"`
}

func (client jiraCloudClient) ListProjectStatuses(projectID string) ([]*IssueType, error) {
var result []*IssueType
func (client jiraCloudClient) ListProjectStatuses(projectID string) ([]*IssueTypeWithStatuses, error) {
var result []*IssueTypeWithStatuses
if err := client.RESTGet(fmt.Sprintf("3/project/%s/statuses", projectID), nil, &result); err != nil {
return nil, err
}
Expand Down
4 changes: 2 additions & 2 deletions server/client_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,8 +209,8 @@ func (client jiraServerClient) GetIssueTypes(projectID string) ([]jira.IssueType
return result.IssueTypes, nil
}

func (client jiraServerClient) ListProjectStatuses(projectID string) ([]*IssueType, error) {
var result []*IssueType
func (client jiraServerClient) ListProjectStatuses(projectID string) ([]*IssueTypeWithStatuses, error) {
var result []*IssueTypeWithStatuses
if err := client.RESTGet(fmt.Sprintf("3/project/%s/statuses", projectID), nil, &result); err != nil {
return nil, err
}
Expand Down
2 changes: 0 additions & 2 deletions server/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ const (
routeAPICreateIssue = "/create-issue"
routeAPIGetCreateIssueMetadata = "/get-create-issue-metadata-for-project"
routeAPIGetJiraProjectMetadata = "/get-jira-project-metadata"
routeAPIGetJiraProjectStatuses = "/get-project-statuses"
routeAPIGetSearchIssues = "/get-search-issues"
routeAPIGetAutoCompleteFields = "/get-search-autocomplete-fields"
routeAPIGetSearchUsers = "/get-search-users"
Expand Down Expand Up @@ -99,7 +98,6 @@ func (p *Plugin) initializeRouter() {
apiRouter.HandleFunc(routeAPICreateIssue, p.checkAuth(p.handleResponse(p.httpCreateIssue))).Methods(http.MethodPost)
apiRouter.HandleFunc(routeAPIGetCreateIssueMetadata, p.checkAuth(p.handleResponse(p.httpGetCreateIssueMetadataForProjects))).Methods(http.MethodGet)
apiRouter.HandleFunc(routeAPIGetJiraProjectMetadata, p.checkAuth(p.handleResponse(p.httpGetJiraProjectMetadata))).Methods(http.MethodGet)
apiRouter.HandleFunc(routeAPIGetJiraProjectStatuses, p.checkAuth(p.handleResponse(p.httpGetJiraProjectStatuses))).Methods(http.MethodGet)
apiRouter.HandleFunc(routeAPIGetSearchIssues, p.checkAuth(p.handleResponse(p.httpGetSearchIssues))).Methods(http.MethodGet)
apiRouter.HandleFunc(routeAPIGetSearchUsers, p.checkAuth(p.handleResponse(p.httpGetSearchUsers))).Methods(http.MethodGet)
apiRouter.HandleFunc(routeAPIAttachCommentToIssue, p.checkAuth(p.handleResponse(p.httpAttachCommentToIssue))).Methods(http.MethodPost)
Expand Down
39 changes: 20 additions & 19 deletions server/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ const (
QueryParamProjectID = "project_id"
)

type CreateMetaInfo struct {
*jira.CreateMetaInfo
IssueTypes []*IssueTypeWithStatuses `json:"issue_types"`
}

func makePost(userID, channelID, message string) *model.Post {
return &model.Post{
UserId: userID,
Expand Down Expand Up @@ -375,16 +380,29 @@ func (p *Plugin) httpGetCreateIssueMetadataForProjects(w http.ResponseWriter, r
return respondJSON(w, cimd)
}

func (p *Plugin) GetCreateIssueMetadataForProjects(instanceID, mattermostUserID types.ID, projectKeys string) (*jira.CreateMetaInfo, error) {
func (p *Plugin) GetCreateIssueMetadataForProjects(instanceID, mattermostUserID types.ID, projectKeys string) (*CreateMetaInfo, error) {
client, _, _, err := p.getClient(instanceID, mattermostUserID)
if err != nil {
return nil, err
}

return client.GetCreateMetaInfo(p.API, &jira.GetQueryOptions{
projectStatuses, err := client.ListProjectStatuses(projectKeys)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have we tested this feature on Jira Cloud and Jira Server?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mickmister Tested and made the necessary changes

if err != nil {
return nil, err
}

metaInfo, err := client.GetCreateMetaInfo(p.API, &jira.GetQueryOptions{
Expand: "projects.issuetypes.fields",
ProjectKeys: projectKeys,
})
if err != nil {
return nil, err
}

return &CreateMetaInfo{
metaInfo,
projectStatuses,
}, nil
}

func (p *Plugin) httpGetSearchIssues(w http.ResponseWriter, r *http.Request) (int, error) {
Expand Down Expand Up @@ -545,23 +563,6 @@ func (p *Plugin) ListJiraProjects(instanceID, mattermostUserID types.ID, expandI
return plist, connection, nil
}

func (p *Plugin) httpGetJiraProjectStatuses(w http.ResponseWriter, r *http.Request) (int, error) {
mattermostUserID := r.Header.Get("Mattermost-User-Id")
instanceID := r.FormValue(QueryParamInstanceID)
projectID := r.FormValue(QueryParamProjectID)
client, _, _, err := p.getClient(types.ID(instanceID), types.ID(mattermostUserID))
if err != nil {
return respondErr(w, http.StatusInternalServerError, errors.WithMessage(err, "failed to get client"))
}

projectStatuses, err := client.ListProjectStatuses(projectID)
if err != nil {
return respondErr(w, http.StatusInternalServerError, errors.WithMessage(err, "failed to list project statuses"))
}

return respondJSON(w, projectStatuses)
}

func (p *Plugin) GetIssueTypes(instanceID, mattermostUserID types.ID, projectID string) ([]jira.IssueType, error) {
client, _, _, err := p.getClient(instanceID, mattermostUserID)
if err != nil {
Expand Down
13 changes: 4 additions & 9 deletions server/subscribe.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,10 @@ type FieldFilter struct {
}

type SubscriptionFilters struct {
Events StringSet `json:"events"`
Projects StringSet `json:"projects"`
IssueStatuses StringSet `json:"issue_statuses"`
IssueTypes StringSet `json:"issue_types"`
Fields []FieldFilter `json:"fields"`
Events StringSet `json:"events"`
Projects StringSet `json:"projects"`
IssueTypes StringSet `json:"issue_types"`
Fields []FieldFilter `json:"fields"`
}

type ChannelSubscription struct {
Expand Down Expand Up @@ -149,10 +148,6 @@ func (p *Plugin) matchesSubsciptionFilters(wh *webhook, filters SubscriptionFilt
return false
}

if filters.IssueStatuses.Len() != 0 && !filters.IssueStatuses.ContainsAny(issue.Fields.Status.ID) {
return false
}

if filters.Projects.Len() != 0 && !filters.Projects.ContainsAny(issue.Fields.Project.Key) {
return false
}
Expand Down
15 changes: 0 additions & 15 deletions webapp/src/actions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,21 +180,6 @@ export const createIssue = (payload: CreateIssueRequest) => {
};
};

export const getProjectStatuses = (instanceID: string, projectID: string) => {
return async (dispatch, getState) => {
const baseUrl = getPluginServerRoute(getState());
try {
const data = await doFetch(`${baseUrl}/api/v2/get-project-statuses?instance_id=${instanceID}&project_id=${projectID}`, {
method: 'get',
});

return {data};
} catch (error) {
return {error};
}
};
};

export const attachCommentToIssue = (payload) => {
return async (dispatch, getState) => {
const baseUrl = getPluginServerRoute(getState());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ exports[`components/JiraEpicSelector should match snapshot 1`] = `
issueMetadata={
Object {
"expand": "projects",
"issue_types": Array [
Object {
"statuses": Array [],
},
],
"projects": Array [
Object {
"expand": "issuetypes",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ exports[`components/ChannelSubscriptionFilters should match snapshot 1`] = `
instanceID="https://something.atlassian.net"
issueMetadata={
Object {
"issue_types": Array [],
"projects": Array [
Object {
"issuetypes": Array [
Expand Down Expand Up @@ -218,6 +219,7 @@ exports[`components/ChannelSubscriptionFilters should match snapshot 1`] = `
}
issueMetadata={
Object {
"issue_types": Array [],
"projects": Array [
Object {
"issuetypes": Array [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -491,45 +491,6 @@ exports[`components/EditChannelSubscription should match snapshot after fetching
]
}
/>
<ReactSelectSetting
addValidate={[Function]}
isMulti={true}
label="Issue Status"
name="issue_status"
onChange={[Function]}
options={Array []}
removeValidate={[Function]}
theme={
Object {
"awayIndicator": "#ffbc42",
"buttonBg": "#166de0",
"buttonColor": "#ffffff",
"centerChannelBg": "#ffffff",
"centerChannelColor": "#3d3c40",
"codeTheme": "github",
"dndIndicator": "#f74343",
"errorTextColor": "#fd5960",
"linkColor": "#2389d7",
"mentionBg": "#ffffff",
"mentionBj": "#ffffff",
"mentionColor": "#145dbf",
"mentionHighlightBg": "#ffe577",
"mentionHighlightLink": "#166de0",
"newMessageSeparator": "#ff8800",
"onlineIndicator": "#06d6a0",
"sidebarBg": "#145dbf",
"sidebarHeaderBg": "#1153ab",
"sidebarHeaderTextColor": "#ffffff",
"sidebarText": "#ffffff",
"sidebarTextActiveBorder": "#579eff",
"sidebarTextActiveColor": "#ffffff",
"sidebarTextHoverBg": "#4578bf",
"sidebarUnreadText": "#ffffff",
"type": "Mattermost",
}
}
value={Array []}
/>
<ChannelSubscriptionFilters
addValidate={[Function]}
chosenIssueTypes={
Expand Down Expand Up @@ -882,6 +843,11 @@ exports[`components/EditChannelSubscription should match snapshot after fetching
issueMetadata={
Object {
"expand": "projects",
"issue_types": Array [
Object {
"statuses": Array [],
},
],
"projects": Array [
Object {
"expand": "issuetypes",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {getCustomFieldFiltersForProjects, isEpicLinkField} from 'utils/jira_issu
import ChannelSubscriptionFilter, {Props} from './channel_subscription_filter';

describe('components/ChannelSubscriptionFilter', () => {
const fields = getCustomFieldFiltersForProjects(issueMetadata, [issueMetadata.projects[0].key]);
const fields = getCustomFieldFiltersForProjects(issueMetadata, [issueMetadata.projects[0].key], []);
const baseProps: Props = {
theme: {},
fields,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {Theme} from 'mattermost-redux/types/preferences';
import ReactSelectSetting from 'components/react_select_setting';
import JiraEpicSelector from 'components/data_selectors/jira_epic_selector';

import {isEpicLinkField, isMultiSelectField, isLabelField, isSecurityLevelField} from 'utils/jira_issue_metadata';
import {isEpicLinkField, isMultiSelectField, isLabelField, isSecurityLevelField, FIELD_KEY_STATUS} from 'utils/jira_issue_metadata';
import {FilterField, FilterValue, ReactSelectOption, IssueMetadata, IssueType, FilterFieldInclusion} from 'types/model';
import ConfirmModal from 'components/confirm_modal';
import JiraAutoCompleteSelector from 'components/data_selectors/jira_autocomplete_selector';
Expand Down Expand Up @@ -199,6 +199,13 @@ export default class ChannelSubscriptionFilter extends React.PureComponent<Props
];
}

if (field.key === FIELD_KEY_STATUS) {
inclusionSelectOptions = [
{label: 'Include', value: FilterFieldInclusion.INCLUDE_ANY},
{label: 'Exclude', value: FilterFieldInclusion.EXCLUDE_ANY},
];
}

if (!isMultiSelectField(field)) {
const includeAllIndex = inclusionSelectOptions.findIndex((opt) => opt.value === FilterFieldInclusion.INCLUDE_ALL);
inclusionSelectOptions.splice(includeAllIndex, 1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ describe('components/ChannelSubscriptionFilters', () => {

const baseProps: Props = {
theme: {},
fields: getCustomFieldFiltersForProjects(issueMetadata, [issueMetadata.projects[0].key]),
fields: getCustomFieldFiltersForProjects(issueMetadata, [issueMetadata.projects[0].key], []),
values: [{
key: 'priority',
inclusion: FilterFieldInclusion.INCLUDE_ANY,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ describe('components/EditChannelSubscription', () => {
selectedSubscription: channelSubscriptionForCloud,
creatingSubscription: false,
securityLevelEmptyForJiraSubscriptions: true,
getProjectStatuses: jest.fn().mockResolvedValue({}),
};

const baseState = {
Expand Down Expand Up @@ -312,7 +311,6 @@ describe('components/EditChannelSubscription', () => {
events: ['event_updated_reopened'],
projects: ['KT'],
issue_types: ['10004'],
issue_statuses: [],
fields: [{
key: 'customfield_10099',
inclusion: 'include_any' as FilterFieldInclusion,
Expand Down
Loading
Loading