diff --git a/src/servala/api/views.py b/src/servala/api/views.py index 7ca0d82..48845d0 100644 --- a/src/servala/api/views.py +++ b/src/servala/api/views.py @@ -14,7 +14,7 @@ from django.views.decorators.csrf import csrf_exempt from servala.api.permissions import OSBBasicAuthPermission from servala.core.exoscale import get_exoscale_origin from servala.core.models import BillingEntity, Organization, User -from servala.core.models.service import Plan, Service +from servala.core.models.service import Service, ServiceOffering logger = logging.getLogger(__name__) @@ -90,11 +90,13 @@ class OSBServiceInstanceView(OSBBasicAuthPermission, View): return self._error(f"Unable to create user: {e}") try: - service = Service.objects.get(id=service_id) - plan = Plan.objects.get(id=plan_id, service_offering__service=service) + service = Service.objects.get(osb_service_id=service_id) + service_offering = ServiceOffering.objects.get( + osb_plan_id=plan_id, service=service + ) except Service.DoesNotExist: return self._error(f"Unknown service_id: {service_id}") - except Plan.DoesNotExist: + except ServiceOffering.DoesNotExist: return self._error( f"Unknown plan_id: {plan_id} for service_id: {service_id}" ) @@ -104,7 +106,9 @@ class OSBServiceInstanceView(OSBBasicAuthPermission, View): organization = Organization.objects.get( osb_guid=organization_guid, origin=exoscale_origin ) - self._send_service_welcome_email(request, organization, user, service, plan) + self._send_service_welcome_email( + request, organization, user, service, service_offering + ) return JsonResponse({"message": "Service already enabled"}, status=200) odoo_data = { @@ -126,7 +130,7 @@ class OSBServiceInstanceView(OSBBasicAuthPermission, View): self._send_invitation_email(request, organization, user) self._send_service_welcome_email( - request, organization, user, service, plan + request, organization, user, service, service_offering ) return JsonResponse( @@ -159,8 +163,10 @@ The Servala Team""" fail_silently=False, ) - def _send_service_welcome_email(self, request, organization, user, service, plan): - service_path = f"{organization.urls.services}{service.slug}/offering/{plan.service_offering.id}/" + def _send_service_welcome_email( + self, request, organization, user, service, service_offering + ): + service_path = f"{organization.urls.services}{service.slug}/offering/{service_offering.id}/" service_url = request.build_absolute_uri(service_path) subject = f"Get started with {service.name} - {organization.name}" diff --git a/src/tests/conftest.py b/src/tests/conftest.py index 3d176ad..32499ca 100644 --- a/src/tests/conftest.py +++ b/src/tests/conftest.py @@ -10,7 +10,6 @@ from servala.core.models import ( ) from servala.core.models.service import ( CloudProvider, - Plan, Service, ServiceCategory, ServiceOffering, @@ -56,6 +55,7 @@ def test_service(test_service_category): slug="redis", category=test_service_category, description="Redis database service", + osb_service_id="test-service-123", ) @@ -73,18 +73,7 @@ def test_service_offering(test_service, test_cloud_provider): service=test_service, provider=test_cloud_provider, description="Redis on Exoscale", - ) - - -@pytest.fixture -def test_plan(test_service_offering): - return Plan.objects.create( - name="Small", - description="Small Redis plan", - term=1, - service_offering=test_service_offering, - features={"memory": "1GB", "connections": 100}, - pricing={"monthly": 10.0}, + osb_plan_id="test-plan-123", ) diff --git a/src/tests/test_api_exoscale.py b/src/tests/test_api_exoscale.py index fa6fc02..a14bd44 100644 --- a/src/tests/test_api_exoscale.py +++ b/src/tests/test_api_exoscale.py @@ -50,16 +50,16 @@ def test_successful_onboarding_new_organization( mock_odoo_success, osb_client, test_service, - test_plan, + test_service_offering, valid_osb_payload, exoscale_origin, instance_id, ): - valid_osb_payload["service_id"] = test_service.id - valid_osb_payload["plan_id"] = test_plan.id + valid_osb_payload["service_id"] = test_service.osb_service_id + valid_osb_payload["plan_id"] = test_service_offering.osb_plan_id response = osb_client.put( - f"/v2/service_instances/{instance_id}", + f"/api/osb/v2/service_instances/{instance_id}", data=json.dumps(valid_osb_payload), content_type="application/json", ) @@ -102,7 +102,7 @@ def test_successful_onboarding_new_organization( def test_duplicate_organization_returns_existing( osb_client, test_service, - test_plan, + test_service_offering, valid_osb_payload, exoscale_origin, instance_id, @@ -113,11 +113,11 @@ def test_duplicate_organization_returns_existing( origin=exoscale_origin, ) - valid_osb_payload["service_id"] = test_service.id - valid_osb_payload["plan_id"] = test_plan.id + valid_osb_payload["service_id"] = test_service.osb_service_id + valid_osb_payload["plan_id"] = test_service_offering.osb_plan_id response = osb_client.put( - f"/v2/service_instances/{instance_id}", + f"/api/osb/v2/service_instances/{instance_id}", data=json.dumps(valid_osb_payload), content_type="application/json", ) @@ -133,15 +133,15 @@ def test_duplicate_organization_returns_existing( def test_unauthenticated_osb_api_request_fails( client, test_service, - test_plan, + test_service_offering, valid_osb_payload, instance_id, ): - valid_osb_payload["service_id"] = test_service.id - valid_osb_payload["plan_id"] = test_plan.id + valid_osb_payload["service_id"] = test_service.osb_service_id + valid_osb_payload["plan_id"] = test_service_offering.osb_plan_id response = client.put( - f"/v2/service_instances/{instance_id}", + f"/api/osb/v2/service_instances/{instance_id}", data=json.dumps(valid_osb_payload), content_type="application/json", ) @@ -169,14 +169,14 @@ def test_unauthenticated_osb_api_request_fails( def test_missing_required_fields_error( osb_client, test_service, - test_plan, + test_service_offering, valid_osb_payload, field_to_remove, expected_error, instance_id, ): - valid_osb_payload["service_id"] = test_service.id - valid_osb_payload["plan_id"] = test_plan.id + valid_osb_payload["service_id"] = test_service.osb_service_id + valid_osb_payload["plan_id"] = test_service_offering.osb_plan_id if isinstance(field_to_remove, tuple): if field_to_remove[0] == "context": @@ -188,7 +188,7 @@ def test_missing_required_fields_error( del valid_osb_payload[field_to_remove] response = osb_client.put( - f"/v2/service_instances/{instance_id}", + f"/api/osb/v2/service_instances/{instance_id}", data=json.dumps(valid_osb_payload), content_type="application/json", ) @@ -204,7 +204,7 @@ def test_invalid_service_id_error(osb_client, valid_osb_payload, instance_id): valid_osb_payload["plan_id"] = 1 response = osb_client.put( - f"/v2/service_instances/{instance_id}", + f"/api/osb/v2/service_instances/{instance_id}", data=json.dumps(valid_osb_payload), content_type="application/json", ) @@ -218,11 +218,11 @@ def test_invalid_service_id_error(osb_client, valid_osb_payload, instance_id): def test_invalid_plan_id_error( osb_client, test_service, valid_osb_payload, instance_id ): - valid_osb_payload["service_id"] = test_service.id + valid_osb_payload["service_id"] = test_service.osb_service_id valid_osb_payload["plan_id"] = 99999 response = osb_client.put( - f"/v2/service_instances/{instance_id}", + f"/api/osb/v2/service_instances/{instance_id}", data=json.dumps(valid_osb_payload), content_type="application/json", ) @@ -230,21 +230,21 @@ def test_invalid_plan_id_error( assert response.status_code == 400 response_data = json.loads(response.content) assert ( - f"Unknown plan_id: 99999 for service_id: {test_service.id}" + f"Unknown plan_id: 99999 for service_id: {test_service.osb_service_id}" in response_data["error"] ) @pytest.mark.django_db def test_empty_users_array_error( - osb_client, test_service, test_plan, valid_osb_payload, instance_id + osb_client, test_service, test_service_offering, valid_osb_payload, instance_id ): - valid_osb_payload["service_id"] = test_service.id - valid_osb_payload["plan_id"] = test_plan.id + valid_osb_payload["service_id"] = test_service.osb_service_id + valid_osb_payload["plan_id"] = test_service_offering.osb_plan_id valid_osb_payload["parameters"]["users"] = [] response = osb_client.put( - f"/v2/service_instances/{instance_id}", + f"/api/osb/v2/service_instances/{instance_id}", data=json.dumps(valid_osb_payload), content_type="application/json", ) @@ -256,17 +256,17 @@ def test_empty_users_array_error( @pytest.mark.django_db def test_multiple_users_error( - osb_client, test_service, test_plan, valid_osb_payload, instance_id + osb_client, test_service, test_service_offering, valid_osb_payload, instance_id ): - valid_osb_payload["service_id"] = test_service.id - valid_osb_payload["plan_id"] = test_plan.id + valid_osb_payload["service_id"] = test_service.osb_service_id + valid_osb_payload["plan_id"] = test_service_offering.osb_plan_id valid_osb_payload["parameters"]["users"] = [ {"email": "user1@example.com", "full_name": "User One"}, {"email": "user2@example.com", "full_name": "User Two"}, ] response = osb_client.put( - f"/v2/service_instances/{instance_id}", + f"/api/osb/v2/service_instances/{instance_id}", data=json.dumps(valid_osb_payload), content_type="application/json", ) @@ -278,16 +278,16 @@ def test_multiple_users_error( @pytest.mark.django_db def test_empty_email_address_error( - osb_client, test_service, test_plan, valid_osb_payload, instance_id + osb_client, test_service, test_service_offering, valid_osb_payload, instance_id ): - valid_osb_payload["service_id"] = test_service.id - valid_osb_payload["plan_id"] = test_plan.id + valid_osb_payload["service_id"] = test_service.osb_service_id + valid_osb_payload["plan_id"] = test_service_offering.osb_plan_id valid_osb_payload["parameters"]["users"] = [ {"email": "", "full_name": "User With No Email"}, ] response = osb_client.put( - f"/v2/service_instances/{instance_id}", + f"/api/osb/v2/service_instances/{instance_id}", data=json.dumps(valid_osb_payload), content_type="application/json", ) @@ -300,7 +300,7 @@ def test_empty_email_address_error( @pytest.mark.django_db def test_invalid_json_error(osb_client, instance_id): response = osb_client.put( - f"/v2/service_instances/{instance_id}", + f"/api/osb/v2/service_instances/{instance_id}", data="invalid json{", content_type="application/json", ) @@ -315,17 +315,17 @@ def test_user_creation_with_name_parsing( mock_odoo_success, osb_client, test_service, - test_plan, + test_service_offering, valid_osb_payload, exoscale_origin, instance_id, ): - valid_osb_payload["service_id"] = test_service.id - valid_osb_payload["plan_id"] = test_plan.id + valid_osb_payload["service_id"] = test_service.osb_service_id + valid_osb_payload["plan_id"] = test_service_offering.osb_plan_id valid_osb_payload["parameters"]["users"][0]["full_name"] = "John Doe Smith" response = osb_client.put( - f"/v2/service_instances/{instance_id}", + f"/api/osb/v2/service_instances/{instance_id}", data=json.dumps(valid_osb_payload), content_type="application/json", ) @@ -341,17 +341,17 @@ def test_email_normalization( mock_odoo_success, osb_client, test_service, - test_plan, + test_service_offering, valid_osb_payload, exoscale_origin, instance_id, ): - valid_osb_payload["service_id"] = test_service.id - valid_osb_payload["plan_id"] = test_plan.id + valid_osb_payload["service_id"] = test_service.osb_service_id + valid_osb_payload["plan_id"] = test_service_offering.osb_plan_id valid_osb_payload["parameters"]["users"][0]["email"] = " TEST@EXAMPLE.COM " response = osb_client.put( - f"/v2/service_instances/{instance_id}", + f"/api/osb/v2/service_instances/{instance_id}", data=json.dumps(valid_osb_payload), content_type="application/json", ) @@ -366,16 +366,16 @@ def test_odoo_integration_failure_handling( mock_odoo_failure, osb_client, test_service, - test_plan, + test_service_offering, valid_osb_payload, exoscale_origin, instance_id, ): - valid_osb_payload["service_id"] = test_service.id - valid_osb_payload["plan_id"] = test_plan.id + valid_osb_payload["service_id"] = test_service.osb_service_id + valid_osb_payload["plan_id"] = test_service_offering.osb_plan_id response = osb_client.put( - f"/v2/service_instances/{instance_id}", + f"/api/osb/v2/service_instances/{instance_id}", data=json.dumps(valid_osb_payload), content_type="application/json", ) @@ -387,11 +387,16 @@ def test_odoo_integration_failure_handling( @pytest.mark.django_db def test_organization_creation_with_context_only( - mock_odoo_success, osb_client, test_service, test_plan, exoscale_origin, instance_id + mock_odoo_success, + osb_client, + test_service, + test_service_offering, + exoscale_origin, + instance_id, ): payload = { - "service_id": test_service.id, - "plan_id": test_plan.id, + "service_id": test_service.osb_service_id, + "plan_id": test_service_offering.osb_plan_id, "context": { "organization_guid": "fallback-org-guid", "organization_name": "Fallback Organization", @@ -407,7 +412,7 @@ def test_organization_creation_with_context_only( } response = osb_client.put( - f"/v2/service_instances/{instance_id}", + f"/api/osb/v2/service_instances/{instance_id}", data=json.dumps(payload), content_type="application/json", )