import logging from django.urls import reverse from django.conf import settings from rest_framework import viewsets, status from rest_framework.response import Response from rest_framework.decorators import action from django.shortcuts import get_object_or_404 from hub.services.models import Lead from hub.services.odoo import OdooAPI from .models import ServiceInstance, ServiceBrokerUser from .serializers import ( ServiceInstanceProvisioningSerializer, CatalogSerializer, ServiceInstanceUpdateSerializer, ) from .authentication import ServiceBrokerAuthentication logger = logging.getLogger(__name__) class ServiceBrokerViewSet(viewsets.ViewSet): authentication_classes = [ServiceBrokerAuthentication] @action(detail=False, methods=["get"], url_path="catalog") def get_catalog(self, request): """ Return the catalog of services available to the authenticated user. """ # Get broker user and their allowed offerings broker_user = get_object_or_404(ServiceBrokerUser, user=request.user) offerings = broker_user.allowed_offerings.prefetch_related( "plans", "service", "cloud_provider", ).all() # Serialize the catalog serializer = CatalogSerializer({"services": offerings}) return Response(serializer.data) def _create_lead_from_provision_data(self, offering, plan, parameters, context): """Create a lead in Odoo from provisioning data""" # Get the first user from parameters as the main contact user_data = parameters["users"][0] lead = Lead( service=offering.service, offering=offering, plan=plan, name=user_data["full_name"], company=context["organization_display_name"], email=user_data["email"], phone="", # Not provided in broker API ) try: odoo = OdooAPI() lead.odoo_lead_id = odoo.create_lead(lead, source="osbapi") lead.save() return lead except Exception as e: logger.error(f"Failed to create lead in Odoo: {str(e)}") raise @action( detail=False, methods=["put"], url_path="service_instances/(?P[^/.]+)", ) def provision_instance(self, request, instance_id): # Validate request data serializer = ServiceInstanceProvisioningSerializer(data=request.data) if not serializer.is_valid(): return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) # Get broker user and check offering access broker_user = get_object_or_404(ServiceBrokerUser, user=request.user) offering = serializer.validated_data["service_id"] # This is now the offering if not broker_user.allowed_offerings.filter(id=offering.id).exists(): return Response( {"error": "Service offering not available for this user"}, status=status.HTTP_403_FORBIDDEN, ) # Check if instance already exists if ServiceInstance.objects.filter(instance_id=instance_id).exists(): return Response(status=status.HTTP_200_OK) try: # Create lead in Odoo lead = self._create_lead_from_provision_data( offering=offering, plan=serializer.validated_data["plan_id"], parameters=serializer.validated_data["parameters"], context=serializer.validated_data["context"], ) # Create service instance instance = ServiceInstance.objects.create( instance_id=instance_id, offering=offering, plan=serializer.validated_data["plan_id"], organization_guid=serializer.validated_data["organization_guid"], space_guid=serializer.validated_data["space_guid"], organization_name=serializer.validated_data["context"][ "organization_name" ], parameters=serializer.validated_data["parameters"], context=serializer.validated_data["context"], lead=lead, ) return Response( { "dashboard_url": settings.WEBSITE_URL + reverse( "services:offering_detail", kwargs={ "provider_slug": offering.cloud_provider.slug, "service_slug": offering.service.slug, }, ), "operation": "provision", }, status=status.HTTP_201_CREATED, ) except Exception as e: logger.error(f"Error provisioning instance: {str(e)}") return Response( {"error": "Failed to provision service instance"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR, ) @action( detail=False, methods=["delete"], url_path="service_instances/(?P[^/.]+)", ) def deprovision_instance(self, request, instance_id): instance = get_object_or_404(ServiceInstance, instance_id=instance_id) instance.delete() return Response(status=status.HTTP_200_OK) @action( detail=False, methods=["get"], url_path="service_instances/(?P[^/.]+)", ) def get_instance(self, request, instance_id): instance = get_object_or_404(ServiceInstance, instance_id=instance_id) return Response( { "service_id": str(instance.offering.id), "plan_id": str(instance.plan.id), "dashboard_url": settings.WEBSITE_URL + reverse( "services:offering_detail", kwargs={ "provider_slug": instance.offering.cloud_provider.slug, "service_slug": instance.offering.service.slug, }, ), } ) @action( detail=False, methods=["patch"], url_path="service_instances/(?P[^/.]+)", ) def update_instance(self, request, instance_id): instance = get_object_or_404(ServiceInstance, instance_id=instance_id) serializer = ServiceInstanceUpdateSerializer( data=request.data, context={"instance": instance} ) if not serializer.is_valid(): return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) instance.plan = serializer.validated_data["plan_id"] instance.save() return Response( { "operation": "update", "dashboard_url": settings.WEBSITE_URL + reverse( "services:offering_detail", kwargs={ "provider_slug": instance.offering.cloud_provider.slug, "service_slug": instance.offering.service.slug, }, ), } )