Skip to content

Commit

Permalink
[MI-3466] Fix Jira issue mattermost#855: Save user's last used field …
Browse files Browse the repository at this point in the history
…values (#67)

* [MI-3466] Fix Jira issue mattermost#955: Save user's last used field values

* [MI-3466] Code refactoring

* [MI-3466] Fixed issue: not able to select prject while creating subscriptions

* [MI-3466] Review fixes
  • Loading branch information
raghavaggarwal2308 authored Sep 12, 2023
1 parent 97184a5 commit 6ff9349
Show file tree
Hide file tree
Showing 11 changed files with 81 additions and 39 deletions.
4 changes: 2 additions & 2 deletions server/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -1035,8 +1035,8 @@ func executeMe(p *Plugin, c *plugin.Context, header *model.CommandArgs, args ...

resp += connectionBullet(info.User.ConnectedInstances.Get(instanceID), connection, info.User.DefaultInstanceID == instanceID)
resp += fmt.Sprintf(" * %s\n", connection.Settings)
if connection.DefaultProjectKey != "" {
resp += fmt.Sprintf(" * Default project: `%s`\n", connection.DefaultProjectKey)
if connection.SavedFieldValues != nil && connection.SavedFieldValues.ProjectKey != "" {
resp += fmt.Sprintf(" * Default project: `%s`\n", connection.SavedFieldValues.ProjectKey)
}
}
}
Expand Down
13 changes: 8 additions & 5 deletions server/issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,8 +318,11 @@ func (p *Plugin) CreateIssue(in *InCreateIssue) (*jira.Issue, error) {
if err != nil {
return nil, errors.WithMessage(err, "failed to fetch issue details "+created.Key)
}

p.UpdateUserDefaults(in.mattermostUserID, in.InstanceID, project.Key)
p.UpdateUserDefaults(in.mattermostUserID, in.InstanceID, &SavedFieldValues{
ProjectKey: project.Key,
IssueType: issue.Fields.Type.ID,
Components: issue.Fields.Components,
})

// Create a public post for all the channel members
publicReply := &model.Post{
Expand Down Expand Up @@ -459,7 +462,7 @@ func (p *Plugin) GetSearchIssues(instanceID, mattermostUserID types.ID, q, jqlSt
type OutProjectMetadata struct {
Projects []utils.ReactSelectOption `json:"projects"`
IssuesPerProjects map[string][]utils.ReactSelectOption `json:"issues_per_project"`
DefaultProjectKey string `json:"default_project_key,omitempty"`
SavedFieldValues *SavedFieldValues `json:"saved_field_values,omitempty"`
}

func (p *Plugin) httpGetJiraProjectMetadata(w http.ResponseWriter, r *http.Request) (int, error) {
Expand Down Expand Up @@ -526,7 +529,7 @@ func (p *Plugin) httpGetJiraProjectMetadata(w http.ResponseWriter, r *http.Reque
return respondJSON(w, OutProjectMetadata{
Projects: projects,
IssuesPerProjects: issues,
DefaultProjectKey: connection.DefaultProjectKey,
SavedFieldValues: connection.SavedFieldValues,
})
}

Expand Down Expand Up @@ -655,7 +658,7 @@ func (p *Plugin) AttachCommentToIssue(in *InAttachCommentToIssue) (*jira.Comment
rootID = post.RootId
}

p.UpdateUserDefaults(in.mattermostUserID, in.InstanceID, "")
p.UpdateUserDefaults(in.mattermostUserID, in.InstanceID, nil)

msg := fmt.Sprintf("Message attached to [%s](%s/browse/%s)", in.IssueKey, instance.GetJiraBaseURL(), in.IssueKey)

Expand Down
8 changes: 6 additions & 2 deletions server/subscribe.go
Original file line number Diff line number Diff line change
Expand Up @@ -819,7 +819,9 @@ func (p *Plugin) httpChannelCreateSubscription(w http.ResponseWriter, r *http.Re
if subscription.Filters.Projects.Len() == 1 {
projectKey = subscription.Filters.Projects.Elems()[0]
}
p.UpdateUserDefaults(types.ID(mattermostUserID), subscription.InstanceID, projectKey)
p.UpdateUserDefaults(types.ID(mattermostUserID), subscription.InstanceID, &SavedFieldValues{
ProjectKey: projectKey,
})

code, err := respondJSON(w, &subscription)
if err != nil {
Expand Down Expand Up @@ -879,7 +881,9 @@ func (p *Plugin) httpChannelEditSubscription(w http.ResponseWriter, r *http.Requ
if subscription.Filters.Projects.Len() == 1 {
projectKey = subscription.Filters.Projects.Elems()[0]
}
p.UpdateUserDefaults(types.ID(mattermostUserID), subscription.InstanceID, projectKey)
p.UpdateUserDefaults(types.ID(mattermostUserID), subscription.InstanceID, &SavedFieldValues{
ProjectKey: projectKey,
})

code, err := respondJSON(w, &subscription)
if err != nil {
Expand Down
16 changes: 11 additions & 5 deletions server/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,14 @@ type Connection struct {
Oauth1AccessSecret string `json:",omitempty"`
OAuth2Token *oauth2.Token `json:",omitempty"`
Settings *ConnectionSettings
DefaultProjectKey string `json:"default_project_key,omitempty"`
MattermostUserID types.ID `json:"mattermost_user_id"`
SavedFieldValues *SavedFieldValues `json:"saved_field_values,omitempty"`
MattermostUserID types.ID `json:"mattermost_user_id"`
}

type SavedFieldValues struct {
ProjectKey string `json:"project_key,omitempty"`
IssueType string `json:"issue_type,omitempty"`
Components []*jira.Component `json:"components,omitempty"`
}

func (c *Connection) JiraAccountID() types.ID {
Expand Down Expand Up @@ -153,7 +159,7 @@ func (user *User) AsConfigMap() map[string]interface{} {
}
}

func (p *Plugin) UpdateUserDefaults(mattermostUserID, instanceID types.ID, projectKey string) {
func (p *Plugin) UpdateUserDefaults(mattermostUserID, instanceID types.ID, savedValues *SavedFieldValues) {
user, err := p.userStore.LoadUser(mattermostUserID)
if err != nil {
return
Expand All @@ -174,8 +180,8 @@ func (p *Plugin) UpdateUserDefaults(mattermostUserID, instanceID types.ID, proje
}
}

if projectKey != "" && projectKey != connection.DefaultProjectKey {
connection.DefaultProjectKey = projectKey
if savedValues != nil {
connection.SavedFieldValues = savedValues
err = p.userStore.StoreConnection(instanceID, user.MattermostUserID, connection)
if err != nil {
return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ describe('components/JiraInstanceAndProjectSelector', () => {
connectedInstances: [{instance_id: 'instance1', type: InstanceType.CLOUD}, {instance_id: 'instance2', type: InstanceType.SERVER}],
defaultUserInstanceID: '',
fetchJiraProjectMetadata: jest.fn().mockResolvedValue({data: {
default_project_key: 'TEST',
saved_field_values: {
project_key: 'TEST',
},
projects: [
{value: 'TEST', label: 'Test Project'},
{value: 'AA', label: 'Apples Arrangement'},
Expand Down Expand Up @@ -120,7 +122,7 @@ describe('components/JiraInstanceAndProjectSelector', () => {
expect(props.onInstanceChange).not.toBeCalled();
});

test('should use default project key after fetch', async () => {
test('should use default field values after fetch', async () => {
const props = {
...baseProps,
defaultUserInstanceID: 'instance2',
Expand All @@ -133,7 +135,9 @@ describe('components/JiraInstanceAndProjectSelector', () => {
expect(wrapper.state().fetchingProjectMetadata).toBe(true);

await props.fetchJiraProjectMetadata('');
expect(props.onProjectChange).toBeCalledWith('TEST');
expect(props.onProjectChange).toBeCalledWith({
project_key: 'TEST',
});
});

test('should pass error on failed fetch', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import React from 'react';

import {Theme} from 'mattermost-redux/types/preferences';

import {Instance, ProjectMetadata, ReactSelectOption, APIResponse, GetConnectedResponse} from 'types/model';
import {Instance, ProjectMetadata, ReactSelectOption, APIResponse, GetConnectedResponse, SavedFieldValues} from 'types/model';
import ReactSelectSetting from 'components/react_select_setting';
import {getProjectValues} from 'utils/jira_issue_metadata';

export type Props = {
selectedInstanceID: string | null;
selectedProjectID: string | null;
onInstanceChange: (instanceID: string) => void;
onProjectChange: (projectID: string) => void;
onProjectChange: (fieldValues: SavedFieldValues) => void;
onError: (err: string) => void;

theme: Theme;
Expand Down Expand Up @@ -95,8 +95,8 @@ export default class JiraInstanceAndProjectSelector extends React.PureComponent<
fetchingProjectMetadata: false,
});

if (projectMetadata.default_project_key && !this.props.selectedProjectID) {
this.props.onProjectChange(projectMetadata.default_project_key);
if (projectMetadata.saved_field_values && projectMetadata.saved_field_values.project_key && !this.props.selectedProjectID) {
this.props.onProjectChange(projectMetadata.saved_field_values);
}
}

Expand All @@ -112,7 +112,9 @@ export default class JiraInstanceAndProjectSelector extends React.PureComponent<
}

handleProjectChange = (_: string, projectID: string) => {
this.props.onProjectChange(projectID);
this.props.onProjectChange({
project_key: projectID,
});
}

render() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {Post} from 'mattermost-redux/types/posts';
import {Team} from 'mattermost-redux/types/teams';
import {Theme} from 'mattermost-redux/types/preferences';

import {APIResponse, AttachCommentRequest} from 'types/model';
import {APIResponse, AttachCommentRequest, SavedFieldValues} from 'types/model';

import {getModalStyles} from 'utils/styles';

Expand Down Expand Up @@ -94,7 +94,7 @@ export default class AttachCommentToIssueForm extends PureComponent<Props, State
selectedProjectID={''}
hideProjectSelector={true}
onInstanceChange={(instanceID: string) => this.setState({instanceID})}
onProjectChange={(projectKey: string) => {}}
onProjectChange={(savedValues: SavedFieldValues) => {}}
theme={this.props.theme}
addValidate={this.validator.addComponent}
removeValidate={this.validator.removeComponent}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,9 @@ describe('components/EditChannelSubscription', () => {
<EditChannelSubscription {...props}/>
);
wrapper.setState(baseState);
wrapper.instance().handleProjectChange('TES');
wrapper.instance().handleProjectChange({
project_key: 'TES',
});
expect(wrapper.state().filters.projects).toEqual(['TES']);
expect(wrapper.state().fetchingIssueMetadata).toBe(true);
expect(fetchJiraIssueMetadataForProjects).toHaveBeenCalled();
Expand All @@ -150,7 +152,9 @@ describe('components/EditChannelSubscription', () => {
fetchJiraIssueMetadataForProjects = jest.fn().mockResolvedValue({error: {message: 'Failure'}});
wrapper.setProps({fetchJiraIssueMetadataForProjects});

wrapper.instance().handleProjectChange('KT');
wrapper.instance().handleProjectChange({
project_key: 'KT',
});
expect(wrapper.state().filters.projects).toEqual(['KT']);
expect(fetchJiraIssueMetadataForProjects).toHaveBeenCalled();
expect(wrapper.state().fetchingIssueMetadata).toBe(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {
filterValueIsSecurityField,
} from 'utils/jira_issue_metadata';

import {ChannelSubscription, ChannelSubscriptionFilters as ChannelSubscriptionFiltersModel, ReactSelectOption, FilterValue, IssueMetadata} from 'types/model';
import {ChannelSubscription, ChannelSubscriptionFilters as ChannelSubscriptionFiltersModel, ReactSelectOption, FilterValue, IssueMetadata, SavedFieldValues} from 'types/model';

import ChannelSubscriptionFilters from './channel_subscription_filters';
import {SharedProps} from './shared_props';
Expand Down Expand Up @@ -244,10 +244,11 @@ export default class EditChannelSubscription extends PureComponent<Props, State>
}

this.setState({instanceID, error: null});
this.handleProjectChange('');
this.handleProjectChange({});
}

handleProjectChange = (projectID: string) => {
handleProjectChange = (fieldValues: SavedFieldValues) => {
const projectID = fieldValues.project_key ? fieldValues.project_key : '';
this.clearConflictingErrorMessage();

let projects: string[];
Expand Down
30 changes: 21 additions & 9 deletions webapp/src/components/modals/create_issue/create_issue_form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {Theme} from 'mattermost-redux/types/preferences';
import {Post} from 'mattermost-redux/types/posts';
import {Team} from 'mattermost-redux/types/teams';

import {APIResponse, IssueMetadata, CreateIssueRequest, JiraFieldTypeEnums, JiraFieldCustomTypeEnums, CreateIssueFields, JiraField} from 'types/model';
import {APIResponse, IssueMetadata, CreateIssueRequest, JiraFieldTypeEnums, JiraFieldCustomTypeEnums, CreateIssueFields, JiraField, SavedFieldValues} from 'types/model';

import {getFields, getIssueTypes} from 'utils/jira_issue_metadata';
import {getModalStyles} from 'utils/styles';
Expand Down Expand Up @@ -115,7 +115,8 @@ export default class CreateIssueForm extends React.PureComponent<Props, State> {
this.setState({instanceID, projectKey: '', error: null});
}

handleProjectChange = (projectKey: string) => {
handleProjectChange = (fieldValues: SavedFieldValues) => {
const projectKey = fieldValues.project_key ? fieldValues.project_key : '';
this.setState({projectKey, fetchingIssueMetadata: true, error: null});

this.props.fetchJiraIssueMetadataForProjects([projectKey], this.state.instanceID as string).then(({data, error}) => {
Expand All @@ -132,21 +133,32 @@ export default class CreateIssueForm extends React.PureComponent<Props, State> {
this.setState(state);
});

const fields = {
let fields = {
summary: this.state.fields.summary,
description: this.state.fields.description,
project: {key: projectKey},
} as CreateIssueFields;

const issueTypes = getIssueTypes(this.state.jiraIssueMetadata, projectKey);
const issueType = issueTypes.length ? issueTypes[0].id : '';
fields.issuetype = {
id: issueType,
};
if (fieldValues.issue_type) {
fields.issuetype = {
id: fieldValues.issue_type,
};
} else {
const issueTypes = getIssueTypes(this.state.jiraIssueMetadata, projectKey);
const issueType = issueTypes.length ? issueTypes[0].id : '';
fields.issuetype = {
id: issueType,
};
}

fields = {...fields};
if (fieldValues) {
fields.components = fieldValues.components as JiraField;
}

this.setState({
projectKey,
issueType,
issueType: fieldValues.issue_type ? fieldValues.issue_type : '',
fields,
});
}
Expand Down
8 changes: 7 additions & 1 deletion webapp/src/types/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,13 @@ export type IssueMetadata = {
export type ProjectMetadata = {
projects: ReactSelectOption[];
issues_per_project: {[key: string]: ReactSelectOption[]};
default_project_key?: string;
saved_field_values?: SavedFieldValues;
}

export type SavedFieldValues = {
project_key?: string;
issue_type?: string;
components?: JiraField;
}

export enum JiraFieldTypeEnums {
Expand Down

0 comments on commit 6ff9349

Please sign in to comment.