Repository: getsentry/sentry

Issue:
[Jira Server] Configurable Default Jira Project per Sentry Project
Hi,

currently, when I create a new Jira ticket via Sentry, I have to choose the Jira project. It would save some time and prevent human errors, if this field could have a default, configurable per Sentry project. This would make sure that the Jira tickets are always in the correct Jira project.


Discussion:
The new Jira integration should remember the last used jira project, is that not happening for you?
Thank you for the quick answer.
It is but this is a bit unconvinient for us:
As an agency, we have many projects and when I wade through Sentry, the last used Jira project is outdated. And when I revisit Sentry the next day, the last Jira project isn't remembered anymore.
And this doesn't prevent human errors as good as a setting it per Sentry project.
> The new Jira integration should remember the last used jira project, is that not happening for you?

@markstory : I think if you use the "Link" tab, it resets all of the saved fields the next time you use the "Create" tab
![image](https://user-images.githubusercontent.com/549473/68822827-cd4bdd80-0646-11ea-93b8-fe0a379f1b8f.png)

I think the issue is that ideally `installation.store_issue_last_defaults(...)` should only get called if `created == True` in:

https://github.com/getsentry/sentry/blob/6589c16bdb8a2362c2e7eed52adec70acd500d3a/src/sentry/api/endpoints/group_integration_details.py#L138

If this seems correct. I'd be happy to submit a PR... but would take me to time to actually work through it since I've never contributed to Sentry before.
@saifelse sounds correct to me. Are you able to test it locally with your patch? Once you submit a PR we can get you a built Docker image if that'd make your life easy.
@saifelse still interested?
@BYK : Whoops, sorry I missed your message in February. I was able to get a dev environment setup, so will have a PR coming your way shortly!

Resolving patch:
diff --git a/src/sentry/integrations/example/integration.py b/src/sentry/integrations/example/integration.py
--- a/src/sentry/integrations/example/integration.py
+++ b/src/sentry/integrations/example/integration.py
@@ -74,7 +74,7 @@ def create_comment(self, issue_id, user_id, group_note):
         return comment
 
     def get_persisted_default_config_fields(self):
-        return ["project"]
+        return ["project", "issueType"]
 
     def get_create_issue_config(self, group, **kwargs):
         kwargs["link_referrer"] = "example_integration"
diff --git a/src/sentry/integrations/issues.py b/src/sentry/integrations/issues.py
--- a/src/sentry/integrations/issues.py
+++ b/src/sentry/integrations/issues.py
@@ -115,7 +115,9 @@ def store_issue_last_defaults(self, project_id, data):
 
         defaults = {k: v for k, v in six.iteritems(data) if k in persisted_fields}
 
-        self.org_integration.config.update({"project_issue_defaults": {project_id: defaults}})
+        self.org_integration.config.setdefault("project_issue_defaults", {}).setdefault(
+            six.text_type(project_id), {}
+        ).update(defaults)
         self.org_integration.save()
 
     def get_project_defaults(self, project_id):


Test patch:
diff --git a/tests/sentry/integrations/test_issues.py b/tests/sentry/integrations/test_issues.py
--- a/tests/sentry/integrations/test_issues.py
+++ b/tests/sentry/integrations/test_issues.py
@@ -145,6 +145,25 @@ def test_get_repository_choices_default_repo(self):
         assert default_repo == "user/repo2"
         assert repo_choice == [("user/repo1", "repo1"), ("user/repo2", "repo2")]
 
+    def test_store_issue_last_defaults_partial_update(self):
+        assert "project" in self.installation.get_persisted_default_config_fields()
+        assert "issueType" in self.installation.get_persisted_default_config_fields()
+        self.installation.store_issue_last_defaults(1, {"project": "xyz", "issueType": "BUG"})
+        self.installation.store_issue_last_defaults(1, {"issueType": "FEATURE"})
+        # {} is commonly triggered by "link issue" flow
+        self.installation.store_issue_last_defaults(1, {})
+        assert self.installation.get_project_defaults(1) == {
+            "project": "xyz",
+            "issueType": "FEATURE",
+        }
+
+    def test_store_issue_last_defaults_multiple_projects(self):
+        assert "project" in self.installation.get_persisted_default_config_fields()
+        self.installation.store_issue_last_defaults(1, {"project": "xyz"})
+        self.installation.store_issue_last_defaults(2, {"project": "abc"})
+        assert self.installation.get_project_defaults(1) == {"project": "xyz"}
+        assert self.installation.get_project_defaults(2) == {"project": "abc"}
+
     def test_annotations(self):
         label = self.installation.get_issue_display_name(self.external_issue)
         link = self.installation.get_issue_url(self.external_issue.key)