From 9c59005eb5c59b5b42d08df5e61ea2cff3a0f1fb Mon Sep 17 00:00:00 2001 From: Tobias Kunze Date: Sun, 9 Mar 2025 14:57:58 +0100 Subject: [PATCH] Rough first draft of service models --- src/servala/core/models/__init__.py | 16 ++++ src/servala/core/models/service.py | 133 ++++++++++++++++++++++++++++ 2 files changed, 149 insertions(+) create mode 100644 src/servala/core/models/service.py diff --git a/src/servala/core/models/__init__.py b/src/servala/core/models/__init__.py index 3456eab..28a75be 100644 --- a/src/servala/core/models/__init__.py +++ b/src/servala/core/models/__init__.py @@ -5,11 +5,27 @@ from .organization import ( OrganizationOrigin, OrganizationRole, ) +from .service import ( + ControlPlane, + Plan, + Service, + ServiceCategory, + ServiceOffering, + ServiceOfferingPlan, + ServiceProvider, +) __all__ = [ "BillingEntity", + "ControlPlane", "Organization", "OrganizationMembership", "OrganizationOrigin", "OrganizationRole", + "Plan", + "Service", + "ServiceCategory", + "ServiceOffering", + "ServiceOfferingPlan", + "ServiceProvider", ] diff --git a/src/servala/core/models/service.py b/src/servala/core/models/service.py new file mode 100644 index 0000000..4cd719b --- /dev/null +++ b/src/servala/core/models/service.py @@ -0,0 +1,133 @@ +from django.db import models +from django.utils.translation import gettext_lazy as _ + + +class ServiceCategory(models.Model): + name = models.CharField(max_length=100, verbose_name=_("Name")) + description = models.TextField(blank=True, verbose_name=_("Description")) + logo = models.ImageField( + upload_to="public/service_categories", + blank=True, + null=True, + verbose_name=_("Logo"), + ) + + parent = models.ForeignKey( + to="self", + on_delete=models.CASCADE, + related_name="children", + blank=True, + null=True, + verbose_name=_("Parent"), + ) + + def __str__(self): + return self.name + + +class Service(models.Model): + name = models.CharField(max_length=100, verbose_name=_("Name")) + description = models.TextField(blank=True, verbose_name=_("Description")) + logo = models.ImageField( + upload_to="public/services", blank=True, null=True, verbose_name=_("Logo") + ) + # TODO schema + external_links = models.JSONField(blank=True, verbose_name=_("External links")) + + category = models.ForeignKey( + to="ServiceCategory", + on_delete=models.PROTECT, + related_name="services", + verbose_name=_("Category"), + ) + + def __str__(self): + return self.name + + +class ControlPlane(models.Model): + name = models.CharField(max_length=100, verbose_name=_("Name")) + description = models.TextField(blank=True, verbose_name=_("Description")) + k8s_api_endpoint = models.URLField(verbose_name=_("Kubernetes API endpoint")) + # TODO: schema + api_credentials = models.JSONField(verbose_name=_("API credentials")) + + def __str__(self): + return self.name + + +class ServiceProvider(models.Model): + name = models.CharField(max_length=100, verbose_name=_("Name")) + description = models.TextField(blank=True, verbose_name=_("Description")) + logo = models.ImageField( + upload_to="public/service_providers", + blank=True, + null=True, + verbose_name=_("Logo"), + ) + external_links = models.JSONField(blank=True, verbose_name=_("External links")) + + control_plane = models.ForeignKey( + to="ControlPlane", + on_delete=models.PROTECT, + related_name="service_providers", + verbose_name=_("Control plane"), + ) + + def __str__(self): + return self.name + + +class Plan(models.Model): + name = models.CharField(max_length=100, verbose_name=_("Name")) + description = models.TextField(blank=True, verbose_name=_("Description")) + # TODO schema + features = models.JSONField(verbose_name=_("Features")) + # TODO schema + pricing = models.JSONField(verbose_name=_("Pricing")) + term = models.DurationField(verbose_name=_("Term")) + + def __str__(self): + return self.name + + +class ServiceOfferingPlan(models.Model): + offering = models.ForeignKey( + to="ServiceOffering", + on_delete=models.CASCADE, + related_name="offering_plans", + verbose_name=_("Offering"), + ) + plan = models.ForeignKey( + to="Plan", + on_delete=models.CASCADE, + related_name="offering_plans", + verbose_name=_("Plan"), + ) + pricing = models.JSONField(verbose_name=_("Pricing")) + + +class ServiceOffering(models.Model): + description = models.TextField(blank=True, verbose_name=_("Description")) + services = models.ManyToManyField( + to="Service", + related_name="offerings", + verbose_name=_("Services"), + ) + providers = models.ManyToManyField( + to="ServiceProvider", + related_name="offerings", + verbose_name=_("Providers"), + ) + control_plane = models.ForeignKey( + to="ControlPlane", + on_delete=models.PROTECT, + related_name="offerings", + verbose_name=_("Control plane"), + ) + plans = models.ManyToManyField( + to="Plan", + through="ServiceOfferingPlan", + related_name="offerings", + verbose_name=_("Plans"), + )