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 6 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
1 change: 1 addition & 0 deletions server/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +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) ([]*IssueTypeWithStatuses, error)
}

// SearchService is the interface for search-related APIs.
Expand Down
15 changes: 15 additions & 0 deletions server/client_cloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package main

import (
"encoding/json"
"fmt"
"strconv"

jira "github.com/andygrunwald/go-jira"
Expand All @@ -16,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 @@ -132,3 +138,12 @@ func (client jiraCloudClient) GetIssueTypes(projectID string) ([]jira.IssueType,

return result, nil
}

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
}

return result, nil
}
9 changes: 9 additions & 0 deletions server/client_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,3 +208,12 @@ func (client jiraServerClient) GetIssueTypes(projectID string) ([]jira.IssueType

return result.IssueTypes, nil
}

func (client jiraServerClient) ListProjectStatuses(projectID string) ([]*IssueTypeWithStatuses, error) {
var result []*IssueTypeWithStatuses
if err := client.RESTGet(fmt.Sprintf("2/project/%s/statuses", projectID), nil, &result); err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

A little concerning to me, what causes this to change from 3 to 2?

Copy link
Contributor Author

@raghavaggarwal2308 raghavaggarwal2308 Dec 7, 2023

Choose a reason for hiding this comment

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

@mickmister It was a mistake on my side, the latest API version for the Jira server is 2.

Copy link
Contributor

Choose a reason for hiding this comment

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

Okay we definitely want to test any changes made to Jira Server code, with Jira Server. And mention in the test steps of the PR to test both Jira Cloud/Server depending on the changes

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 Yes, we always test the changes on both instances and it's also mentioned in the description. We missed it initially in this PR, apologies for that.

return nil, err
}

return result, nil
}
25 changes: 23 additions & 2 deletions server/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,16 @@ const (
descriptionField = "description"
resolutionField = "resolution"
securityLevelField = "security"

QueryParamInstanceID = "instance_id"
QueryParamProjectID = "project_id"
)

type CreateMetaInfo struct {
*jira.CreateMetaInfo
IssueTypesWithStatuses []*IssueTypeWithStatuses `json:"issue_types_with_statuses"`
}

func makePost(userID, channelID, message string) *model.Post {
return &model.Post{
UserId: userID,
Expand Down Expand Up @@ -372,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
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_with_statuses": 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_with_statuses": 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_with_statuses": Array [],
"projects": Array [
Object {
"issuetypes": Array [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -843,6 +843,11 @@ exports[`components/EditChannelSubscription should match snapshot after fetching
issueMetadata={
Object {
"expand": "projects",
"issue_types_with_statuses": 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 @@ -187,7 +187,7 @@ export default class EditChannelSubscription extends PureComponent<Props, State>

let conflictingFields = null;
if (finalValue.length > this.state.filters.issue_types.length) {
const filterFields = getCustomFieldFiltersForProjects(this.state.jiraIssueMetadata, this.state.filters.projects);
const filterFields = getCustomFieldFiltersForProjects(this.state.jiraIssueMetadata, this.state.filters.projects, this.state.filters.issue_types);
conflictingFields = getConflictingFields(
filterFields,
finalValue,
Expand Down Expand Up @@ -227,7 +227,7 @@ export default class EditChannelSubscription extends PureComponent<Props, State>
state.getMetaDataErr = `The project ${projectKeys[0]} is unavailable. Please contact your system administrator.`;
}

const filterFields = getCustomFieldFiltersForProjects(jiraIssueMetadata, this.state.filters.projects);
const filterFields = getCustomFieldFiltersForProjects(jiraIssueMetadata, this.state.filters.projects, this.state.filters.issue_types);
for (const v of this.state.filters.fields) {
if (!filterFields.find((f) => f.key === v.key)) {
state.error = 'A field in this subscription has been removed from Jira, so the subscription is invalid. When this form is submitted, the configured field will be removed from the subscription to make the subscription valid again.';
Expand Down Expand Up @@ -296,7 +296,7 @@ export default class EditChannelSubscription extends PureComponent<Props, State>
return;
}

const filterFields = getCustomFieldFiltersForProjects(this.state.jiraIssueMetadata, this.state.filters.projects);
const filterFields = getCustomFieldFiltersForProjects(this.state.jiraIssueMetadata, this.state.filters.projects, this.state.filters.issue_types);
const configuredFields = this.state.filters.fields.concat([]);
for (const v of this.state.filters.fields) {
if (!filterFields.find((f) => f.key === v.key)) {
Expand Down Expand Up @@ -345,7 +345,7 @@ export default class EditChannelSubscription extends PureComponent<Props, State>
const issueOptions = issueTypes.map((it) => ({label: it.name, value: it.id}));

const customFields = getCustomFieldValuesForEvents(this.state.jiraIssueMetadata, this.state.filters.projects);
const filterFields = getCustomFieldFiltersForProjects(this.state.jiraIssueMetadata, this.state.filters.projects);
const filterFields = getCustomFieldFiltersForProjects(this.state.jiraIssueMetadata, this.state.filters.projects, this.state.filters.issue_types);

const eventOptions = JiraEventOptions.concat(customFields);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -781,6 +781,11 @@ exports[`components/CreateIssue should match snapshot with error 1`] = `
issueMetadata={
Object {
"expand": "projects",
"issue_types_with_statuses": Array [
Object {
"statuses": Array [],
},
],
"projects": Array [
Object {
"expand": "issuetypes",
Expand Down Expand Up @@ -4239,6 +4244,11 @@ exports[`components/CreateIssue should match snapshot with form filled 1`] = `
issueMetadata={
Object {
"expand": "projects",
"issue_types_with_statuses": Array [
Object {
"statuses": Array [],
},
],
"projects": Array [
Object {
"expand": "issuetypes",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
{
"expand": "projects",
"issue_types_with_statuses": [
{
"statuses": []
}
],
"projects": [{
"expand": "issuetypes",
"self": "https://mmtest.atlassian.net/rest/api/2/project/10008",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
{
"expand": "projects",
"issue_types_with_statuses": [
{
"statuses": []
}
],
"projects": [
{
"expand": "issuetypes",
Expand Down
1 change: 1 addition & 0 deletions webapp/src/testdata/jira-issue-metadata-helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ export const useFieldForIssueMetadata = (field: JiraField, key: string): IssueMe
}],
},
],
issue_types_with_statuses: [],
};
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
{
"expand": "projects",
"issue_types_with_statuses": [
{
"statuses": []
}
],
"projects": [{
"expand": "issuetypes",
"self": "http://localhost:8080/rest/api/2/project/10401",
Expand Down
12 changes: 12 additions & 0 deletions webapp/src/types/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,20 @@ export type Project = {
issuetypes: IssueType[];
}

export type IssueTypeWithStatuses = {
id: string;
name: string;
statuses: Status[];
}

export type IssueMetadata = {
projects: Project[];
issue_types_with_statuses: IssueTypeWithStatuses[];
}

export type Status = {
id: string;
name: string;
}

export type ProjectMetadata = {
Expand Down
Loading
Loading