edx-enterprise-subsidy-client 0.4.2__tar.gz → 0.4.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (19) hide show
  1. {edx-enterprise-subsidy-client-0.4.2 → edx_enterprise_subsidy_client-0.4.4}/CHANGELOG.rst +8 -0
  2. {edx-enterprise-subsidy-client-0.4.2 → edx_enterprise_subsidy_client-0.4.4}/PKG-INFO +9 -1
  3. {edx-enterprise-subsidy-client-0.4.2 → edx_enterprise_subsidy_client-0.4.4}/edx_enterprise_subsidy_client/__init__.py +1 -1
  4. {edx-enterprise-subsidy-client-0.4.2 → edx_enterprise_subsidy_client-0.4.4}/edx_enterprise_subsidy_client/client.py +75 -0
  5. {edx-enterprise-subsidy-client-0.4.2 → edx_enterprise_subsidy_client-0.4.4}/edx_enterprise_subsidy_client.egg-info/PKG-INFO +9 -1
  6. {edx-enterprise-subsidy-client-0.4.2 → edx_enterprise_subsidy_client-0.4.4}/tests/test_client.py +75 -0
  7. {edx-enterprise-subsidy-client-0.4.2 → edx_enterprise_subsidy_client-0.4.4}/LICENSE +0 -0
  8. {edx-enterprise-subsidy-client-0.4.2 → edx_enterprise_subsidy_client-0.4.4}/LICENSE.txt +0 -0
  9. {edx-enterprise-subsidy-client-0.4.2 → edx_enterprise_subsidy_client-0.4.4}/MANIFEST.in +0 -0
  10. {edx-enterprise-subsidy-client-0.4.2 → edx_enterprise_subsidy_client-0.4.4}/README.rst +0 -0
  11. {edx-enterprise-subsidy-client-0.4.2 → edx_enterprise_subsidy_client-0.4.4}/edx_enterprise_subsidy_client.egg-info/SOURCES.txt +0 -0
  12. {edx-enterprise-subsidy-client-0.4.2 → edx_enterprise_subsidy_client-0.4.4}/edx_enterprise_subsidy_client.egg-info/dependency_links.txt +0 -0
  13. {edx-enterprise-subsidy-client-0.4.2 → edx_enterprise_subsidy_client-0.4.4}/edx_enterprise_subsidy_client.egg-info/not-zip-safe +0 -0
  14. {edx-enterprise-subsidy-client-0.4.2 → edx_enterprise_subsidy_client-0.4.4}/edx_enterprise_subsidy_client.egg-info/requires.txt +0 -0
  15. {edx-enterprise-subsidy-client-0.4.2 → edx_enterprise_subsidy_client-0.4.4}/edx_enterprise_subsidy_client.egg-info/top_level.txt +0 -0
  16. {edx-enterprise-subsidy-client-0.4.2 → edx_enterprise_subsidy_client-0.4.4}/requirements/base.in +0 -0
  17. {edx-enterprise-subsidy-client-0.4.2 → edx_enterprise_subsidy_client-0.4.4}/requirements/constraints.txt +0 -0
  18. {edx-enterprise-subsidy-client-0.4.2 → edx_enterprise_subsidy_client-0.4.4}/setup.cfg +0 -0
  19. {edx-enterprise-subsidy-client-0.4.2 → edx_enterprise_subsidy_client-0.4.4}/setup.py +0 -0
@@ -14,6 +14,14 @@ Change Log
14
14
  Unreleased
15
15
  **********
16
16
 
17
+ [0.4.4]
18
+ *******
19
+ * feat: add support for deposit creation (ENT-9133)
20
+
21
+ [0.4.3]
22
+ *******
23
+ * feat: adding new subsidy client method to fetch subsidy aggregate data
24
+
17
25
  [0.4.2]
18
26
  *******
19
27
  * Switch from ``edx-sphinx-theme`` to ``sphinx-book-theme`` since the former is
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: edx-enterprise-subsidy-client
3
- Version: 0.4.2
3
+ Version: 0.4.4
4
4
  Summary: Client for interacting with the enterprise-subsidy service.
5
5
  Home-page: https://github.com/openedx/edx-enterprise-subsidy-client
6
6
  Author: edX
@@ -225,6 +225,14 @@ Change Log
225
225
  Unreleased
226
226
  **********
227
227
 
228
+ [0.4.4]
229
+ *******
230
+ * feat: add support for deposit creation (ENT-9133)
231
+
232
+ [0.4.3]
233
+ *******
234
+ * feat: adding new subsidy client method to fetch subsidy aggregate data
235
+
228
236
  [0.4.2]
229
237
  *******
230
238
  * Switch from ``edx-sphinx-theme`` to ``sphinx-book-theme`` since the former is
@@ -2,6 +2,6 @@
2
2
  Client for interacting with the enterprise-subsidy service..
3
3
  """
4
4
 
5
- __version__ = '0.4.2'
5
+ __version__ = '0.4.4'
6
6
 
7
7
  from .client import EnterpriseSubsidyAPIClient, EnterpriseSubsidyAPIClientV2, get_enterprise_subsidy_api_client
@@ -73,6 +73,41 @@ class EnterpriseSubsidyAPIClient:
73
73
  settings.BACKEND_SERVICE_EDX_OAUTH2_SECRET,
74
74
  )
75
75
 
76
+ def get_subsidy_aggregates_by_learner_url(self, subsidy_uuid):
77
+ """
78
+ Helper method to fetch subsidy learner aggregate data API url.
79
+ """
80
+ return f"{self.SUBSIDIES_ENDPOINT}{subsidy_uuid}/aggregates-by-learner"
81
+
82
+ def get_subsidy_aggregates_by_learner_data(self, subsidy_uuid, policy_uuid=None):
83
+ """
84
+ Client method to fetch subsidy specific learner aggregate data.
85
+
86
+ Args:
87
+ subsidy_uuid (str): Subsidy record UUID
88
+ policy_uuid (string): Optional param to filter subsidy aggregate data by subsidy access policy UUID
89
+ Returns:
90
+ json subsidy learner aggregate data response:
91
+ [{
92
+ 'lms_user_id': '1337',
93
+ 'enrollment_count': 45,
94
+ } ... ]
95
+ """
96
+ url = self.get_subsidy_aggregates_by_learner_url(subsidy_uuid)
97
+ if policy_uuid:
98
+ url += f"?subsidy_access_policy_uuid={policy_uuid}"
99
+ try:
100
+ resp = self.client.get(url)
101
+ response_data = resp
102
+ resp.raise_for_status()
103
+ except requests.exceptions.HTTPError as exc:
104
+ logger.exception(
105
+ f'Subsidy client failed to fetch aggregate data for {subsidy_uuid} '
106
+ f'and policy: {policy_uuid}'
107
+ )
108
+ raise exc
109
+ return response_data.json()
110
+
76
111
  def get_content_metadata_url(self, content_identifier):
77
112
  """Helper method to generate the subsidy service metadata API url, with a trailing slash."""
78
113
  return self.CONTENT_METADATA_ENDPOINT + content_identifier + '/'
@@ -256,6 +291,7 @@ class EnterpriseSubsidyAPIClientV2(EnterpriseSubsidyAPIClient): # pylint: disab
256
291
  """
257
292
  V2_BASE_URL = EnterpriseSubsidyAPIClient.API_BASE_URL + 'v2/'
258
293
  TRANSACTIONS_LIST_ENDPOINT = V2_BASE_URL + 'subsidies/{subsidy_uuid}/admin/transactions/'
294
+ DEPOSITS_CREATE_ENDPOINT = V2_BASE_URL + 'subsidies/{subsidy_uuid}/admin/deposits/'
259
295
 
260
296
  def list_subsidy_transactions(
261
297
  self, subsidy_uuid, include_aggregates=True,
@@ -335,3 +371,42 @@ class EnterpriseSubsidyAPIClientV2(EnterpriseSubsidyAPIClient): # pylint: disab
335
371
  )
336
372
  response.raise_for_status()
337
373
  return response.json()
374
+
375
+ def create_subsidy_deposit(
376
+ self,
377
+ subsidy_uuid,
378
+ desired_deposit_quantity,
379
+ sales_contract_reference_id,
380
+ sales_contract_reference_provider,
381
+ metadata,
382
+ idempotency_key=None,
383
+ ):
384
+ """
385
+ Creates a deposit in the given subsidy, requires operator-level permissions.
386
+
387
+ Raises:
388
+ requests.exceptions.HTTPError:
389
+ - 403 Forbidden: If auth failed.
390
+ - 429 Too Many Requests: If the ledger was locked (resource contention, try again later).
391
+ - 400 Bad Request: If any of the values were invalid. Reasons include:
392
+ * non-positive quantity.
393
+ * provider slug does not exist in database.
394
+ - 422 Unprocessable Entity: Catchall status for anything that prevented the deposit from being
395
+ created. Reasons include, but are not limited to:
396
+ * Subsidy is inactive.
397
+ * Another deposit with same idempotency_key already exists.
398
+ """
399
+ request_payload = {
400
+ 'desired_deposit_quantity': desired_deposit_quantity,
401
+ 'sales_contract_reference_id': sales_contract_reference_id,
402
+ 'sales_contract_reference_provider': sales_contract_reference_provider,
403
+ 'metadata': metadata,
404
+ }
405
+ if idempotency_key is not None:
406
+ request_payload['idempotency_key'] = idempotency_key
407
+ response = self.client.post(
408
+ self.DEPOSITS_CREATE_ENDPOINT.format(subsidy_uuid=subsidy_uuid),
409
+ json=request_payload,
410
+ )
411
+ response.raise_for_status()
412
+ return response.json()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: edx-enterprise-subsidy-client
3
- Version: 0.4.2
3
+ Version: 0.4.4
4
4
  Summary: Client for interacting with the enterprise-subsidy service.
5
5
  Home-page: https://github.com/openedx/edx-enterprise-subsidy-client
6
6
  Author: edX
@@ -225,6 +225,14 @@ Change Log
225
225
  Unreleased
226
226
  **********
227
227
 
228
+ [0.4.4]
229
+ *******
230
+ * feat: add support for deposit creation (ENT-9133)
231
+
232
+ [0.4.3]
233
+ *******
234
+ * feat: adding new subsidy client method to fetch subsidy aggregate data
235
+
228
236
  [0.4.2]
229
237
  *******
230
238
  * Switch from ``edx-sphinx-theme`` to ``sphinx-book-theme`` since the former is
@@ -4,6 +4,9 @@ Tests for edx_enterprise_subsidy_client.py.
4
4
  import uuid
5
5
  from unittest import mock
6
6
 
7
+ import requests
8
+ from pytest import raises
9
+
7
10
  from edx_enterprise_subsidy_client import EnterpriseSubsidyAPIClient, EnterpriseSubsidyAPIClientV2
8
11
  from test_utils.utils import MockResponse
9
12
 
@@ -16,6 +19,40 @@ def test_client_init():
16
19
  assert subsidy_client is not None
17
20
 
18
21
 
22
+ @mock.patch('edx_enterprise_subsidy_client.client.OAuthAPIClient', return_value=mock.MagicMock())
23
+ def test_client_fetch_subsidy_aggregate_data_success(mock_oauth_client):
24
+ """
25
+ Test the client's ability to handle api requests to fetch subsidy learner aggregate data from the subsidy service
26
+ """
27
+ mocked_data = {
28
+ 'lms_user_id': '1337',
29
+ 'enrollment_count': 10,
30
+ }
31
+ mock_oauth_client.return_value.get.return_value = MockResponse(mocked_data, 200)
32
+ subsidy_service_client = EnterpriseSubsidyAPIClient()
33
+ response = subsidy_service_client.get_subsidy_aggregates_by_learner_data(
34
+ subsidy_uuid=str(uuid.uuid4()),
35
+ )
36
+ assert response == mocked_data
37
+
38
+
39
+ @mock.patch('edx_enterprise_subsidy_client.client.OAuthAPIClient', return_value=mock.MagicMock())
40
+ def test_client_fetch_subsidy_aggregate_data_exception_handling(mock_oauth_client):
41
+ """
42
+ Test the client's ability to properly throw errors when the subsidy service returns an error response.
43
+ """
44
+ error_message = 'some_error_string'
45
+ mock_oauth_client.return_value.get.return_value = MockResponse(error_message, 400)
46
+ subsidy_service_client = EnterpriseSubsidyAPIClient()
47
+ with raises(requests.exceptions.HTTPError) as exc:
48
+ subsidy_service_client.get_subsidy_aggregates_by_learner_data(
49
+ subsidy_uuid=str(uuid.uuid4()),
50
+ )
51
+
52
+ assert exc.value.response.json() == error_message
53
+ assert exc.value.response.status_code == 400
54
+
55
+
19
56
  @mock.patch('edx_enterprise_subsidy_client.client.OAuthAPIClient', return_value=mock.MagicMock())
20
57
  def test_client_fetch_subsidy_content_data_success(mock_oauth_client):
21
58
  """
@@ -135,3 +172,41 @@ def test_v2_list_subsidy_transactions(mock_oauth_client):
135
172
  'page_size': 1,
136
173
  },
137
174
  )
175
+
176
+
177
+ @mock.patch('edx_enterprise_subsidy_client.client.OAuthAPIClient', return_value=mock.MagicMock())
178
+ def test_client_v2_create_subsidy_deposit(mock_oauth_client):
179
+ """
180
+ Test the client's ability to create subsidy deposits.
181
+ """
182
+ mocked_data = {
183
+ 'uuid': str(uuid.uuid4()),
184
+ }
185
+ mock_post = mock_oauth_client.return_value.post
186
+ mock_post.return_value = MockResponse(mocked_data, 201)
187
+
188
+ subsidy_service_client = EnterpriseSubsidyAPIClientV2()
189
+
190
+ payload = {
191
+ 'subsidy_uuid': uuid.uuid4(),
192
+ 'desired_deposit_quantity': 100,
193
+ 'sales_contract_reference_id': str(uuid.uuid4()),
194
+ 'sales_contract_reference_provider': 'foo-bar',
195
+ 'idempotency_key': 'hello',
196
+ 'metadata': {'key': 'value'},
197
+ }
198
+
199
+ response = subsidy_service_client.create_subsidy_deposit(**payload)
200
+
201
+ assert response == mocked_data
202
+ expected_url = EnterpriseSubsidyAPIClientV2.DEPOSITS_CREATE_ENDPOINT.format(
203
+ subsidy_uuid=payload['subsidy_uuid'],
204
+ )
205
+ expected_post_payload = {
206
+ 'desired_deposit_quantity': 100,
207
+ 'sales_contract_reference_id': payload['sales_contract_reference_id'],
208
+ 'sales_contract_reference_provider': 'foo-bar',
209
+ 'idempotency_key': 'hello',
210
+ 'metadata': {'key': 'value'},
211
+ }
212
+ mock_post.assert_called_once_with(expected_url, json=expected_post_payload)