Skip to content

Commit

Permalink
Merge pull request #4481 from open-formulieren/migration-hotfix
Browse files Browse the repository at this point in the history
[#4267] Fix migration crash
  • Loading branch information
sergei-maertens authored Jul 2, 2024
2 parents 980272b + a7c4e62 commit cc7e811
Show file tree
Hide file tree
Showing 6 changed files with 212 additions and 123 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,44 +2,13 @@

from django.db import migrations

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


def add_default_objects_api_group(
apps: StateApps, schema_editor: BaseDatabaseSchemaEditor
) -> None:
FormRegistrationBackend = apps.get_model("forms", "FormRegistrationBackend")
ObjectsAPIGroupConfig = apps.get_model(
"registrations_objects_api", "ObjectsAPIGroupConfig"
)

objects_api_group_config = ObjectsAPIGroupConfig.objects.order_by("pk").first()
if objects_api_group_config is None:
# Because this migration runs after registrations_objects_api/0017_move_singleton_data,
# Having no Objects API Group means we had no solo config in the first place, thus
# it is safe to assume no Objects API registration backend was set up.
return

for registration_backend in FormRegistrationBackend.objects.filter(
backend="objects_api"
):
registration_backend.options.setdefault(
"objects_api_group", objects_api_group_config.pk
)
registration_backend.save()


class Migration(migrations.Migration):

dependencies = [
("forms", "0099_auto_20240613_0654"),
("registrations_objects_api", "0017_move_singleton_data"),
]

operations = [
migrations.RunPython(
add_default_objects_api_group,
migrations.RunPython.noop,
),
# Moved to `registrations_objects_api.0019_add_default_objects_api_group`.
]
23 changes: 1 addition & 22 deletions src/openforms/forms/migrations/0101_objecttype_url_to_uuid.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,6 @@

from django.db import migrations

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


def objecttype_url_to_uuid(
apps: StateApps, schema_editor: BaseDatabaseSchemaEditor
) -> None:
"""Change the objects API registration options to reference the objecttype UUID instead of the URL."""

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

for registration_backend in FormRegistrationBackend.objects.filter(
backend="objects_api"
):
objecttype_url = registration_backend.options["objecttype"]
registration_backend.options["objecttype"] = objecttype_url.rsplit("/", 1)[1]
registration_backend.save()


class Migration(migrations.Migration):

Expand All @@ -28,8 +10,5 @@ class Migration(migrations.Migration):
]

operations = [
migrations.RunPython(
objecttype_url_to_uuid,
migrations.RunPython.noop,
),
# Moved to `registrations_objects_api.0020_objecttype_url_to_uuid`.
]
69 changes: 0 additions & 69 deletions src/openforms/forms/tests/test_migrations.py
Original file line number Diff line number Diff line change
Expand Up @@ -451,75 +451,6 @@ def test_conditionals_are_fixed(self):
self.assertTrue(fixed_components[6]["components"][0]["conditional"]["eq"])


class AddDefaultObjectsAPIGroupMigrationTests(TestMigrations):
app = "forms"
migrate_from = "0099_auto_20240613_0654"
migrate_to = "0100_add_default_objects_api_group"

def setUpBeforeMigration(self, apps: StateApps) -> None:
Form = apps.get_model("forms", "Form")
FormRegistrationBackend = apps.get_model("forms", "FormRegistrationBackend")
ObjectsAPIGroupConfig = apps.get_model(
"registrations_objects_api", "ObjectsAPIGroupConfig"
)

form = Form.objects.create(name="test form")
ObjectsAPIGroupConfig.objects.create(name="Objects API Group")

FormRegistrationBackend.objects.create(
form=form,
name="Objects API backend",
key="backend",
backend="objects_api",
)

def test_sets_default_objects_api_group(self) -> None:
FormRegistrationBackend = self.apps.get_model(
"forms", "FormRegistrationBackend"
)
ObjectsAPIGroupConfig = self.apps.get_model(
"registrations_objects_api", "ObjectsAPIGroupConfig"
)

backend = FormRegistrationBackend.objects.get()
self.assertEqual(
backend.options["objects_api_group"], ObjectsAPIGroupConfig.objects.get().pk
)


class ObjecttypeUrltoUuidMigrationTests(TestMigrations):
app = "forms"
migrate_from = "0100_add_default_objects_api_group"
migrate_to = "0101_objecttype_url_to_uuid"

def setUpBeforeMigration(self, apps: StateApps) -> None:
Form = apps.get_model("forms", "Form")
FormRegistrationBackend = apps.get_model("forms", "FormRegistrationBackend")

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

FormRegistrationBackend.objects.create(
form=form,
name="Objects API backend",
key="backend",
backend="objects_api",
options={
"objecttype": "http://objecttypen.nl/api/v1/objecttypes/8e46e0a5-b1b4-449b-b9e9-fa3cea655f48",
},
)

def test_changes_objecttype_key_name(self) -> None:
FormRegistrationBackend = self.apps.get_model(
"forms", "FormRegistrationBackend"
)

backend = FormRegistrationBackend.objects.get()
self.assertEqual(
backend.options["objecttype"],
"8e46e0a5-b1b4-449b-b9e9-fa3cea655f48",
)


class PrefillIdentifierRoleRename(TestMigrations):
app = "forms"
migrate_from = "0102_alter_formvariable_prefill_identifier_role"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Generated by Django 4.2.11 on 2024-07-02 15:18

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


def add_default_objects_api_group(
apps: StateApps, schema_editor: BaseDatabaseSchemaEditor
) -> None:
FormRegistrationBackend = apps.get_model("forms", "FormRegistrationBackend")
ObjectsAPIGroupConfig = apps.get_model(
"registrations_objects_api", "ObjectsAPIGroupConfig"
)
backends_qs = FormRegistrationBackend.objects.filter(backend="objects_api")
# nothing to do if there are no relevant backends
if not backends_qs.exists():
return

objects_api_group_config = ObjectsAPIGroupConfig.objects.order_by("pk").first()
if objects_api_group_config is None:
# This shouldn't happen because of:
# * upgrade checks
# * operations in registrations_objects_api/0017_move_singleton_data
#
# BUT that doesn't mean it's impossible, like on continuously deployed
# environments... For those cases, we generate a (knowingly broken)
# configuration so that migrations don't crash and options have at least the
# right shape so that we can trust the type annotations.
objects_api_group_config = ObjectsAPIGroupConfig.objects.create(
name="AUTO_GENERATED - FIXME",
# DeprecationWarning
# Open Forms 3.0 will drop the nullable fields, so this data migration needs
# to be gone by then.
objects_service=None,
objecttypes_service=None,
drc_service=None,
catalogi_service=None,
)

for registration_backend in backends_qs:
registration_backend.options.setdefault(
"objects_api_group", objects_api_group_config.pk
)
registration_backend.save()


class Migration(migrations.Migration):

dependencies = [
(
"registrations_objects_api",
"0018_remove_objectsapiconfig_catalogi_service_and_more",
),
(
"forms",
"0100_add_default_objects_api_group",
),
]

operations = [
migrations.RunPython(
add_default_objects_api_group,
migrations.RunPython.noop,
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Generated by Django 4.2.11 on 2024-07-02 15:21

from django.db import migrations

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


def objecttype_url_to_uuid(
apps: StateApps, schema_editor: BaseDatabaseSchemaEditor
) -> None:
"""Change the objects API registration options to reference the objecttype UUID instead of the URL."""

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

for registration_backend in FormRegistrationBackend.objects.filter(
backend="objects_api"
):

objecttype_url = registration_backend.options.get("objecttype")
if objecttype_url is not None and "/" in objecttype_url:
# If it is `None`, we are dealing with broken confs. and upgrade checks where bypassed.
registration_backend.options["objecttype"] = objecttype_url.rsplit("/", 1)[
1
]
registration_backend.save()


class Migration(migrations.Migration):

dependencies = [
("registrations_objects_api", "0019_add_default_objects_api_group"),
("forms", "0100_add_default_objects_api_group"),
]

operations = [
migrations.RunPython(
objecttype_url_to_uuid,
migrations.RunPython.noop,
),
]
Original file line number Diff line number Diff line change
Expand Up @@ -275,3 +275,106 @@ def test_no_zgw_api_group_created(self):
)

self.assertFalse(ObjectsAPIGroupConfig.objects.exists())


class AddDefaultObjectsAPIGroupMigrationTests(TestMigrations):
app = "registrations_objects_api"
migrate_from = "0018_remove_objectsapiconfig_catalogi_service_and_more"
migrate_to = "0019_add_default_objects_api_group"

def setUpBeforeMigration(self, apps: StateApps) -> None:
Form = apps.get_model("forms", "Form")
FormRegistrationBackend = apps.get_model("forms", "FormRegistrationBackend")
ObjectsAPIGroupConfig = apps.get_model(
"registrations_objects_api", "ObjectsAPIGroupConfig"
)

form = Form.objects.create(name="test form")
ObjectsAPIGroupConfig.objects.create(name="Objects API Group")

FormRegistrationBackend.objects.create(
form=form,
name="Objects API backend",
key="backend",
backend="objects_api",
)

def test_sets_default_objects_api_group(self) -> None:
FormRegistrationBackend = self.apps.get_model(
"forms", "FormRegistrationBackend"
)
ObjectsAPIGroupConfig = self.apps.get_model(
"registrations_objects_api", "ObjectsAPIGroupConfig"
)

backend = FormRegistrationBackend.objects.get()
self.assertEqual(
backend.options["objects_api_group"], ObjectsAPIGroupConfig.objects.get().pk
)


class AddDefaultObjectsAPIGroupWithBrokenStateMigrationTests(TestMigrations):
app = "registrations_objects_api"
migrate_from = "0018_remove_objectsapiconfig_catalogi_service_and_more"
migrate_to = "0019_add_default_objects_api_group"

def setUpBeforeMigration(self, apps: StateApps) -> None:
Form = apps.get_model("forms", "Form")
FormRegistrationBackend = apps.get_model("forms", "FormRegistrationBackend")
ObjectsAPIGroupConfig = apps.get_model(
"registrations_objects_api", "ObjectsAPIGroupConfig"
)

form = Form.objects.create(name="test form")
FormRegistrationBackend.objects.create(
form=form,
name="Objects API backend",
key="backend",
backend="objects_api",
)
assert not ObjectsAPIGroupConfig.objects.exists()

def test_sets_default_objects_api_group(self) -> None:
FormRegistrationBackend = self.apps.get_model(
"forms", "FormRegistrationBackend"
)
ObjectsAPIGroupConfig = self.apps.get_model(
"registrations_objects_api", "ObjectsAPIGroupConfig"
)
auto_created_config = ObjectsAPIGroupConfig.objects.get()

backend = FormRegistrationBackend.objects.get()
self.assertEqual(backend.options["objects_api_group"], auto_created_config.pk)


class ObjecttypeUrltoUuidMigrationTests(TestMigrations):
app = "registrations_objects_api"
migrate_from = "0019_add_default_objects_api_group"
migrate_to = "0020_objecttype_url_to_uuid"

def setUpBeforeMigration(self, apps: StateApps) -> None:
Form = apps.get_model("forms", "Form")
FormRegistrationBackend = apps.get_model("forms", "FormRegistrationBackend")

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

FormRegistrationBackend.objects.create(
form=form,
name="Objects API backend",
key="backend",
backend="objects_api",
options={
"objecttype": "http://objecttypen.nl/api/v1/objecttypes/8e46e0a5-b1b4-449b-b9e9-fa3cea655f48",
},
)

def test_changes_objecttype_key_name(self) -> None:
FormRegistrationBackend = self.apps.get_model(
"forms", "FormRegistrationBackend"
)

backend = FormRegistrationBackend.objects.get()
self.assertEqual(
backend.options["objecttype"],
"8e46e0a5-b1b4-449b-b9e9-fa3cea655f48",
)

0 comments on commit cc7e811

Please sign in to comment.