diff --git a/src/objecttypes/setup_configuration/demo.py b/src/objecttypes/setup_configuration/demo.py deleted file mode 100644 index 215d4c7e..00000000 --- a/src/objecttypes/setup_configuration/demo.py +++ /dev/null @@ -1,53 +0,0 @@ -from django.conf import settings -from django.urls import reverse - -import requests -from django_setup_configuration.configuration import BaseConfigurationStep -from django_setup_configuration.exceptions import SelfTestFailed - -from objecttypes.token.models import TokenAuth -from objecttypes.utils import build_absolute_url - - -class DemoUserStep(BaseConfigurationStep): - """ - Create demo user to request Objectypes API - """ - - verbose_name = "Demo User Configuration" - required_settings = [ - "DEMO_TOKEN", - "DEMO_PERSON", - "DEMO_EMAIL", - ] - enable_setting = "DEMO_CONFIG_ENABLE" - - def is_configured(self) -> bool: - return TokenAuth.objects.filter(token=settings.DEMO_TOKEN).exists() - - def configure(self): - TokenAuth.objects.update_or_create( - token=settings.DEMO_TOKEN, - defaults={ - "contact_person": settings.DEMO_PERSON, - "email": settings.DEMO_EMAIL, - }, - ) - - def test_configuration(self): - endpoint = reverse("v2:objecttype-list") - full_url = build_absolute_url(endpoint, request=None) - - try: - response = requests.get( - full_url, - headers={ - "Authorization": f"Token {settings.DEMO_TOKEN}", - "Accept": "application/json", - }, - ) - response.raise_for_status() - except requests.RequestException as exc: - raise SelfTestFailed( - "Could not list objecttypes for the configured token" - ) from exc diff --git a/src/objecttypes/setup_configuration/models.py b/src/objecttypes/setup_configuration/models.py index 3457bb52..522f0d57 100644 --- a/src/objecttypes/setup_configuration/models.py +++ b/src/objecttypes/setup_configuration/models.py @@ -1,3 +1,5 @@ +from django.contrib.sites.models import Site + from django_setup_configuration.models import ConfigurationModel from pydantic import Field @@ -21,3 +23,17 @@ class Meta: class TokenAuthGroupConfigurationModel(ConfigurationModel): items: list[TokenAuthConfigurationModel] = Field() + + +class SiteConfigurationModel(ConfigurationModel): + class Meta: + django_model_refs = { + Site: ( + "domain", + "name", + ) + } + + +class SiteGroupConfigurationModel(ConfigurationModel): + items: list[SiteConfigurationModel] = Field() diff --git a/src/objecttypes/setup_configuration/objects.py b/src/objecttypes/setup_configuration/objects.py deleted file mode 100644 index a24d5cf5..00000000 --- a/src/objecttypes/setup_configuration/objects.py +++ /dev/null @@ -1,55 +0,0 @@ -from django.conf import settings -from django.urls import reverse - -import requests -from django_setup_configuration.configuration import BaseConfigurationStep -from django_setup_configuration.exceptions import SelfTestFailed - -from objecttypes.token.models import TokenAuth -from objecttypes.utils import build_absolute_url - - -class ObjectsAuthStep(BaseConfigurationStep): - """ - Configure credentials for Objects API to request Objecttypes API - """ - - verbose_name = "Objects API Authentication Configuration" - required_settings = [ - "OBJECTS_OBJECTTYPES_TOKEN", - "OBJECTS_OBJECTTYPES_PERSON", - "OBJECTS_OBJECTTYPES_EMAIL", - ] - enable_setting = "OBJECTS_OBJECTTYPES_CONFIG_ENABLE" - - def is_configured(self) -> bool: - return TokenAuth.objects.filter( - token=settings.OBJECTS_OBJECTTYPES_TOKEN - ).exists() - - def configure(self): - TokenAuth.objects.update_or_create( - token=settings.OBJECTS_OBJECTTYPES_TOKEN, - defaults={ - "contact_person": settings.OBJECTS_OBJECTTYPES_PERSON, - "email": settings.OBJECTS_OBJECTTYPES_EMAIL, - }, - ) - - def test_configuration(self): - endpoint = reverse("v2:objecttype-list") - full_url = build_absolute_url(endpoint, request=None) - - try: - response = requests.get( - full_url, - headers={ - "Authorization": f"Token {settings.OBJECTS_OBJECTTYPES_TOKEN}", - "Accept": "application/json", - }, - ) - response.raise_for_status() - except requests.RequestException as exc: - raise SelfTestFailed( - "Could not list objecttypes for the configured token" - ) from exc diff --git a/src/objecttypes/setup_configuration/site.py b/src/objecttypes/setup_configuration/site.py deleted file mode 100644 index 3d00b878..00000000 --- a/src/objecttypes/setup_configuration/site.py +++ /dev/null @@ -1,37 +0,0 @@ -from django.conf import settings -from django.contrib.sites.models import Site -from django.urls import reverse - -import requests -from django_setup_configuration.configuration import BaseConfigurationStep -from django_setup_configuration.exceptions import SelfTestFailed - -from objecttypes.utils import build_absolute_url - - -class SiteConfigurationStep(BaseConfigurationStep): - """ - Configure the application site/domain. - """ - - verbose_name = "Site Configuration" - required_settings = ["OBJECTTYPES_DOMAIN", "OBJECTTYPES_ORGANIZATION"] - enable_setting = "SITES_CONFIG_ENABLE" - - def is_configured(self) -> bool: - site = Site.objects.get_current() - return site.domain == settings.OBJECTTYPES_DOMAIN - - def configure(self): - site = Site.objects.get_current() - site.domain = settings.OBJECTTYPES_DOMAIN - site.name = f"Objecttypes {settings.OBJECTTYPES_ORGANIZATION}".strip() - site.save() - - def test_configuration(self): - full_url = build_absolute_url(reverse("home")) - try: - response = requests.get(full_url) - response.raise_for_status() - except requests.RequestException as exc: - raise SelfTestFailed(f"Could not access home page at '{full_url}'") from exc diff --git a/src/objecttypes/setup_configuration/steps.py b/src/objecttypes/setup_configuration/steps.py index 51732922..6cbe2efe 100644 --- a/src/objecttypes/setup_configuration/steps.py +++ b/src/objecttypes/setup_configuration/steps.py @@ -1,12 +1,16 @@ import logging +from django.contrib.sites.models import Site from django.core.exceptions import ValidationError from django.db import IntegrityError from django_setup_configuration.configuration import BaseConfigurationStep from django_setup_configuration.exceptions import ConfigurationRunFailed -from objecttypes.setup_configuration.models import TokenAuthGroupConfigurationModel +from objecttypes.setup_configuration.models import ( + SiteGroupConfigurationModel, + TokenAuthGroupConfigurationModel, +) from objecttypes.token.models import TokenAuth logger = logging.getLogger(__name__) @@ -69,32 +73,49 @@ def execute(self, model: TokenAuthGroupConfigurationModel) -> None: logger.info(f"Configured {item.identifier}") -class SiteConfigurationStep(BaseConfigurationStep[TokenAuthGroupConfigurationModel]): +class SitesConfigurationStep(BaseConfigurationStep[SiteGroupConfigurationModel]): """ - Configure configuration groups for the Objects API backend - Configure the application site/domain. + """ - verbose_name = "Site Configuration" - required_settings = ["OBJECTTYPES_DOMAIN", "OBJECTTYPES_ORGANIZATION"] - enable_setting = "SITES_CONFIG_ENABLE" + namespace = "objecttypes_sites" + enable_setting = "objecttypes_site_config_enable" + + verbose_name = "Configuration to set up Sites for ObjectTypes" + config_model = SiteGroupConfigurationModel + + def execute(self, model: SiteGroupConfigurationModel) -> None: + for item in model.items: + logger.info(f"Configuring {item.domain}") - def is_configured(self) -> bool: - site = Site.objects.get_current() - return site.domain == settings.OBJECTTYPES_DOMAIN + model_kwargs = { + "domain": item.domain, + "name": item.name, + } - def configure(self): - site = Site.objects.get_current() - site.domain = settings.OBJECTTYPES_DOMAIN - site.name = f"Objecttypes {settings.OBJECTTYPES_ORGANIZATION}".strip() - site.save() + instance = Site(**model_kwargs) - def test_configuration(self): - full_url = build_absolute_url(reverse("home")) - try: - response = requests.get(full_url) - response.raise_for_status() - except requests.RequestException as exc: - raise SelfTestFailed(f"Could not access home page at '{full_url}'") from exc + try: + instance.full_clean(exclude=("id",), validate_unique=False) + except ValidationError as exception: + exception_message = f"Validation error(s) occured for {item.domain}." + raise ConfigurationRunFailed(exception_message) from exception - """ + logger.debug(f"No validation errors found for {item.domain}") + + try: + logger.debug(f"Saving {item.domain}") + Site.objects.update_or_create( + domain=item.domain, + defaults={ + key: value + for key, value in model_kwargs.items() + if key != "domain" + }, + ) + + except IntegrityError as exception: + exception_message = f"Failed configuring token {item.domain}." + raise ConfigurationRunFailed(exception_message) from exception + + logger.info(f"Configured {item.domain}") diff --git a/src/objecttypes/setup_configuration/tests/files/sites/invalid_setup.yaml b/src/objecttypes/setup_configuration/tests/files/sites/invalid_setup.yaml new file mode 100644 index 00000000..1c377cf1 --- /dev/null +++ b/src/objecttypes/setup_configuration/tests/files/sites/invalid_setup.yaml @@ -0,0 +1,3 @@ +objecttypes_site_config_enable: true +objecttypes_sites: + items: \ No newline at end of file diff --git a/src/objecttypes/setup_configuration/tests/files/sites/valid_setup.yaml b/src/objecttypes/setup_configuration/tests/files/sites/valid_setup.yaml new file mode 100644 index 00000000..9034cb6c --- /dev/null +++ b/src/objecttypes/setup_configuration/tests/files/sites/valid_setup.yaml @@ -0,0 +1,8 @@ +objecttypes_site_config_enable: true +objecttypes_sites: + items: + - domain: example-1.com + name: example-1 + + - domain: example-2.com + name: example-2 diff --git a/src/objecttypes/setup_configuration/tests/files/invalid_setup_empty.yaml b/src/objecttypes/setup_configuration/tests/files/token_auth/invalid_setup_empty.yaml similarity index 100% rename from src/objecttypes/setup_configuration/tests/files/invalid_setup_empty.yaml rename to src/objecttypes/setup_configuration/tests/files/token_auth/invalid_setup_empty.yaml diff --git a/src/objecttypes/setup_configuration/tests/files/valid_setup_complete.yaml b/src/objecttypes/setup_configuration/tests/files/token_auth/valid_setup_complete.yaml similarity index 100% rename from src/objecttypes/setup_configuration/tests/files/valid_setup_complete.yaml rename to src/objecttypes/setup_configuration/tests/files/token_auth/valid_setup_complete.yaml diff --git a/src/objecttypes/setup_configuration/tests/files/valid_setup_default.yaml b/src/objecttypes/setup_configuration/tests/files/token_auth/valid_setup_default.yaml similarity index 100% rename from src/objecttypes/setup_configuration/tests/files/valid_setup_default.yaml rename to src/objecttypes/setup_configuration/tests/files/token_auth/valid_setup_default.yaml diff --git a/src/objecttypes/setup_configuration/tests/test_site_config.py b/src/objecttypes/setup_configuration/tests/test_site_config.py new file mode 100644 index 00000000..e957efdf --- /dev/null +++ b/src/objecttypes/setup_configuration/tests/test_site_config.py @@ -0,0 +1,93 @@ +from pathlib import Path + +from django.contrib.sites.models import Site +from django.test import TestCase + +from django_setup_configuration.exceptions import ( + ConfigurationRunFailed, + PrerequisiteFailed, +) +from django_setup_configuration.test_utils import build_step_config_from_sources + +from objecttypes.setup_configuration.steps import SitesConfigurationStep + +DIR_FILES = (Path(__file__).parent / "files/sites").resolve() + + +class SitesConfigurationStepTests(TestCase): + def test_valid_setup_default(self): + sites = Site.objects.order_by("pk") + site = sites[0] + self.assertEqual(sites.count(), 1) + self.assertEqual(site.domain, "example.com") + self.assertEqual(site.name, "example.com") + + setup_config = build_step_config_from_sources( + SitesConfigurationStep, + str(DIR_FILES / "valid_setup.yaml"), + ) + step = SitesConfigurationStep() + step.execute(setup_config) + + sites = Site.objects.order_by("pk") + self.assertEqual(sites.count(), 3) + + site = sites[1] + self.assertEqual(site.domain, "example-1.com") + self.assertEqual(site.name, "example-1") + + site = sites[2] + self.assertEqual(site.domain, "example-2.com") + self.assertEqual(site.name, "example-2") + + def test_valid_update_existing_sites(self): + sites = Site.objects.order_by("pk") + site = sites[0] + self.assertEqual(sites.count(), 1) + self.assertEqual(site.domain, "example.com") + self.assertEqual(site.name, "example.com") + + Site.objects.create(domain="example-2.com", name="example-3") + sites = Site.objects.order_by("pk") + self.assertEqual(sites.count(), 2) + + setup_config = build_step_config_from_sources( + SitesConfigurationStep, + str(DIR_FILES / "valid_setup.yaml"), + ) + step = SitesConfigurationStep() + step.execute(setup_config) + + sites = Site.objects.order_by("pk") + self.assertEqual(sites.count(), 3) + + site = sites[1] + self.assertEqual(site.domain, "example-2.com") + self.assertEqual(site.name, "example-2") + + site = sites[2] + self.assertEqual(site.domain, "example-1.com") + self.assertEqual(site.name, "example-1") + + def test_invalid_setup_empty(self): + sites = Site.objects.order_by("pk") + site = sites[0] + self.assertEqual(sites.count(), 1) + self.assertEqual(site.domain, "example.com") + self.assertEqual(site.name, "example.com") + + with self.assertRaises(PrerequisiteFailed) as command_error: + setup_config = build_step_config_from_sources( + SitesConfigurationStep, + str(DIR_FILES / "invalid_setup.yaml"), + ) + step = SitesConfigurationStep() + step.execute(setup_config) + + self.assertTrue("Input should be a valid list" in str(command_error.exception)) + + sites = Site.objects.order_by("pk") + site = sites[0] + self.assertEqual(sites.count(), 1) + self.assertEqual(site.domain, "example.com") + self.assertEqual(site.name, "example.com") diff --git a/src/objecttypes/setup_configuration/tests/test_token_auth_config.py b/src/objecttypes/setup_configuration/tests/test_token_auth_config.py index e197595f..da0b9e00 100644 --- a/src/objecttypes/setup_configuration/tests/test_token_auth_config.py +++ b/src/objecttypes/setup_configuration/tests/test_token_auth_config.py @@ -12,7 +12,7 @@ from objecttypes.token.models import TokenAuth from objecttypes.token.tests.factories.token import TokenAuthFactory -DIR_FILES = (Path(__file__).parent / "files").resolve() +DIR_FILES = (Path(__file__).parent / "files/token_auth").resolve() class TokenAuthConfigurationStepTests(TestCase): @@ -288,6 +288,45 @@ def test_invalid_setup_token_missing(self): self.assertTrue("Field required" in str(command_error.exception)) self.assertEqual(TokenAuth.objects.count(), 0) + def test_invalid_setup_token_unique(self): + object_source = { + "objecttypes_tokens_config_enable": True, + "objecttypes_tokens": { + "items": [ + { + "identifier": "token-1", + "contact_person": "Person 1", + "token": "ba9d233e95e04c4a8a661a27daffe7c9bd019067", + "email": "person-1@example.com", + "organization": "Organization 1", + "application": "Application 1", + "administration": "Administration 1", + }, + { + "identifier": "token-2", + "contact_person": "Person 2", + "token": "ba9d233e95e04c4a8a661a27daffe7c9bd019067", + "email": "person-2@example.com", + "organization": "Organization 2", + "application": "Application 2", + "administration": "Administration 2", + }, + ], + }, + } + with self.assertRaises(ConfigurationRunFailed) as command_error: + setup_config = build_step_config_from_sources( + TokenAuthConfigurationStep, + object_source=object_source, + ) + step = TokenAuthConfigurationStep() + step.execute(setup_config) + + self.assertTrue( + "Failed configuring token token-2" in str(command_error.exception) + ) + self.assertEqual(TokenAuth.objects.count(), 0) + def test_invalid_setup_contact_person(self): object_source = { "objecttypes_tokens_config_enable": True,