Exoscale offboarding MVP #282

Merged
tobru merged 4 commits from 262-exoscale-offboarding into main 2025-11-17 10:49:24 +00:00
5 changed files with 70 additions and 46 deletions
Showing only changes of commit e4c64c4a17 - Show all commits

View file

@ -21,7 +21,7 @@ from servala.core.models import (
User,
)
from servala.core.models.service import Service, ServiceInstance, ServiceOffering
from servala.core.odoo import CLIENT
from servala.core.odoo import create_helpdesk_ticket
logger = logging.getLogger(__name__)
@ -269,9 +269,7 @@ The Servala Team"""
organization = None
try:
# Look for instances with this name in the service offering's context
# TODO: we do not currently match instance IDs from exoscale yet, this
# will likely not work at all yet
instances = (
service_instance = (
ServiceInstance.objects.filter(
name=instance_id,
context__service_offering=service_offering,
@ -280,7 +278,7 @@ The Servala Team"""
.first()
)
if instances:
if service_instance:
organization = service_instance.organization
except Exception:
pass
@ -327,13 +325,11 @@ The Servala Team"""
description_parts.append(f" - {full_name} ({user_link}) - {role}")
description = "\n".join(description_parts)
ticket_data = {
"name": f"Exoscale OSB {action} - {service.name} - {instance_id}",
"team_id": settings.ODOO["HELPDESK_TEAM_ID"],
"description": description,
}
CLIENT.execute("helpdesk.ticket", "create", [ticket_data])
create_helpdesk_ticket(
title=f"Exoscale OSB {action} - {service.name} - {instance_id}",
description=description,
)
logger.info(
f"Created {action} helpdesk ticket for instance {instance_id}, service {service.name}"
)

View file

@ -207,3 +207,19 @@ def get_invoice_addresses(user):
return invoice_addresses or []
except Exception:
return []
def create_helpdesk_ticket(title, description, partner_id=None, sale_order_id=None):
ticket_data = {
"name": title,
"team_id": settings.ODOO["HELPDESK_TEAM_ID"],
"description": description,
}
if partner_id:
ticket_data["partner_id"] = partner_id
if sale_order_id:
ticket_data["sale_order_id"] = sale_order_id
return CLIENT.execute("helpdesk.ticket", "create", [ticket_data])

View file

@ -1,11 +1,10 @@
from django.conf import settings
from django.contrib import messages
from django.shortcuts import redirect
from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _
from django.views.generic import FormView
from servala.core.odoo import CLIENT
from servala.core.odoo import create_helpdesk_ticket
from servala.frontend.forms.support import SupportForm
from servala.frontend.views.mixins import OrganizationViewMixin
@ -24,21 +23,16 @@ class SupportView(OrganizationViewMixin, FormView):
if not partner_id:
raise Exception("Could not get or create Odoo contact for user")
ticket_data = {
"name": f"Servala Support - Organization {organization.name}",
"team_id": settings.ODOO["HELPDESK_TEAM_ID"],
"partner_id": partner_id,
"description": message,
}
# All orgs should have a sale order ID, but legacy ones might not have it.
# Also, we want to be very sure that support requests work, especially for
# organizations where something in the creation process may have gone wrong,
# so if the ID does not exist, we omit it entirely.
if organization.odoo_sale_order_id:
ticket_data["sale_order_id"] = organization.odoo_sale_order_id
CLIENT.execute("helpdesk.ticket", "create", [ticket_data])
create_helpdesk_ticket(
title=f"Servala Support - Organization {organization.name}",
description=message,
partner_id=partner_id,
sale_order_id=organization.odoo_sale_order_id or None,
)
messages.success(
self.request,
_(

View file

@ -8,6 +8,7 @@ overrides/adds settings specific to testing.
from servala.settings import * # noqa: F403, F401
SECRET_KEY = "test-secret-key-for-testing-only-do-not-use-in-production"
SALT_KEY = SECRET_KEY
PASSWORD_HASHERS = [
"django.contrib.auth.hashers.MD5PasswordHasher",
]

View file

@ -614,7 +614,8 @@ def test_delete_creates_ticket_with_admin_links(
test_service_offering,
instance_id,
):
mock_api_client = mocker.patch("servala.api.views.CLIENT")
# Mock the create_helpdesk_ticket function
mock_create_ticket = mocker.patch("servala.api.views.create_helpdesk_ticket")
response = osb_client.delete(
f"/api/osb/v2/service_instances/{instance_id}"
@ -623,13 +624,15 @@ def test_delete_creates_ticket_with_admin_links(
assert response.status_code == 200
ticket_call = mock_api_client.execute.call_args_list[-1]
ticket_data = ticket_call[0][2][0]
# Verify the ticket was created with admin URL
mock_create_ticket.assert_called_once()
call_kwargs = mock_create_ticket.call_args[1]
assert "admin/core/serviceoffering" in ticket_data["description"]
assert f"/{test_service_offering.pk}/" in ticket_data["description"]
# Check that the description contains an admin URL
assert "admin/core/serviceoffering" in call_kwargs["description"]
assert f"/{test_service_offering.pk}/" in call_kwargs["description"]
assert (
ticket_data["name"]
call_kwargs["title"]
== f"Exoscale OSB Offboard - {test_service.name} - {instance_id}"
)
@ -644,7 +647,9 @@ def test_patch_creates_ticket_with_user_admin_links(
instance_id,
org_owner,
):
mock_api_client = mocker.patch("servala.api.views.CLIENT")
# Mock the create_helpdesk_ticket function
mock_create_ticket = mocker.patch("servala.api.views.create_helpdesk_ticket")
payload = {
"service_id": test_service.osb_service_id,
"plan_id": test_service_offering.osb_plan_id,
@ -666,13 +671,17 @@ def test_patch_creates_ticket_with_user_admin_links(
)
assert response.status_code == 200
ticket_call = mock_api_client.execute.call_args_list[-1]
ticket_data = ticket_call[0][2][0]
assert "admin/core/serviceoffering" in ticket_data["description"]
assert "admin/core/user" in ticket_data["description"]
assert f"/{org_owner.pk}/" in ticket_data["description"]
# Verify the ticket was created with admin URLs
mock_create_ticket.assert_called_once()
call_kwargs = mock_create_ticket.call_args[1]
# Check that the description contains admin URLs
assert "admin/core/serviceoffering" in call_kwargs["description"]
assert "admin/core/user" in call_kwargs["description"]
assert f"/{org_owner.pk}/" in call_kwargs["description"]
assert (
ticket_data["name"]
call_kwargs["title"]
== f"Exoscale OSB Suspend - {test_service.name} - {instance_id}"
)
@ -686,7 +695,9 @@ def test_ticket_includes_organization_and_instance_when_found(
test_service_offering,
organization,
):
mock_api_client = mocker.patch("servala.api.views.CLIENT")
# Mock the create_helpdesk_ticket function
mock_create_ticket = mocker.patch("servala.api.views.create_helpdesk_ticket")
service_definition = ServiceDefinition.objects.create(
name="Test Definition",
service=test_service,
@ -719,11 +730,17 @@ def test_ticket_includes_organization_and_instance_when_found(
)
assert response.status_code == 200
ticket_call = mock_api_client.execute.call_args_list[-1]
ticket_data = ticket_call[0][2][0]
assert f"Organization: {organization.name}" in ticket_data["description"]
assert "admin/core/organization" in ticket_data["description"]
assert f"/{organization.pk}/" in ticket_data["description"]
assert f"Instance: {service_instance.name}" in ticket_data["description"]
assert "admin/core/serviceinstance" in ticket_data["description"]
assert f"/{service_instance.pk}/" in ticket_data["description"]
# Verify the ticket was created with all admin URLs
mock_create_ticket.assert_called_once()
call_kwargs = mock_create_ticket.call_args[1]
# Check organization is included
assert f"Organization: {organization.name}" in call_kwargs["description"]
assert "admin/core/organization" in call_kwargs["description"]
assert f"/{organization.pk}/" in call_kwargs["description"]
# Check instance is included
assert f"Instance: {service_instance.name}" in call_kwargs["description"]
assert "admin/core/serviceinstance" in call_kwargs["description"]
assert f"/{service_instance.pk}/" in call_kwargs["description"]