Skip to content

Commit

Permalink
Merge pull request #93 from maykinmedia/add-service-slug-and-natural-key
Browse files Browse the repository at this point in the history
Add service slug and natural key
  • Loading branch information
swrichards authored Aug 13, 2024
2 parents aceb68b + 2d44fee commit 70f4dc7
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 2 deletions.
16 changes: 16 additions & 0 deletions tests/test_models.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from django.core.exceptions import ValidationError
from django.db import IntegrityError

import pytest
import requests_mock
Expand Down Expand Up @@ -118,3 +119,18 @@ def test_connection_check_service_model_correctly_configured(settings):
)
service.refresh_from_db()
assert service.connection_check == 200


@pytest.mark.django_db
def test_can_get_service_by_natural_key():
service = ServiceFactory.create()

assert Service.objects.get_by_natural_key(*service.natural_key()) == service


@pytest.mark.django_db
def test_fields_making_up_natural_key_field_are_unique():
ServiceFactory.create(slug="i-should-be-unique")

with pytest.raises(IntegrityError):
ServiceFactory.create(slug="i-should-be-unique")
2 changes: 2 additions & 0 deletions tests/test_oas_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from django.core.exceptions import ValidationError
from django.core.files.base import File
from django.utils.text import slugify

import pytest

Expand All @@ -26,6 +27,7 @@ def test_use_local_oas_file(settings, tmp_path):
api_type=APITypes.drc,
api_root="http://foo.bar",
oas_file=File(oas_file, name="schema.yaml"),
slug=slugify("http://foo.bar"),
)
service.full_clean()

Expand Down
5 changes: 5 additions & 0 deletions tests/test_zgw_services_legacy.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from pathlib import Path

from django.core.files.base import File
from django.utils.text import slugify

import pytest

Expand Down Expand Up @@ -73,11 +74,13 @@ def test_get_catalogi(settings, requests_mock):
api_type=APITypes.ztc,
api_root=CATALOGI_API_ROOT,
oas_file=File(oas_file, name="schema.yaml"),
slug=slugify(CATALOGI_API_ROOT),
)
service2 = Service.objects.create(
api_type=APITypes.ztc,
api_root=CATALOGI_API_ROOT2,
oas_file=File(oas_file, name="schema.yaml"),
slug=slugify(CATALOGI_API_ROOT2),
)
client1 = service1.build_client()
client2 = service2.build_client()
Expand Down Expand Up @@ -232,11 +235,13 @@ def test_get_informatieobjecttypen(settings, requests_mock):
api_type=APITypes.ztc,
api_root=CATALOGI_API_ROOT,
oas_file=File(oas_file, name="schema.yaml"),
slug=slugify(CATALOGI_API_ROOT),
)
service2 = Service.objects.create(
api_type=APITypes.ztc,
api_root=CATALOGI_API_ROOT2,
oas_file=File(oas_file, name="schema.yaml"),
slug=slugify(CATALOGI_API_ROOT2),
)
client1 = service1.build_client()
client2 = service2.build_client()
Expand Down
5 changes: 3 additions & 2 deletions zgw_consumers/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@

@admin.register(Service)
class ServiceAdmin(admin.ModelAdmin):
list_display = ("label", "api_type", "api_root", "nlx", "auth_type")
list_display = ("label", "api_type", "api_root", "slug", "nlx", "auth_type")
list_filter = ("api_type", "auth_type")
search_fields = ("label", "api_root", "nlx", "uuid")
search_fields = ("label", "api_root", "nlx", "uuid", "slug")
readonly_fields = ("get_connection_check",)
prepopulated_fields = {"slug": ["api_root"]}

@admin.display(description=_("Connection check status code"))
def get_connection_check(self, obj):
Expand Down
53 changes: 53 additions & 0 deletions zgw_consumers/migrations/0022_set_default_service_slug.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Generated by Django 5.1 on 2024-08-12 13:20

from django.db import migrations, models
from django.utils.text import slugify


def set_service_slug_default_from_api_root(apps, schema_editor):
Service = apps.get_model("zgw_consumers", "Service")

def generate_unique_slug(original_slug, count=0):
slug = original_slug + ("-" + str(count) if count else "")
if not Service.objects.filter(slug=slug).exists():
return slug

return generate_unique_slug(original_slug, count + 1)

for row in Service.objects.all():
candidate_slug = slugify(row.label if row.label else row.api_root)
row.slug = generate_unique_slug(candidate_slug)
row.save(update_fields=["slug"])


class Migration(migrations.Migration):

dependencies = [
("zgw_consumers", "0021_service_api_connection_check_path"),
]

operations = [
migrations.AddField(
model_name="service",
name="slug",
field=models.SlugField(
unique=False,
blank=True,
db_index=False,
),
preserve_default=False,
),
migrations.RunPython(
set_service_slug_default_from_api_root,
reverse_code=migrations.RunPython.noop,
),
migrations.AlterField(
model_name="service",
name="slug",
field=models.SlugField(
help_text="A unique, human-friendly slug to identify this service. Primarily useful for cross-instance import/export.",
unique=True,
verbose_name="service slug",
),
),
]
19 changes: 19 additions & 0 deletions zgw_consumers/models/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,22 @@
from ..legacy.client import ZGWClient


class ServiceManager(models.Manager):
def get_by_natural_key(self, slug):
return self.get(slug=slug)


class Service(RestAPIService):
uuid = models.UUIDField(_("UUID"), default=uuid.uuid4)
slug = models.SlugField(
_("service slug"),
blank=False,
null=False,
unique=True,
help_text=_(
"A unique, human-friendly slug to identify this service. Primarily useful for cross-instance import/export."
),
)
api_type = models.CharField(_("type"), max_length=20, choices=APITypes.choices)
api_root = models.CharField(_("api root url"), max_length=255, unique=True)
api_connection_check_path = models.CharField(
Expand Down Expand Up @@ -99,13 +113,18 @@ class Service(RestAPIService):
default=10,
)

objects = ServiceManager()

class Meta:
verbose_name = _("service")
verbose_name_plural = _("services")

def __str__(self):
return f"[{self.get_api_type_display()}] {self.label}"

def natural_key(self):
return (self.slug,)

def save(self, *args, **kwargs):
if not self.api_root.endswith("/"):
self.api_root = f"{self.api_root}/"
Expand Down
5 changes: 5 additions & 0 deletions zgw_consumers/test/factories.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import uuid

from django.utils.text import slugify

import factory
from faker.providers.internet import Provider as InternetProvider

Expand All @@ -21,6 +25,7 @@ def api_root(self) -> str:
class ServiceFactory(factory.django.DjangoModelFactory):
label = factory.Sequence(lambda n: f"API-{n}")
api_root = factory.Faker("api_root")
slug = factory.LazyAttribute(lambda o: slugify(o.api_root))

class Meta:
model = Service
Expand Down

0 comments on commit 70f4dc7

Please sign in to comment.