Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: ENT-7553 Added Academies api for MFEs #714

Merged
merged 1 commit into from
Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions enterprise_catalog/apps/api/v1/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from django.db import IntegrityError, models
from rest_framework import serializers, status

from enterprise_catalog.apps.academy.models import Academy, Tag
from enterprise_catalog.apps.api.v1.utils import (
get_enterprise_utm_context,
get_most_recent_modified_time,
Expand Down Expand Up @@ -392,3 +393,25 @@ def get_highlight_sets(self, obj):
}
for highlight_set in catalog_highlight_sets
]


class TagsSerializer(serializers.ModelSerializer):
"""
Serializer for the `Tag` model.
"""
class Meta:
model = Tag
fields = '__all__'


class AcademySerializer(serializers.ModelSerializer):
"""
Serializer for the `Academy` model.
"""
enterprise_catalogs = EnterpriseCatalogSerializer(many=True)
tags = TagsSerializer(many=True)

class Meta:
model = Academy
fields = '__all__'
lookup_field = 'uuid'
54 changes: 54 additions & 0 deletions enterprise_catalog/apps/api/v1/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@
import pytz
from django.conf import settings
from django.db import IntegrityError
from django.utils.http import urlencode
from django.utils.text import slugify
from rest_framework import status
from rest_framework.reverse import reverse
from rest_framework.settings import api_settings
from six.moves.urllib.parse import quote_plus

from enterprise_catalog.apps.academy.tests.factories import AcademyFactory
from enterprise_catalog.apps.api.v1.serializers import ContentMetadataSerializer
from enterprise_catalog.apps.api.v1.tests.mixins import APITestMixin
from enterprise_catalog.apps.api.v1.utils import is_any_course_run_active
Expand Down Expand Up @@ -2312,3 +2314,55 @@ def test_content_metadata_delete_not_implemented(self):
"""
response = self.client.delete(urljoin(self.url, f"{self.content_key_1}/"))
assert response.status_code == 405


@ddt.ddt
class AcademiesViewSetTests(APITestMixin):
"""
Tests for the AcademyViewSet.
"""
def setUp(self):
super().setUp()
self.set_up_catalog_learner()
self.academy1 = AcademyFactory()
self.academy2 = AcademyFactory()
self.enterprise_catalog_query = CatalogQueryFactory(uuid=uuid.uuid4())
self.enterprise_catalog1 = EnterpriseCatalogFactory(catalog_query=self.enterprise_catalog_query)
self.enterprise_catalog1.academies.add(self.academy1)
self.enterprise_catalog2 = EnterpriseCatalogFactory(catalog_query=self.enterprise_catalog_query)
self.enterprise_catalog2.academies.add(self.academy2)

@mock.patch('enterprise_catalog.apps.api_client.enterprise_cache.EnterpriseApiClient')
def test_list_for_academies(self, mock_client): # pylint: disable=unused-argument
"""
Verify the viewset returns enterprise specific academies
"""
params = {
'enterprise_customer': str(self.enterprise_catalog2.enterprise_customer.uuid)
}
url = reverse('api:v1:academies-list') + '?{}'.format(urlencode(params))
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['count'], 1)
results = response.data['results']
self.assertEqual(uuid.UUID(results[0]['uuid']), self.academy2.uuid)

def test_retrieve_for_academies(self):
"""
Verify the viewset retrieves an academy
"""
url = reverse('api:v1:academies-detail', kwargs={
'uuid': self.academy2.uuid,
})
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(uuid.UUID(response.data['uuid']), self.academy2.uuid)

def test_list_with_missing_enterprise_customer(self):
"""
Verify the viewset returns no records when enterprise customer is missing in params
"""
url = reverse('api:v1:academies-list')
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['count'], 0)
10 changes: 10 additions & 0 deletions enterprise_catalog/apps/api/v1/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
from django.urls import path, re_path
from rest_framework.routers import DefaultRouter

from enterprise_catalog.apps.api.v1.views.academies import (
AcademiesReadOnlyViewSet,
)
from enterprise_catalog.apps.api.v1.views.catalog_csv import CatalogCsvView
from enterprise_catalog.apps.api.v1.views.catalog_csv_data import (
CatalogCsvDataView,
Expand Down Expand Up @@ -53,6 +56,7 @@
router.register(r'enterprise-curations-admin', EnterpriseCurationConfigViewSet, basename='enterprise-curations-admin')
router.register(r'highlight-sets', HighlightSetReadOnlyViewSet, basename='highlight-sets')
router.register(r'highlight-sets-admin', HighlightSetViewSet, basename='highlight-sets-admin')
router.register(r'academies', AcademiesReadOnlyViewSet, basename='academies')

urlpatterns = [
path('enterprise-catalogs/catalog_csv_data', CatalogCsvDataView.as_view(),
Expand All @@ -67,6 +71,12 @@
path('enterprise-catalogs/catalog_workbook', CatalogWorkbookView.as_view(),
name='catalog-workbook'
),
path('academies', AcademiesReadOnlyViewSet.as_view({'get': 'list'}),
name='academies-list'
),
path('academies/<uuid:uuid>/', AcademiesReadOnlyViewSet.as_view({'get': 'retrieve'}),
name='academies-detail'
),
re_path(
r'^enterprise-catalogs/(?P<uuid>[\S]+)/get_content_metadata',
EnterpriseCatalogGetContentMetadata.as_view({'get': 'get'}),
Expand Down
46 changes: 46 additions & 0 deletions enterprise_catalog/apps/api/v1/views/academies.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from django.utils.functional import cached_property
from edx_rest_framework_extensions.auth.jwt.authentication import (
JwtAuthentication,
)
from rest_framework import permissions, viewsets
from rest_framework.authentication import SessionAuthentication
from rest_framework.renderers import JSONRenderer
from rest_framework_xml.renderers import XMLRenderer

from enterprise_catalog.apps.academy.models import Academy
from enterprise_catalog.apps.api.v1.serializers import AcademySerializer


class AcademiesReadOnlyViewSet(viewsets.ReadOnlyModelViewSet):
""" Viewset for Read Only operations on Academies """
authentication_classes = [JwtAuthentication, SessionAuthentication]
permission_classes = [permissions.IsAuthenticated]
renderer_classes = [JSONRenderer, XMLRenderer]
serializer_class = AcademySerializer
lookup_field = 'uuid'

@cached_property
def request_action(self):
return getattr(self, 'action', None)

def get_queryset(self):
"""
Returns the queryset corresponding to all academies the requesting user has access to.
"""
enterprise_customer = self.request.GET.get('enterprise_customer', False)
all_academies = Academy.objects.all()
if self.request_action == 'list':
if enterprise_customer:
user_accessible_academy_uuids = []
for academy in all_academies:
academy_associated_catalogs = academy.enterprise_catalogs.all()
enterprise_associated_catalogs = academy_associated_catalogs.filter(
enterprise_uuid=enterprise_customer
)
if enterprise_associated_catalogs:
user_accessible_academy_uuids.append(academy.uuid)
return all_academies.filter(uuid__in=user_accessible_academy_uuids)
else:
return Academy.objects.none()

return Academy.objects.all()
Loading