Repository: getsentry/sentry

Issue:
Missing 'org_results' data in production but has some in staging
In file 'web/frontend/help_platform_base.py', method 'group_project_list' the org_results appears to be blank in our production environment.

This manifests when we try to load the 'setup & installation' page
<img width="734" alt="prod" src="https://cloud.githubusercontent.com/assets/4432296/9042832/2c773b94-39c6-11e5-9cc8-fa49f0a6f3ef.png">

However, the same setup is in staging and appears to work, for certain random projects:
<img width="737" alt="staging" src="https://cloud.githubusercontent.com/assets/4432296/9042839/37822d32-39c6-11e5-8c65-ddf6d700de75.png">

In staging I ran 'update sentry_organizationmember_teams set is_active = 1;' and the projects all showed up in the dropdown. 

We _did_ add a custom SSO implementation, so perhaps we missed a step on account creation?

Since there is a "client keys" page, this isn't a show stopper but I'm worried that whatever is wrong could manifest in other ways that we haven't found yet.



Discussion:
@kuzmik by custom SSO do you mean an actual SSO Provider (an implementation of sentry.auth.Provider) or simply a middleware/something similar that creates accounts?

If the latter it's possible you need to set some flags or create some specific objects.

Will dig into this as it's possible there's an issue on our side, but we may just ignore this and ensure it's addressed in the v8 redesign branch (which we're close to landing).

We wrote this implementation:

```
import urllib, json
from django.conf import settings
from django.contrib.auth.backends import RemoteUserBackend
from sentry.models import (
    Organization, OrganizationMember, OrganizationMemberType,
    UserOption
)

class DoormanAuth(RemoteUserBackend):
    def configure_user(self, user):
        user.email = user.username + "@squareup.com"
        user.is_managed = True
        user.save()

        # Fetch full name
        response = urllib.urlopen("userlookupservice/%s" % user.username, proxies={})
        if response.getcode() is 200:
            data = json.loads(response.read())
            user.first_name = data["name"]
            user.save()

        # Disable notfications by default
        UserOption.objects.set_value(
            user=user,
            project=None,
            key="subscribe_by_default",
            value="0",
        )

        # Disable notes by default
        UserOption.objects.set_value(
            user=user,
            project=None,
            key='subscribe_notes',
            value="0",
        )

        if settings.SENTRY_SINGLE_ORGANIZATION:
            org = Organization.get_default()
            OrganizationMember.objects.create(
                organization=org,
                user=user,
                type=OrganizationMemberType.OWNER,
                has_global_access=True,
            )

        return user
```

Ignoring for now is fine, like I said we can get the client keys from that page. Was just something I wanted to make sure wouldn't manifest elsewhere. 

At a glance the implementation looks correct. Assuming its an internal issue. Will try to repro just to ensure it's not broken in v8.

Found the source of the issue - if any set of members have collectively left all teams (aka at least one person has left each team) this bug presents, otherwise it just shows a different set of data - I've got a patch that I'm still working on to make sure it doesn't make the query perform poorly.


Resolving patch:
diff --git a/src/sentry/web/frontend/help_platform_base.py b/src/sentry/web/frontend/help_platform_base.py
--- a/src/sentry/web/frontend/help_platform_base.py
+++ b/src/sentry/web/frontend/help_platform_base.py
@@ -3,7 +3,7 @@
 from django.db.models import Q
 from itertools import groupby
 
-from sentry.models import Project, ProjectKey
+from sentry.models import OrganizationMemberTeam, Project, ProjectKey
 from sentry.web.frontend.base import BaseView
 
 
@@ -15,8 +15,11 @@ def get_project_list(self, user):
             Q(organization__member_set__has_global_access=True, organization__member_set__user=user)
             | Q(team__organizationmember__user=user)
         ).exclude(
-            team__organizationmemberteam__is_active=False,
-        ).select_related('team', 'organization').order_by('organization', 'team').distinct())
+            team__id__in=OrganizationMemberTeam.objects.filter(
+                organizationmember__user=user,
+                is_active=False
+            ).values('team_id')
+        ).select_related('team', 'organization').order_by('organization__name', 'team__name', 'name').distinct())
 
     def group_project_list(self, project_list):
         results = []


Test patch:
diff --git a/tests/sentry/web/frontend/test_help_platform_index.py b/tests/sentry/web/frontend/test_help_platform_index.py
--- a/tests/sentry/web/frontend/test_help_platform_index.py
+++ b/tests/sentry/web/frontend/test_help_platform_index.py
@@ -45,3 +45,100 @@ def test_logged_in(self):
         assert len(team_results[1]) == 2
         assert project1 in team_results[1]
         assert project2 in team_results[1]
+
+    # https://github.com/getsentry/sentry/issues/1673
+    def test_correct_project_list_as_global_active(self):
+        org = self.create_organization(owner=self.user)
+        team1 = self.create_team(name='foo', organization=org)
+        project1 = self.create_project(name='foo', team=team1)
+        project2 = self.create_project(name='bar', team=team1)
+        team2 = self.create_team(name='bar', organization=org)
+        project3 = self.create_project(name='baz', team=team2)
+
+        # Our other user
+        other_user = self.create_user('other@localhost', is_superuser=True)
+
+        member = OrganizationMember.objects.get(
+            organization=org,
+            user=self.user,
+        )
+        OrganizationMemberTeam.objects.create(
+            organizationmember=member,
+            team=team2,
+            is_active=False,
+        )
+
+        other_member = OrganizationMember.objects.create(
+            organization=org,
+            user=other_user,
+        )
+        OrganizationMemberTeam.objects.create(
+            organizationmember=other_member,
+            team=team1,
+            is_active=False,
+        )
+
+        path = reverse('sentry-help-platform-list')
+
+        self.login_as(self.user)
+        resp = self.client.get(path)
+        assert resp.status_code == 200
+        assert len(resp.context['org_results']) == 1
+        org_results = resp.context['org_results'][0]
+        assert org_results[0] == org
+        assert len(org_results[1]) == 1
+        team_results = org_results[1][0]
+        assert team_results[0] == team1
+        assert len(team_results[1]) == 2
+        assert project1 in team_results[1]
+        assert project2 in team_results[1]
+
+    def test_correct_project_list_not_global_active(self):
+        org = self.create_organization(owner=self.user)
+        team1 = self.create_team(name='foo', organization=org)
+        project1 = self.create_project(name='foo', team=team1)
+        project2 = self.create_project(name='bar', team=team1)
+        team2 = self.create_team(name='bar', organization=org)
+        project3 = self.create_project(name='baz', team=team2)
+
+        # Our other user
+        other_user = self.create_user('other@localhost', is_superuser=False)
+
+        member = OrganizationMember.objects.get(
+            organization=org,
+            user=self.user,
+        )
+        OrganizationMemberTeam.objects.create(
+            organizationmember=member,
+            team=team2,
+            is_active=False,
+        )
+
+        other_member = OrganizationMember.objects.create(
+            organization=org,
+            user=other_user,
+        )
+        OrganizationMemberTeam.objects.create(
+            organizationmember=other_member,
+            team=team1,
+            is_active=False,
+        )
+        OrganizationMemberTeam.objects.create(
+            organizationmember=other_member,
+            team=team2,
+            is_active=True,
+        )
+
+        path = reverse('sentry-help-platform-list')
+
+        self.login_as(other_user)
+        resp = self.client.get(path)
+        assert resp.status_code == 200
+        assert len(resp.context['org_results']) == 1
+        org_results = resp.context['org_results'][0]
+        assert org_results[0] == org
+        assert len(org_results[1]) == 1
+        team_results = org_results[1][0]
+        assert team_results[0] == team2
+        assert len(team_results[1]) == 1
+        assert project3 in team_results[1]