Skip to content
Open
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
105 changes: 65 additions & 40 deletions enterprise_access/apps/api_client/license_manager_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,71 @@ class LicenseManagerApiClient(BaseOAuthClient):
subscription_provisioning_endpoint = api_base_url + 'provisioning-admins/subscriptions/'
subscription_plan_renewal_provisioning_endpoint = api_base_url + 'provisioning-admins/subscription-plan-renewals/'

def list_subscriptions(self, enterprise_customer_uuid):
"""
List subscription plans for an enterprise.

Returns a paginated DRF list response: { count, next, previous, results: [...] }
"""
try:
params = {
'enterprise_customer_uuid': enterprise_customer_uuid,
}

response = self.client.get(
self.subscriptions_endpoint,
params=params,
timeout=settings.LICENSE_MANAGER_CLIENT_TIMEOUT,
)
response.raise_for_status()
return response.json()
except requests.exceptions.HTTPError as exc:
logger.exception(
'Failed to list subscriptions for enterprise %s, response: %s, exc: %s',
enterprise_customer_uuid, safe_error_response_content(exc), exc,
)
raise

def update_subscription_plan(self, subscription_uuid, salesforce_opportunity_line_item=None, **kwargs):
"""
Partially update a SubscriptionPlan via the provisioning-admins endpoint.

Accepts any fields supported by license-manager for SubscriptionPlan patching, including:
- is_active (bool)
- change_reason (str)
- salesforce_opportunity_line_item (str)

Args:
subscription_uuid (str): Subscription plan UUID.
salesforce_opportunity_line_item (str|None): Optional Salesforce OLI to associate.
**kwargs: Additional JSON fields to patch.

Returns:
dict: JSON response from license-manager.
"""
payload = {**kwargs}
if salesforce_opportunity_line_item:
payload['salesforce_opportunity_line_item'] = salesforce_opportunity_line_item

if not payload:
raise ValueError('Must supply payload to update subscription plan')

endpoint = f"{self.subscription_provisioning_endpoint}{subscription_uuid}/"
try:
response = self.client.patch(
endpoint,
json=payload,
timeout=settings.LICENSE_MANAGER_CLIENT_TIMEOUT,
)
response.raise_for_status()
return response.json()
except requests.exceptions.HTTPError as exc:
logger.exception(
'Failed to update subscription %s, payload=%s, response: %s, exc: %s',
subscription_uuid, payload, safe_error_response_content(exc), exc,
)
raise

def get_subscription_overview(self, subscription_uuid):
"""
Call license-manager API for data about a SubscriptionPlan.
Expand Down Expand Up @@ -212,46 +277,6 @@ def create_subscription_plan(
exc,
) from exc

def update_subscription_plan(self, subscription_uuid, salesforce_opportunity_line_item):
"""
Update a SubscriptionPlan's Salesforce Opportunity Line Item.

Arguments:
subscription_uuid (str): UUID of the SubscriptionPlan to update
salesforce_opportunity_line_item (str): Salesforce OLI to associate with the plan

Returns:
dict: Updated subscription plan data from the API

Raises:
APIClientException: If the API call fails
"""
endpoint = f"{self.api_base_url}subscription-plans/{subscription_uuid}/"
payload = {
'salesforce_opportunity_line_item': salesforce_opportunity_line_item
}

try:
response = self.client.patch(
endpoint,
json=payload,
timeout=settings.LICENSE_MANAGER_CLIENT_TIMEOUT
)
response.raise_for_status()
return response.json()
except requests.exceptions.HTTPError as exc:
logger.exception(
'Failed to update subscription plan %s with OLI %s, response %s, exception: %s',
subscription_uuid,
salesforce_opportunity_line_item,
safe_error_response_content(exc),
exc,
)
raise APIClientException(
f'Could not update subscription plan {subscription_uuid}',
exc,
) from exc

def create_subscription_plan_renewal(
self,
prior_subscription_plan_uuid: str,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,49 @@ def test_create_customer_agreement(self, mock_oauth_client):
json=expected_payload,
)

@mock.patch('enterprise_access.apps.api_client.base_oauth.OAuthAPIClient', autospec=True)
def test_list_subscriptions_params(self, mock_oauth_client):
mock_get = mock_oauth_client.return_value.get
mock_get.return_value.json.return_value = {'results': []}

lm_client = LicenseManagerApiClient()
enterprise_uuid = 'ec-uuid-123'

# Should only set enterprise_customer_uuid parameter
result = lm_client.list_subscriptions(enterprise_uuid)
self.assertEqual(result, {'results': []})

# Verify URL and params
expected_url = (
'http://license-manager.example.com'
'/api/v1/subscriptions/'
)
mock_get.assert_called_with(
expected_url,
params={'enterprise_customer_uuid': enterprise_uuid},
timeout=settings.LICENSE_MANAGER_CLIENT_TIMEOUT,
)

@mock.patch('enterprise_access.apps.api_client.base_oauth.OAuthAPIClient', autospec=True)
def test_update_subscription_plan_patch(self, mock_oauth_client):
mock_patch = mock_oauth_client.return_value.patch
mock_patch.return_value.json.return_value = {'uuid': 'plan-uuid', 'is_active': False}

lm_client = LicenseManagerApiClient()
payload = {'is_active': False, 'change_reason': 'delayed_payment'}
result = lm_client.update_subscription_plan('plan-uuid', **payload)

self.assertEqual(result, mock_patch.return_value.json.return_value)
expected_url = (
'http://license-manager.example.com'
'/api/v1/provisioning-admins/subscriptions/plan-uuid/'
)
mock_patch.assert_called_once_with(
expected_url,
json=payload,
timeout=settings.LICENSE_MANAGER_CLIENT_TIMEOUT,
)

@mock.patch('enterprise_access.apps.api_client.base_oauth.OAuthAPIClient', autospec=True)
def test_create_subscription_plan(self, mock_oauth_client):
mock_post = mock_oauth_client.return_value.post
Expand Down
Loading
Loading