diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 514075bc29..6ca73cf6a0 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -800,14 +800,14 @@ class OrganizationSerializer(BaseSerializer): def get_summary_fields(self, obj): summary_dict = super(OrganizationSerializer, self).get_summary_fields(obj) - counts_dict = self.context.get('counts', None) + counts_dict = self.context.get('related_field_counts', None) if counts_dict is not None and summary_dict is not None: if obj.id not in counts_dict: - summary_dict['counts'] = { + summary_dict['related_field_counts'] = { 'inventories': 0, 'teams': 0, 'users': 0, 'job_templates': 0, 'admins': 0, 'projects': 0} else: - summary_dict['counts'] = counts_dict[obj.id] + summary_dict['related_field_counts'] = counts_dict[obj.id] return summary_dict diff --git a/awx/api/views.py b/awx/api/views.py index ebdb57098d..b50ba0497c 100644 --- a/awx/api/views.py +++ b/awx/api/views.py @@ -625,47 +625,43 @@ class OrganizationList(ListCreateAPIView): org_id_list = org_qs.values('id') if len(org_id_list) == 0: if self.request.method == 'POST': - full_context['counts'] = {} + full_context['related_field_counts'] = {} return full_context - # Produce counts of Foreign Key relationships inv_qs = self.request.user.get_queryset(Inventory) + project_qs = self.request.user.get_queryset(Project) + user_qs = self.request.user.get_queryset(User) + + # Produce counts of Foreign Key relationships db_results['inventories'] = inv_qs\ .values('organization').annotate(Count('organization')).order_by('organization') db_results['teams'] = self.request.user.get_queryset(Team)\ .values('organization').annotate(Count('organization')).order_by('organization') + # TODO: When RBAC branch merges, change this to project relationship JT_reference = 'inventory__organization' # Extra filter is applied on the inventory, because this catches # the case of deleted (and purged) inventory - db_JT_results = self.request.user.get_queryset(JobTemplate)\ - .filter(inventory_id__in=inv_qs.values_list('pk', flat=True))\ + db_results['job_templates'] = self.request.user.get_queryset(JobTemplate)\ + .filter(inventory__in=inv_qs)\ .values(JT_reference).annotate(Count(JT_reference))\ .order_by(JT_reference) # Produce counts of m2m relationships - project_qs = self.request.user.get_queryset(Project) db_results['projects'] = Organization.projects.through.objects\ - .filter( - project_id__in=project_qs.values_list('pk', flat=True), - organization_id__in=org_qs.values_list('pk', flat=True))\ + .filter(project__in=project_qs, organization__in=org_qs)\ .values('organization')\ .annotate(Count('organization')).order_by('organization') # TODO: When RBAC branch merges, change these to role relation - user_qs = self.request.user.get_queryset(User) db_results['users'] = Organization.users.through.objects\ - .filter( - user_id__in=user_qs.values_list('pk', flat=True), - organization_id__in=org_qs.values_list('pk', flat=True))\ + .filter(user__in=user_qs, organization__in=org_qs)\ .values('organization')\ .annotate(Count('organization')).order_by('organization') db_results['admins'] = Organization.admins.through.objects\ - .filter( - user_id__in=user_qs.values_list('pk', flat=True), - organization_id__in=org_qs.values_list('pk', flat=True))\ + .filter(user__in=user_qs, organization__in=org_qs)\ .values('organization')\ .annotate(Count('organization')).order_by('organization') @@ -677,15 +673,16 @@ class OrganizationList(ListCreateAPIView): 'admins': 0, 'projects': 0} for res in db_results: + if res == 'job_templates': + org_reference = JT_reference + else: + org_reference = 'organization' for entry in db_results[res]: - org_id = entry['organization'] - count_context[org_id][res] = entry['organization__count'] + org_id = entry[org_reference] + if org_id in count_context: + count_context[org_id][res] = entry['%s__count' % org_reference] - for entry in db_JT_results: - org_id = entry[JT_reference] - count_context[org_id]['job_templates'] = entry['%s__count' % JT_reference] - - full_context['counts'] = count_context + full_context['related_field_counts'] = count_context return full_context diff --git a/awx/main/tests/functional/api/test_organization_counts.py b/awx/main/tests/functional/api/test_organization_counts.py index 56fdb8215e..de629dbcf4 100644 --- a/awx/main/tests/functional/api/test_organization_counts.py +++ b/awx/main/tests/functional/api/test_organization_counts.py @@ -3,7 +3,7 @@ import pytest from django.core.urlresolvers import reverse @pytest.fixture -def resourced_organization(organization, project, user): +def resourced_organization(organization, project, team, inventory, user): admin_user = user('test-admin', True) member_user = user('org-member') @@ -11,12 +11,12 @@ def resourced_organization(organization, project, user): organization.users.add(member_user) organization.admins.add(admin_user) organization.projects.add(project) - organization.teams.create(name='org-team') - inventory = organization.inventories.create(name="associated-inv") - inventory.jobtemplates.create(name="test-jt", - description="test-job-template-desc", - project=project, - playbook="test_playbook.yml") + # organization.teams.create(name='org-team') + # inventory = organization.inventories.create(name="associated-inv") + project.jobtemplates.create(name="test-jt", + description="test-job-template-desc", + inventory=inventory, + playbook="test_playbook.yml") return organization @@ -25,8 +25,9 @@ def test_org_counts_admin(resourced_organization, user, get): # Check that all types of resources are counted by a superuser external_admin = user('admin', True) response = get(reverse('api:organization_list', args=[]), external_admin) - counts = response.data['results'][0]['summary_fields']['counts'] + assert response.status_code == 200 + counts = response.data['results'][0]['summary_fields']['related_field_counts'] assert counts == { 'users': 1, 'admins': 1, @@ -42,7 +43,9 @@ def test_org_counts_member(resourced_organization, get): # user count, consistent with the RBAC rules member_user = resourced_organization.users.get(username='org-member') response = get(reverse('api:organization_list', args=[]), member_user) - counts = response.data['results'][0]['summary_fields']['counts'] + assert response.status_code == 200 + + counts = response.data['results'][0]['summary_fields']['related_field_counts'] assert counts == { 'users': 1, # User can see themselves @@ -60,10 +63,10 @@ def test_new_org_zero_counts(user, post): org_list_url = reverse('api:organization_list', args=[]) post_response = post(url=org_list_url, data={'name': 'test organization', 'description': ''}, user=user('admin', True)) - new_org_list = post_response.render().data - counts_dict = new_org_list['summary_fields']['counts'] - assert post_response.status_code == 201 + + new_org_list = post_response.render().data + counts_dict = new_org_list['summary_fields']['related_field_counts'] assert counts_dict == { 'users': 0, 'admins': 0, @@ -79,12 +82,14 @@ def test_two_organizations(resourced_organization, organizations, user, get): external_admin = user('admin', True) organization_zero = organizations(1)[0] response = get(reverse('api:organization_list', args=[]), external_admin) + assert response.status_code == 200 + org_id_full = resourced_organization.id org_id_zero = organization_zero.id counts = {} for i in range(2): org_id = response.data['results'][i]['id'] - counts[org_id] = response.data['results'][i]['summary_fields']['counts'] + counts[org_id] = response.data['results'][i]['summary_fields']['related_field_counts'] assert counts[org_id_full] == { 'users': 1, @@ -102,3 +107,49 @@ def test_two_organizations(resourced_organization, organizations, user, get): 'inventories': 0, 'teams': 0 } + +@pytest.mark.django_db +def test_overlapping_project(resourced_organization, organizations, user, get): + # Check correct results for two organizations are returned + external_admin = user('admin', True) + organization2 = organizations(1)[0] + the_project = resourced_organization.projects.all()[0] + organization2.projects.add(the_project) + organization2.projects.create(name="second-project", + description="test-proj-desc", + scm_type="git", + scm_url="https://github.com/jlaska/ansible-playbooks") + inventory = organization2.inventories.create(name="second-inventory") + organization2.projects.get(name="second-project").jobtemplates.create( + name="second-job-template", + inventory=inventory, + playbook="hello.yml" + ) + + response = get(reverse('api:organization_list', args=[]), external_admin) + assert response.status_code == 200 + + org_id_full = resourced_organization.id + org_id2 = organization2.id + counts = {} + for i in range(2): + org_id = response.data['results'][i]['id'] + counts[org_id] = response.data['results'][i]['summary_fields']['related_field_counts'] + + assert counts[org_id_full] == { + 'users': 1, + 'admins': 1, + 'job_templates': 1, + 'projects': 1, + 'inventories': 1, + 'teams': 1 + } + assert counts[org_id2] == { + 'users': 0, + 'admins': 0, + 'job_templates': 2, + 'projects': 2, + 'inventories': 1, + 'teams': 0 + } + assert False