diff --git a/server/command.go b/server/command.go index 8fc92fa5e..89d25933f 100644 --- a/server/command.go +++ b/server/command.go @@ -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) } } } diff --git a/server/issue.go b/server/issue.go index bc5902e96..1cc0b1e5b 100644 --- a/server/issue.go +++ b/server/issue.go @@ -318,8 +318,10 @@ 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, + }) // Create a public post for all the channel members publicReply := &model.Post{ @@ -459,7 +461,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) { @@ -526,7 +528,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, }) } @@ -655,7 +657,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) diff --git a/server/subscribe.go b/server/subscribe.go index f19d39af7..4a265cc23 100644 --- a/server/subscribe.go +++ b/server/subscribe.go @@ -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 { @@ -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 { diff --git a/server/user.go b/server/user.go index 890a076ec..3cabb7274 100644 --- a/server/user.go +++ b/server/user.go @@ -33,8 +33,13 @@ 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"` } func (c *Connection) JiraAccountID() types.ID { @@ -153,7 +158,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 @@ -174,8 +179,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 diff --git a/webapp/src/components/jira_instance_and_project_selector/jira_instance_and_project_selector.test.tsx b/webapp/src/components/jira_instance_and_project_selector/jira_instance_and_project_selector.test.tsx index 5e94d5ed1..2f17efa17 100644 --- a/webapp/src/components/jira_instance_and_project_selector/jira_instance_and_project_selector.test.tsx +++ b/webapp/src/components/jira_instance_and_project_selector/jira_instance_and_project_selector.test.tsx @@ -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'}, @@ -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', @@ -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 () => { diff --git a/webapp/src/components/jira_instance_and_project_selector/jira_instance_and_project_selector.tsx b/webapp/src/components/jira_instance_and_project_selector/jira_instance_and_project_selector.tsx index 2af4e634e..07cb08197 100644 --- a/webapp/src/components/jira_instance_and_project_selector/jira_instance_and_project_selector.tsx +++ b/webapp/src/components/jira_instance_and_project_selector/jira_instance_and_project_selector.tsx @@ -2,7 +2,7 @@ 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'; @@ -10,7 +10,7 @@ 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; @@ -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); } } @@ -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() { diff --git a/webapp/src/components/modals/attach_comment_modal/attach_comment_form.tsx b/webapp/src/components/modals/attach_comment_modal/attach_comment_form.tsx index 8e524c115..b9a90dd7b 100644 --- a/webapp/src/components/modals/attach_comment_modal/attach_comment_form.tsx +++ b/webapp/src/components/modals/attach_comment_modal/attach_comment_form.tsx @@ -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'; @@ -94,7 +94,7 @@ export default class AttachCommentToIssueForm extends PureComponent this.setState({instanceID})} - onProjectChange={(projectKey: string) => {}} + onProjectChange={(savedValues: SavedFieldValues) => {}} theme={this.props.theme} addValidate={this.validator.addComponent} removeValidate={this.validator.removeComponent} diff --git a/webapp/src/components/modals/channel_subscriptions/edit_channel_subscription.test.tsx b/webapp/src/components/modals/channel_subscriptions/edit_channel_subscription.test.tsx index f2e867680..618fa3ea7 100644 --- a/webapp/src/components/modals/channel_subscriptions/edit_channel_subscription.test.tsx +++ b/webapp/src/components/modals/channel_subscriptions/edit_channel_subscription.test.tsx @@ -138,7 +138,9 @@ describe('components/EditChannelSubscription', () => { ); 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(); @@ -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); diff --git a/webapp/src/components/modals/channel_subscriptions/edit_channel_subscription.tsx b/webapp/src/components/modals/channel_subscriptions/edit_channel_subscription.tsx index f8163d75f..fab7a5d62 100644 --- a/webapp/src/components/modals/channel_subscriptions/edit_channel_subscription.tsx +++ b/webapp/src/components/modals/channel_subscriptions/edit_channel_subscription.tsx @@ -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'; @@ -244,10 +244,11 @@ export default class EditChannelSubscription extends PureComponent } 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[]; diff --git a/webapp/src/components/modals/create_issue/create_issue_form.tsx b/webapp/src/components/modals/create_issue/create_issue_form.tsx index ac097b86d..3e9a5b191 100644 --- a/webapp/src/components/modals/create_issue/create_issue_form.tsx +++ b/webapp/src/components/modals/create_issue/create_issue_form.tsx @@ -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'; @@ -115,7 +115,8 @@ export default class CreateIssueForm extends React.PureComponent { 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}) => { @@ -138,15 +139,21 @@ export default class CreateIssueForm extends React.PureComponent { 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, + }; + } this.setState({ projectKey, - issueType, + issueType: fieldValues.issue_type ? fieldValues.issue_type : '', fields, }); } diff --git a/webapp/src/types/model.ts b/webapp/src/types/model.ts index 9f8b4bb8d..b78420b75 100644 --- a/webapp/src/types/model.ts +++ b/webapp/src/types/model.ts @@ -72,7 +72,12 @@ 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; } export enum JiraFieldTypeEnums {