Use new osb_id fields to match API requests
All checks were successful
Tests / test (push) Successful in 32s

This commit is contained in:
Tobias Kunze 2025-10-02 09:49:03 +02:00
parent 4be6eeb18f
commit e459047622
3 changed files with 70 additions and 70 deletions

View file

@ -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}"

View file

@ -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",
)

View file

@ -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",
)