-
Notifications
You must be signed in to change notification settings - Fork 26
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[#3688] Add a migration to create a default service if possible
- Loading branch information
Showing
2 changed files
with
200 additions
and
0 deletions.
There are no files selected for viewing
95 changes: 95 additions & 0 deletions
95
...istrations/contrib/objects_api/migrations/0011_create_objecttypesypes_service_from_url.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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), | ||
] |
105 changes: 105 additions & 0 deletions
105
src/openforms/registrations/contrib/objects_api/tests/test_migrations.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |