Skip to content

Commit

Permalink
[#3688] Add a migration to create a default service if possible
Browse files Browse the repository at this point in the history
  • Loading branch information
Viicos committed Jan 31, 2024
1 parent ef7885f commit e81b791
Show file tree
Hide file tree
Showing 2 changed files with 200 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Generated by Django 3.2.23 on 2024-01-31 14:20
import logging
import re
from furl import furl

from django.db import migrations
from django.db.migrations.state import StateApps
from django.db.backends.base.schema import BaseDatabaseSchemaEditor

from openforms.registrations.contrib.objects_api.plugin import (
PLUGIN_IDENTIFIER as OBJECTS_API_PLUGIN_IDENTIFIER,
)

from zgw_consumers.constants import APITypes

logger = logging.getLogger(__name__)


def create_objecttypes_api_service(
apps: StateApps, schema_editor: BaseDatabaseSchemaEditor
) -> None:
"""Create a "placeholder" Objecttypes API service from an already existing Objecttypes URL.
This migration exists to help users migrate to a new version of Open Forms. Note that
they will still need to configure authentication on the created Objecttypes Service.
"""
ObjectsAPIConfig = apps.get_model("registrations_objects_api", "ObjectsAPIConfig")

# First, try getting the default objecttypes URL
try:
objects_api_config = ObjectsAPIConfig.objects.get()
except ObjectsAPIConfig.DoesNotExist:
return
if objects_api_config.objecttypes_service:
# Extra safety, even though it shouldn't happen
return

objecttypes_url = objects_api_config.objecttype

# If no default objecttypes URL was configured, get it from form registration backends:
if not objecttypes_url:
form_registration_urls: set[str] = set()

FormRegistrationBackend = apps.get_model("forms", "FormRegistrationBackend")
for form_registration_backend in FormRegistrationBackend.objects.filter(
backend=OBJECTS_API_PLUGIN_IDENTIFIER
).iterator():
if url := form_registration_backend.options.get("objecttype"):
form_registration_urls.add(url)

if len(form_registration_urls) >= 2:
logger.warning(
"Found %d different Objecttypes URLs in form registration backend options",
len(form_registration_urls),
)

if form_registration_urls:
objecttypes_url = form_registration_urls.pop()

if objecttypes_url:
Service = apps.get_model("zgw_consumers", "Service")

root_url = furl(objecttypes_url).remove(fragment=True, query=True)
path_segments = root_url.path.segments

# Keep the relevant part of the path,
# e.g. /path/v2/other/path/api/v2/objecttypes/uuid -> /path/v2/other/path/api/v2/
for i, path in enumerate(reversed(path_segments)):
if re.match(r"v\d", path):
root_url.set(path=path_segments[: len(path_segments) - i])
break

# This "path normalization" is normally done in the `Service.save` method,
# but we are dealing with "fake" models during migrations.
if not root_url.path.isdir:
root_url.path.segments.append("") # Empty string means leading '/'

objects_api_config.objecttypes_service = Service.objects.create(
api_type=APITypes.orc,
api_root=str(root_url),
oas=str(root_url / "schema/openapi.yaml"),
)

objects_api_config.save()


class Migration(migrations.Migration):

dependencies = [
("registrations_objects_api", "0010_objectsapiconfig_objecttypes_service"),
]

operations = [
migrations.RunPython(create_objecttypes_api_service, migrations.RunPython.noop),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
from django.db.migrations.state import StateApps

from openforms.registrations.contrib.objects_api.plugin import (
PLUGIN_IDENTIFIER as OBJECTS_API_PLUGIN_IDENTIFIER,
)
from openforms.utils.tests.test_migrations import TestMigrations


class ObjecttypesServiceFromDefaultUrlMigrationTests(TestMigrations):
app = "registrations_objects_api"
migrate_from = "0010_objectsapiconfig_objecttypes_service"
migrate_to = "0011_create_objecttypesypes_service_from_url"

def setUpBeforeMigration(self, apps: StateApps):
ObjectsAPIConfig = apps.get_model(
"registrations_objects_api", "ObjectsAPIConfig"
)

ObjectsAPIConfig.objects.create(
objecttype="https://objecttypen.nl/unrelated/version/v4/api/v1/objecttypes/2c66dabf-a967-4057-9969-0700320d23a2",
)

def test_migration_sets_service_from_default_url(self):
ObjectsAPIConfig = self.apps.get_model(
"registrations_objects_api", "ObjectsAPIConfig"
)

objects_api_config = ObjectsAPIConfig.objects.get()

self.assertEqual(
objects_api_config.objecttypes_service.api_root,
"https://objecttypen.nl/unrelated/version/v4/api/v1/",
)

self.assertEqual(
objects_api_config.objecttypes_service.oas,
"https://objecttypen.nl/unrelated/version/v4/api/v1/schema/openapi.yaml",
)


class ObjecttypesServiceFromFormMigrationTests(TestMigrations):
app = "registrations_objects_api"
migrate_from = "0010_objectsapiconfig_objecttypes_service"
migrate_to = "0011_create_objecttypesypes_service_from_url"

def setUpBeforeMigration(self, apps: StateApps):

ObjectsAPIConfig = apps.get_model(
"registrations_objects_api", "ObjectsAPIConfig"
)
ObjectsAPIConfig.objects.create()

FormRegistrationBackend = apps.get_model("forms", "FormRegistrationBackend")
Form = apps.get_model("forms", "Form")

form = Form.objects.create(name="test form")

# This one shouldn't be used
FormRegistrationBackend.objects.create(
form=form,
key="dummy",
name="dummy",
backend="unrelated",
options={
"objecttype": "https://example.com/api/v1/objecttypes/a62257f8-6357-4626-96b7-fd6025517ff7"
},
)

FormRegistrationBackend.objects.create(
form=form,
key="dummy2",
name="dummy2",
backend=OBJECTS_API_PLUGIN_IDENTIFIER,
options={
"objecttype": "https://objecttypen.nl/api/v1/objecttypes/2c66dabf-a967-4057-9969-0700320d23a2"
},
)

def test_migration_sets_service_from_form_registration_url(self):
ObjectsAPIConfig = self.apps.get_model(
"registrations_objects_api", "ObjectsAPIConfig"
)
objects_api_config = ObjectsAPIConfig.objects.get()

self.assertEqual(
objects_api_config.objecttypes_service.api_root,
"https://objecttypen.nl/api/v1/",
)

self.assertEqual(
objects_api_config.objecttypes_service.oas,
"https://objecttypen.nl/api/v1/schema/openapi.yaml",
)


class NoObjecttypesServiceMigrationTests(TestMigrations):
app = "registrations_objects_api"
migrate_from = "0010_objectsapiconfig_objecttypes_service"
migrate_to = "0011_create_objecttypesypes_service_from_url"

def test_migration_does_nothing_if_no_objects_api_config(self):
ObjectsAPIConfig = self.apps.get_model(
"registrations_objects_api", "ObjectsAPIConfig"
)
self.assertRaises(ObjectsAPIConfig.DoesNotExist, ObjectsAPIConfig.objects.get)

0 comments on commit e81b791

Please sign in to comment.