Skip to content

Commit

Permalink
feat: transaction api ordering
Browse files Browse the repository at this point in the history
  • Loading branch information
johnnagro committed Nov 9, 2023
1 parent 4958596 commit cffd3b8
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 3 deletions.
75 changes: 74 additions & 1 deletion enterprise_subsidy/apps/api/v1/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,11 @@ class APITestBase(APITestMixin):

enterprise_1_uuid = STATIC_ENTERPRISE_UUID
enterprise_2_uuid = str(uuid.uuid4())
enterprise_3_uuid = str(uuid.uuid4())
subsidy_1_uuid = str(uuid.uuid4())
subsidy_2_uuid = str(uuid.uuid4())
subsidy_3_uuid = str(uuid.uuid4())
subsidy_4_uuid = str(uuid.uuid4())
subsidy_1_transaction_1_uuid = str(uuid.uuid4())
subsidy_1_transaction_2_uuid = str(uuid.uuid4())
subsidy_2_transaction_1_uuid = str(uuid.uuid4())
Expand All @@ -50,11 +52,16 @@ class APITestBase(APITestMixin):
subsidy_3_transaction_2_uuid = str(uuid.uuid4())
subsidy_access_policy_1_uuid = str(uuid.uuid4())
subsidy_access_policy_2_uuid = str(uuid.uuid4())
subsidy_access_policy_3_uuid = str(uuid.uuid4())
subsidy_4_transaction_1_uuid = str(uuid.uuid4())
subsidy_4_transaction_2_uuid = str(uuid.uuid4())
content_key_1 = "course-v1:edX+test+course.1"
content_title_1 = "edx: Test Course 1"
content_key_2 = "course-v1:edX+test+course.2"
content_title_2 = "edx: Test Course 2"
lms_user_email = '[email protected]'
transaction_quantity_1 = -1
transaction_quantity_2 = -2

def setUp(self):
super().setUp()
Expand Down Expand Up @@ -141,6 +148,28 @@ def setUp(self):
lms_user_email=self.lms_user_email,
)

self.subsidy_4 = SubsidyFactory(
uuid=self.subsidy_4_uuid,
enterprise_customer_uuid=self.enterprise_3_uuid,
starting_balance=15000
)
self.subsidy_4_transaction_initial = self.subsidy_4.ledger.transactions.first()

self.subsidy_4_transaction_1 = TransactionFactory(
uuid=self.subsidy_4_transaction_1_uuid,
state=TransactionStateChoices.COMMITTED,
quantity=self.transaction_quantity_1,
ledger=self.subsidy_4.ledger,
lms_user_id=STATIC_LMS_USER_ID+1000,
)
self.subsidy_4_transaction_2 = TransactionFactory(
uuid=self.subsidy_4_transaction_2_uuid,
state=TransactionStateChoices.COMMITTED,
quantity=self.transaction_quantity_2,
ledger=self.subsidy_4.ledger,
lms_user_id=STATIC_LMS_USER_ID+1000,
)

self.all_initial_transactions = set([
str(self.subsidy_1_transaction_initial.uuid),
str(self.subsidy_2_transaction_initial.uuid),
Expand Down Expand Up @@ -203,7 +232,7 @@ def test_get_subsidy_list_as_operator(self):
response = self.client.get(self.get_list_url)
print(response.json()['results'][0]['uuid'].find(str(self.subsidy_1.uuid)))
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.json()['count'], 3)
self.assertEqual(response.json()['count'], 4)
self.assertEqual(len(response.json()['results']), response.json()['count'])

def test_get_subsidy_list_with_query_parameter_enterprise_customer_uuid(self):
Expand Down Expand Up @@ -1398,6 +1427,50 @@ def test_list_search(self, request_query_params, expected_response_status, expec
set(expected_response_uuids) - self.all_initial_transactions
)

@ddt.data(
{
"request_subsidy_uuid": APITestBase.subsidy_4_uuid,
"request_ordering_query": "created",
"expected_response_status": 200,
"expected_response_uuid_order": [
APITestBase.subsidy_4_transaction_1_uuid,
APITestBase.subsidy_4_transaction_2_uuid,
],
},
{
"request_subsidy_uuid": APITestBase.subsidy_4_uuid,
"request_ordering_query": "quantity",
"expected_response_status": 200,
"expected_response_uuid_order": [
APITestBase.subsidy_4_transaction_2_uuid,
APITestBase.subsidy_4_transaction_1_uuid,
],
},
)
@ddt.unpack
def test_list_ordering(
self,
request_subsidy_uuid,
request_ordering_query,
expected_response_status,
expected_response_uuid_order,
):
"""
Test list Transactions search.
"""
self.set_up_admin(enterprise_uuids=[self.enterprise_3_uuid])
url = reverse("api:v2:transaction-admin-list-create", args=[request_subsidy_uuid])
query_string = urllib.parse.urlencode({"ordering": request_ordering_query})
if query_string:
query_string = "?" + query_string
response = self.client.get(url + query_string)
assert response.status_code == expected_response_status
if response.status_code < 300:
list_response_data = response.json()["results"]
response_uuids = [tx["uuid"] for tx in list_response_data]
response_uuids.remove(str(self.subsidy_4_transaction_initial.uuid))
self.assertEqual(response_uuids, expected_response_uuid_order)


@ddt.ddt
class ContentMetadataViewSetTests(APITestBase):
Expand Down
8 changes: 7 additions & 1 deletion enterprise_subsidy/apps/api/v1/views/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,17 @@ class TransactionViewSet(
lookup_field = "uuid"
serializer_class = TransactionSerializer
pagination_class = TransactionListPaginator
filter_backends = [filters.SearchFilter]
filter_backends = [filters.SearchFilter, filters.OrderingFilter]

# fields that are queried for search
search_fields = ['lms_user_email', 'content_title']

# Settings that control list ordering, powered by OrderingFilter.
# Fields in `ordering_fields` are what we allow to be passed to the "?ordering=" query param.
ordering_fields = ['created', 'quantity']
# `ordering` defines the default order.
ordering = ['-created']

# Fields that control permissions for 'list' actions, required by PermissionRequiredForListingMixin.
list_lookup_field = "ledger__subsidy__enterprise_customer_uuid"
allowed_roles = [ENTERPRISE_SUBSIDY_ADMIN_ROLE, ENTERPRISE_SUBSIDY_LEARNER_ROLE, ENTERPRISE_SUBSIDY_OPERATOR_ROLE]
Expand Down
73 changes: 73 additions & 0 deletions enterprise_subsidy/apps/api/v2/tests/test_transaction_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,12 @@ class APITestBase(APITestMixin):
lms_user_email = '[email protected]'
enterprise_1_uuid = STATIC_ENTERPRISE_UUID
enterprise_2_uuid = str(uuid.uuid4())
enterprise_3_uuid = str(uuid.uuid4())

subsidy_1_uuid = str(uuid.uuid4())
subsidy_2_uuid = str(uuid.uuid4())
subsidy_3_uuid = str(uuid.uuid4())
subsidy_4_uuid = str(uuid.uuid4())

subsidy_1_transaction_1_uuid = str(uuid.uuid4())
subsidy_1_transaction_2_uuid = str(uuid.uuid4())
Expand All @@ -48,17 +50,22 @@ class APITestBase(APITestMixin):
subsidy_2_transaction_2_uuid = str(uuid.uuid4())
subsidy_3_transaction_1_uuid = str(uuid.uuid4())
subsidy_3_transaction_2_uuid = str(uuid.uuid4())
subsidy_4_transaction_1_uuid = str(uuid.uuid4())
subsidy_4_transaction_2_uuid = str(uuid.uuid4())
# Add an extra UUID for any failed transaction that
# a subclass may need to use
failed_transaction_uuid = str(uuid.uuid4())

subsidy_access_policy_1_uuid = str(uuid.uuid4())
subsidy_access_policy_2_uuid = str(uuid.uuid4())
subsidy_access_policy_3_uuid = str(uuid.uuid4())

content_key_1 = "course-v1:edX+test+course.1"
content_key_2 = "course-v1:edX+test+course.2"
content_title_1 = "edX: Test Course 1"
content_title_2 = "edx: Test Course 2"
transaction_quantity_1 = -1
transaction_quantity_2 = -2
failed_content_title = "Studebaker"

@classmethod
Expand Down Expand Up @@ -95,6 +102,13 @@ def _setup_subsidies(cls):
)
cls.subsidy_3_transaction_initial = cls.subsidy_3.ledger.transactions.first()

cls.subsidy_4 = SubsidyFactory(
uuid=cls.subsidy_4_uuid,
enterprise_customer_uuid=cls.enterprise_3_uuid,
starting_balance=15000
)
cls.subsidy_4_transaction_initial = cls.subsidy_4.ledger.transactions.first()

@classmethod
def _setup_transactions(cls):
cls.subsidy_1_transaction_1 = TransactionFactory(
Expand Down Expand Up @@ -169,6 +183,21 @@ def _setup_transactions(cls):
lms_user_id=STATIC_LMS_USER_ID+1000,
)

cls.subsidy_4_transaction_1 = TransactionFactory(
uuid=cls.subsidy_4_transaction_1_uuid,
state=TransactionStateChoices.COMMITTED,
quantity=cls.transaction_quantity_1,
ledger=cls.subsidy_4.ledger,
lms_user_id=STATIC_LMS_USER_ID+1000,
)
cls.subsidy_4_transaction_2 = TransactionFactory(
uuid=cls.subsidy_4_transaction_2_uuid,
state=TransactionStateChoices.COMMITTED,
quantity=cls.transaction_quantity_2,
ledger=cls.subsidy_4.ledger,
lms_user_id=STATIC_LMS_USER_ID+1000,
)

def _prepend_initial_transaction_uuid(self, subsidy_uuid, user_transaction_uuids):
"""
Helper to put the appropriate initial transaction uuid for a subsidy at the start
Expand Down Expand Up @@ -506,6 +535,50 @@ def test_list_search(
response_uuids = [tx["uuid"] for tx in list_response_data]
self.assertEqual(sorted(response_uuids), sorted(expected_response_uuids))

@ddt.data(
{
"request_subsidy_uuid": APITestBase.subsidy_4_uuid,
"request_ordering_query": "created",
"expected_response_status": 200,
"expected_response_uuid_order": [
APITestBase.subsidy_4_transaction_1_uuid,
APITestBase.subsidy_4_transaction_2_uuid,
],
},
{
"request_subsidy_uuid": APITestBase.subsidy_4_uuid,
"request_ordering_query": "quantity",
"expected_response_status": 200,
"expected_response_uuid_order": [
APITestBase.subsidy_4_transaction_2_uuid,
APITestBase.subsidy_4_transaction_1_uuid,
],
},
)
@ddt.unpack
def test_list_ordering(
self,
request_subsidy_uuid,
request_ordering_query,
expected_response_status,
expected_response_uuid_order,
):
"""
Test list Transactions search.
"""
self.set_up_admin(enterprise_uuids=[self.enterprise_3_uuid])
url = reverse("api:v2:transaction-admin-list-create", args=[request_subsidy_uuid])
query_string = urllib.parse.urlencode({"ordering": request_ordering_query})
if query_string:
query_string = "?" + query_string
response = self.client.get(url + query_string)
assert response.status_code == expected_response_status
if response.status_code < 300:
list_response_data = response.json()["results"]
response_uuids = [tx["uuid"] for tx in list_response_data]
response_uuids.remove(str(self.subsidy_4_transaction_initial.uuid))
self.assertEqual(response_uuids, expected_response_uuid_order)


@ddt.ddt
class TransactionAdminCreateViewTests(APITestBase):
Expand Down
8 changes: 7 additions & 1 deletion enterprise_subsidy/apps/api/v2/views/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,12 +83,18 @@ class TransactionAdminListCreate(TransactionBaseViewMixin, generics.ListCreateAP
of the related subsidy's enterprise customer. It lists all transactions
for the requested subsidy, or a subset thereof, depending on the query parameters.
"""
filter_backends = [drf_filters.DjangoFilterBackend, filters.SearchFilter]
filter_backends = [drf_filters.DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
filterset_class = TransactionAdminFilterSet

# fields that are queried for search
search_fields = ['lms_user_email', 'content_title']

# Settings that control list ordering, powered by OrderingFilter.
# Fields in `ordering_fields` are what we allow to be passed to the "?ordering=" query param.
ordering_fields = ['created', 'quantity']
# `ordering` defines the default order.
ordering = ['-created']

def __init__(self, *args, **kwargs):
self.extra_context = {}
return super().__init__(*args, **kwargs)
Expand Down

0 comments on commit cffd3b8

Please sign in to comment.