(plugin) add CommuneCreation plugin

Add unit tests and refactor name normalization and zone naming.
This commit is contained in:
Laurent Bossavit
2025-02-06 11:50:27 +01:00
committed by Laurent Bossavit
parent a68f8171cb
commit 4cb695c2bf
3 changed files with 46 additions and 9 deletions

View File

@@ -10,6 +10,7 @@ and this project adheres to
### Added
- ✨(plugin) add CommuneCreation plugin with domain provisioning #658
- ✨(frontend) display action required status on domain
- ✨(domains) store last health check details on MailDomain
- ✨(domains) add support email field on domain

View File

@@ -1,8 +1,10 @@
"""Organization related plugins."""
import logging
import re
from django.conf import settings
from django.utils.text import slugify
import requests
from requests.adapters import HTTPAdapter, Retry
@@ -139,6 +141,8 @@ class CommuneCreation(BaseOrganizationPlugin):
def dns_call(self, spec):
"""Call to add a DNS record"""
zone_name = self.zone_name(spec.inputs["name"])
records = [
{
"name": item["target"],
@@ -151,16 +155,24 @@ class CommuneCreation(BaseOrganizationPlugin):
result = ApiCall()
result.method = "PATCH"
result.base = "https://api.scaleway.com"
result.url = (
f"/domain/v2beta1/dns-zones/{spec.inputs['name']}.collectivite.fr/records"
)
result.url = f"/domain/v2beta1/dns-zones/{zone_name}/records"
result.params = {"changes": [{"add": {"records": records}}]}
result.headers = {"X-Auth-Token": settings.DNS_PROVISIONING_API_CREDENTIALS}
return result
def normalize_name(self, name: str) -> str:
"""Map the name to a standard form"""
name = re.sub("'", "-", name)
return slugify(name)
def zone_name(self, name: str) -> str:
"""Derive the zone name from the commune name"""
normalized = self.normalize_name(name)
return f"{normalized}.collectivite.fr"
def complete_commune_creation(self, name: str) -> ApiCall:
"""Specify the tasks to be completed after a commune is created."""
inputs = {"name": name.lower()}
inputs = {"name": self.normalize_name(name)}
create_zone = ApiCall()
create_zone.method = "POST"
@@ -175,15 +187,17 @@ class CommuneCreation(BaseOrganizationPlugin):
"X-Auth-Token": settings.DNS_PROVISIONING_API_CREDENTIALS
}
zone_name = self.zone_name(inputs["name"])
create_domain = ApiCall()
create_domain.method = "POST"
create_domain.base = settings.MAIL_PROVISIONING_API_URL
create_domain.url = "/domains"
create_domain.params = {
"name": f"{inputs['name']}.collectivite.fr",
"name": zone_name,
"delivery": "virtual",
"features": ["webmail", "mailbox"],
"context_name": f"{inputs['name']}.collectivite.fr",
"context_name": zone_name,
}
create_domain.headers = {
"Authorization": f"Basic {settings.MAIL_PROVISIONING_API_CREDENTIALS}"
@@ -192,7 +206,7 @@ class CommuneCreation(BaseOrganizationPlugin):
spec_domain = ApiCall()
spec_domain.inputs = inputs
spec_domain.base = settings.MAIL_PROVISIONING_API_URL
spec_domain.url = f"/domains/{inputs['name']}.collectivite.fr/spec"
spec_domain.url = f"/domains/{zone_name}/spec"
spec_domain.headers = {
"Authorization": f"Basic {settings.MAIL_PROVISIONING_API_CREDENTIALS}"
}
@@ -234,7 +248,9 @@ class CommuneCreation(BaseOrganizationPlugin):
organization.save(update_fields=["name", "updated_at"])
logger.info("Organization %s name updated to %s", organization, name)
MailDomain.objects.get_or_create(name=f"{name.lower()}.collectivite.fr")
zone_name = self.zone_name(name)
support = f"support@{zone_name}"
MailDomain.objects.get_or_create(name=zone_name, support_email=support)
# Compute and execute the rest of the process
tasks = self.complete_commune_creation(name)
@@ -247,7 +263,7 @@ class CommuneCreation(BaseOrganizationPlugin):
"""After granting an organization access, check for needed domain access grant."""
orga = organization_access.organization
user = organization_access.user
zone_name = orga.name.lower() + ".collectivite.fr"
zone_name = self.zone_name(orga.name)
try:
domain = MailDomain.objects.get(name=zone_name)

View File

@@ -169,3 +169,23 @@ def test_tasks_on_commune_creation_include_dns_records():
assert (
zone_call.headers["X-Auth-Token"] == settings.DNS_PROVISIONING_API_CREDENTIALS
)
def test_normalize_name():
"""Test name normalization"""
plugin = CommuneCreation()
assert plugin.normalize_name("Asnières-sur-Saône") == "asnieres-sur-saone"
assert plugin.normalize_name("Bâgé-le-Châtel") == "bage-le-chatel"
assert plugin.normalize_name("Courçais") == "courcais"
assert plugin.normalize_name("Moÿ-de-l'Aisne") == "moy-de-l-aisne"
assert plugin.normalize_name("Salouël") == "salouel"
assert (
plugin.normalize_name("Bors (Canton de Tude-et-Lavalette)")
== "bors-canton-de-tude-et-lavalette"
)
def test_zone_name():
"""Test transforming a commune name to a sub-zone of collectivite.fr"""
plugin = CommuneCreation()
assert plugin.zone_name("Bâgé-le-Châtel") == "bage-le-chatel.collectivite.fr"