Browse Source

feat(codeowners): Auto-sync setting UI & API field update (#31696)

NisanthanNanthakumar 3 years ago
parent
commit
67fd4b4a73

+ 7 - 0
src/sentry/api/endpoints/project_ownership.py

@@ -18,6 +18,7 @@ class ProjectOwnershipSerializer(serializers.Serializer):
     raw = serializers.CharField(allow_blank=True)
     raw = serializers.CharField(allow_blank=True)
     fallthrough = serializers.BooleanField()
     fallthrough = serializers.BooleanField()
     autoAssignment = serializers.BooleanField()
     autoAssignment = serializers.BooleanField()
+    codeownersAutoSync = serializers.BooleanField(default=True)
 
 
     @staticmethod
     @staticmethod
     def _validate_no_codeowners(rules):
     def _validate_no_codeowners(rules):
@@ -79,6 +80,12 @@ class ProjectOwnershipSerializer(serializers.Serializer):
                 ownership.fallthrough = fallthrough
                 ownership.fallthrough = fallthrough
                 changed = True
                 changed = True
 
 
+        if "codeownersAutoSync" in self.validated_data:
+            codeowners_auto_sync = self.validated_data["codeownersAutoSync"]
+            if ownership.codeowners_auto_sync != codeowners_auto_sync:
+                ownership.codeowners_auto_sync = codeowners_auto_sync
+                changed = True
+
         changed = self.__modify_auto_assignment(ownership) or changed
         changed = self.__modify_auto_assignment(ownership) or changed
 
 
         if changed:
         if changed:

+ 1 - 0
src/sentry/api/serializers/models/projectownership.py

@@ -14,4 +14,5 @@ class ProjectOwnershipSerializer(Serializer):
             "lastUpdated": obj.last_updated,
             "lastUpdated": obj.last_updated,
             "isActive": obj.is_active,
             "isActive": obj.is_active,
             "autoAssignment": obj.auto_assignment,
             "autoAssignment": obj.auto_assignment,
+            "codeownersAutoSync": obj.codeowners_auto_sync,
         }
         }

+ 25 - 0
static/app/views/settings/project/projectOwnership/index.tsx

@@ -13,6 +13,7 @@ import Access from 'sentry/components/acl/access';
 import Feature from 'sentry/components/acl/feature';
 import Feature from 'sentry/components/acl/feature';
 import Alert from 'sentry/components/alert';
 import Alert from 'sentry/components/alert';
 import Button from 'sentry/components/button';
 import Button from 'sentry/components/button';
+import FeatureBadge from 'sentry/components/featureBadge';
 import Form from 'sentry/components/forms/form';
 import Form from 'sentry/components/forms/form';
 import JsonForm from 'sentry/components/forms/jsonForm';
 import JsonForm from 'sentry/components/forms/jsonForm';
 import HookOrDefault from 'sentry/components/hookOrDefault';
 import HookOrDefault from 'sentry/components/hookOrDefault';
@@ -380,6 +381,7 @@ tags.sku_class:enterprise #enterprise`;
           initialData={{
           initialData={{
             fallthrough: ownership.fallthrough,
             fallthrough: ownership.fallthrough,
             autoAssignment: ownership.autoAssignment,
             autoAssignment: ownership.autoAssignment,
+            codeownersAutoSync: ownership.codeownersAutoSync,
           }}
           }}
           hideFooter
           hideFooter
         >
         >
@@ -406,6 +408,29 @@ tags.sku_class:enterprise #enterprise`;
                     ),
                     ),
                     disabled,
                     disabled,
                   },
                   },
+                  {
+                    name: 'codeownersAutoSync',
+                    type: 'boolean',
+                    label: tct(
+                      `Automatically sync changes from CODEOWNERS file to Code Owners [badge]`,
+                      {
+                        badge: (
+                          <FeatureBadge
+                            type="new"
+                            title={
+                              !(this.state.codeowners || []).length
+                                ? 'Setup Code Owners to use this feature.'
+                                : undefined
+                            }
+                          />
+                        ),
+                      }
+                    ),
+                    help: t(
+                      'Sentry will watch for CODEOWNERS file changes during a Release and then update Code Owners.'
+                    ),
+                    disabled: disabled || !(this.state.codeowners || []).length,
+                  },
                 ],
                 ],
               },
               },
             ]}
             ]}

+ 14 - 0
tests/sentry/api/endpoints/test_project_ownership.py

@@ -37,6 +37,7 @@ class ProjectOwnershipEndpointTestCase(APITestCase):
             "isActive": True,
             "isActive": True,
             "dateCreated": None,
             "dateCreated": None,
             "lastUpdated": None,
             "lastUpdated": None,
+            "codeownersAutoSync": True,
         }
         }
 
 
     def test_update(self):
     def test_update(self):
@@ -47,6 +48,7 @@ class ProjectOwnershipEndpointTestCase(APITestCase):
         assert resp.data["raw"] == "*.js admin@localhost #tiger-team"
         assert resp.data["raw"] == "*.js admin@localhost #tiger-team"
         assert resp.data["dateCreated"] is not None
         assert resp.data["dateCreated"] is not None
         assert resp.data["lastUpdated"] is not None
         assert resp.data["lastUpdated"] is not None
+        assert resp.data["codeownersAutoSync"] is True
 
 
         resp = self.client.put(self.path, {"fallthrough": False})
         resp = self.client.put(self.path, {"fallthrough": False})
         assert resp.status_code == 200
         assert resp.status_code == 200
@@ -55,6 +57,7 @@ class ProjectOwnershipEndpointTestCase(APITestCase):
         assert resp.data["raw"] == "*.js admin@localhost #tiger-team"
         assert resp.data["raw"] == "*.js admin@localhost #tiger-team"
         assert resp.data["dateCreated"] is not None
         assert resp.data["dateCreated"] is not None
         assert resp.data["lastUpdated"] is not None
         assert resp.data["lastUpdated"] is not None
+        assert resp.data["codeownersAutoSync"] is True
 
 
         resp = self.client.get(self.path)
         resp = self.client.get(self.path)
         assert resp.status_code == 200
         assert resp.status_code == 200
@@ -63,6 +66,7 @@ class ProjectOwnershipEndpointTestCase(APITestCase):
         assert resp.data["raw"] == "*.js admin@localhost #tiger-team"
         assert resp.data["raw"] == "*.js admin@localhost #tiger-team"
         assert resp.data["dateCreated"] is not None
         assert resp.data["dateCreated"] is not None
         assert resp.data["lastUpdated"] is not None
         assert resp.data["lastUpdated"] is not None
+        assert resp.data["codeownersAutoSync"] is True
 
 
         resp = self.client.put(self.path, {"raw": "..."})
         resp = self.client.put(self.path, {"raw": "..."})
         assert resp.status_code == 400
         assert resp.status_code == 400
@@ -74,6 +78,16 @@ class ProjectOwnershipEndpointTestCase(APITestCase):
         assert resp.data["raw"] == "*.js admin@localhost #tiger-team"
         assert resp.data["raw"] == "*.js admin@localhost #tiger-team"
         assert resp.data["dateCreated"] is not None
         assert resp.data["dateCreated"] is not None
         assert resp.data["lastUpdated"] is not None
         assert resp.data["lastUpdated"] is not None
+        assert resp.data["codeownersAutoSync"] is True
+
+        resp = self.client.put(self.path, {"codeownersAutoSync": False})
+        assert resp.status_code == 200
+        assert resp.data["fallthrough"] is False
+        assert resp.data["autoAssignment"] is True
+        assert resp.data["raw"] == "*.js admin@localhost #tiger-team"
+        assert resp.data["dateCreated"] is not None
+        assert resp.data["lastUpdated"] is not None
+        assert resp.data["codeownersAutoSync"] is False
 
 
         resp = self.client.get(self.path)
         resp = self.client.get(self.path)
         assert resp.status_code == 200
         assert resp.status_code == 200