Skip to content

Commit acf1aa8

Browse files
committed
feat: handle payment error on trial
1 parent fcdda0a commit acf1aa8

File tree

7 files changed

+897
-288
lines changed

7 files changed

+897
-288
lines changed

enterprise_access/apps/api_client/license_manager_client.py

Lines changed: 84 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,71 @@ class LicenseManagerApiClient(BaseOAuthClient):
2828
subscription_provisioning_endpoint = api_base_url + 'provisioning-admins/subscriptions/'
2929
subscription_plan_renewal_provisioning_endpoint = api_base_url + 'provisioning-admins/subscription-plan-renewals/'
3030

31+
def list_subscriptions(self, enterprise_customer_uuid):
32+
"""
33+
List subscription plans for an enterprise.
34+
35+
Returns a paginated DRF list response: { count, next, previous, results: [...] }
36+
"""
37+
try:
38+
params = {
39+
'enterprise_customer_uuid': enterprise_customer_uuid,
40+
}
41+
42+
response = self.client.get(
43+
self.subscriptions_endpoint,
44+
params=params,
45+
timeout=settings.LICENSE_MANAGER_CLIENT_TIMEOUT,
46+
)
47+
response.raise_for_status()
48+
return response.json()
49+
except requests.exceptions.HTTPError as exc:
50+
logger.exception(
51+
'Failed to list subscriptions for enterprise %s, response: %s, exc: %s',
52+
enterprise_customer_uuid, safe_error_response_content(exc), exc,
53+
)
54+
raise
55+
56+
def update_subscription_plan(self, subscription_uuid, salesforce_opportunity_line_item=None, **kwargs):
57+
"""
58+
Partially update a SubscriptionPlan via the provisioning-admins endpoint.
59+
60+
Accepts any fields supported by license-manager for SubscriptionPlan patching, including:
61+
- is_active (bool)
62+
- change_reason (str)
63+
- salesforce_opportunity_line_item (str)
64+
65+
Args:
66+
subscription_uuid (str): Subscription plan UUID.
67+
salesforce_opportunity_line_item (str|None): Optional Salesforce OLI to associate.
68+
**kwargs: Additional JSON fields to patch.
69+
70+
Returns:
71+
dict: JSON response from license-manager.
72+
"""
73+
payload = {**kwargs}
74+
if salesforce_opportunity_line_item:
75+
payload['salesforce_opportunity_line_item'] = salesforce_opportunity_line_item
76+
77+
if not payload:
78+
raise ValueError('Must supply payload to update subscription plan')
79+
80+
endpoint = f"{self.subscription_provisioning_endpoint}{subscription_uuid}/"
81+
try:
82+
response = self.client.patch(
83+
endpoint,
84+
json=payload,
85+
timeout=settings.LICENSE_MANAGER_CLIENT_TIMEOUT,
86+
)
87+
response.raise_for_status()
88+
return response.json()
89+
except requests.exceptions.HTTPError as exc:
90+
logger.exception(
91+
'Failed to update subscription %s, payload=%s, response: %s, exc: %s',
92+
subscription_uuid, payload, safe_error_response_content(exc), exc,
93+
)
94+
raise
95+
3196
def get_subscription_overview(self, subscription_uuid):
3297
"""
3398
Call license-manager API for data about a SubscriptionPlan.
@@ -212,46 +277,6 @@ def create_subscription_plan(
212277
exc,
213278
) from exc
214279

215-
def update_subscription_plan(self, subscription_uuid, salesforce_opportunity_line_item):
216-
"""
217-
Update a SubscriptionPlan's Salesforce Opportunity Line Item.
218-
219-
Arguments:
220-
subscription_uuid (str): UUID of the SubscriptionPlan to update
221-
salesforce_opportunity_line_item (str): Salesforce OLI to associate with the plan
222-
223-
Returns:
224-
dict: Updated subscription plan data from the API
225-
226-
Raises:
227-
APIClientException: If the API call fails
228-
"""
229-
endpoint = f"{self.api_base_url}subscription-plans/{subscription_uuid}/"
230-
payload = {
231-
'salesforce_opportunity_line_item': salesforce_opportunity_line_item
232-
}
233-
234-
try:
235-
response = self.client.patch(
236-
endpoint,
237-
json=payload,
238-
timeout=settings.LICENSE_MANAGER_CLIENT_TIMEOUT
239-
)
240-
response.raise_for_status()
241-
return response.json()
242-
except requests.exceptions.HTTPError as exc:
243-
logger.exception(
244-
'Failed to update subscription plan %s with OLI %s, response %s, exception: %s',
245-
subscription_uuid,
246-
salesforce_opportunity_line_item,
247-
safe_error_response_content(exc),
248-
exc,
249-
)
250-
raise APIClientException(
251-
f'Could not update subscription plan {subscription_uuid}',
252-
exc,
253-
) from exc
254-
255280
def create_subscription_plan_renewal(
256281
self,
257282
prior_subscription_plan_uuid: str,
@@ -321,6 +346,25 @@ def create_subscription_plan_renewal(
321346
)
322347
raise
323348

349+
def retrieve_subscription_plan_renewal(self, renewal_id: int) -> dict:
350+
"""Fetch the details for a specific subscription plan renewal."""
351+
endpoint = f'{self.subscription_plan_renewal_provisioning_endpoint}{renewal_id}/'
352+
try:
353+
response = self.client.get(endpoint, timeout=settings.LICENSE_MANAGER_CLIENT_TIMEOUT)
354+
response.raise_for_status()
355+
return response.json()
356+
except requests.exceptions.HTTPError as exc:
357+
logger.exception(
358+
'Failed to retrieve subscription plan renewal %s, response: %s, exc: %s',
359+
renewal_id,
360+
safe_error_response_content(exc),
361+
exc,
362+
)
363+
raise APIClientException(
364+
f'Could not fetch subscription plan renewal {renewal_id}',
365+
exc,
366+
) from exc
367+
324368
def process_subscription_plan_renewal(self, renewal_id: int) -> dict:
325369
"""
326370
Process an existing subscription plan renewal via the license-manager service.

enterprise_access/apps/api_client/tests/test_license_manager_client.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,68 @@ def test_create_customer_agreement(self, mock_oauth_client):
9090
json=expected_payload,
9191
)
9292

93+
@mock.patch('enterprise_access.apps.api_client.base_oauth.OAuthAPIClient', autospec=True)
94+
def test_list_subscriptions_params(self, mock_oauth_client):
95+
mock_get = mock_oauth_client.return_value.get
96+
mock_get.return_value.json.return_value = {'results': []}
97+
98+
lm_client = LicenseManagerApiClient()
99+
enterprise_uuid = 'ec-uuid-123'
100+
101+
# Should only set enterprise_customer_uuid parameter
102+
result = lm_client.list_subscriptions(enterprise_uuid)
103+
self.assertEqual(result, {'results': []})
104+
105+
# Verify URL and params
106+
expected_url = (
107+
'http://license-manager.example.com'
108+
'/api/v1/subscriptions/'
109+
)
110+
mock_get.assert_called_with(
111+
expected_url,
112+
params={'enterprise_customer_uuid': enterprise_uuid},
113+
timeout=settings.LICENSE_MANAGER_CLIENT_TIMEOUT,
114+
)
115+
116+
@mock.patch('enterprise_access.apps.api_client.base_oauth.OAuthAPIClient', autospec=True)
117+
def test_update_subscription_plan_patch(self, mock_oauth_client):
118+
mock_patch = mock_oauth_client.return_value.patch
119+
mock_patch.return_value.json.return_value = {'uuid': 'plan-uuid', 'is_active': False}
120+
121+
lm_client = LicenseManagerApiClient()
122+
payload = {'is_active': False, 'change_reason': 'delayed_payment'}
123+
result = lm_client.update_subscription_plan('plan-uuid', **payload)
124+
125+
self.assertEqual(result, mock_patch.return_value.json.return_value)
126+
expected_url = (
127+
'http://license-manager.example.com'
128+
'/api/v1/provisioning-admins/subscriptions/plan-uuid/'
129+
)
130+
mock_patch.assert_called_once_with(
131+
expected_url,
132+
json=payload,
133+
timeout=settings.LICENSE_MANAGER_CLIENT_TIMEOUT,
134+
)
135+
136+
@mock.patch('enterprise_access.apps.api_client.base_oauth.OAuthAPIClient', autospec=True)
137+
def test_retrieve_subscription_plan_renewal(self, mock_oauth_client):
138+
mock_get = mock_oauth_client.return_value.get
139+
mock_get.return_value.json.return_value = {'id': 42}
140+
141+
lm_client = LicenseManagerApiClient()
142+
renewal_id = 42
143+
result = lm_client.retrieve_subscription_plan_renewal(renewal_id)
144+
145+
self.assertEqual(result, {'id': 42})
146+
expected_url = (
147+
'http://license-manager.example.com'
148+
'/api/v1/provisioning-admins/subscription-plan-renewals/42/'
149+
)
150+
mock_get.assert_called_once_with(
151+
expected_url,
152+
timeout=settings.LICENSE_MANAGER_CLIENT_TIMEOUT,
153+
)
154+
93155
@mock.patch('enterprise_access.apps.api_client.base_oauth.OAuthAPIClient', autospec=True)
94156
def test_create_subscription_plan(self, mock_oauth_client):
95157
mock_post = mock_oauth_client.return_value.post

0 commit comments

Comments
 (0)