Skip to content

Commit

Permalink
Merge pull request #99 from maykinmedia/feature/django-setup-configur…
Browse files Browse the repository at this point in the history
…ation

✨ [#100] Add ConfigurationStep for Service model
  • Loading branch information
stevenbal authored Dec 2, 2024
2 parents bd7191c + 2e4516a commit ecfa9b2
Show file tree
Hide file tree
Showing 12 changed files with 280 additions and 0 deletions.
1 change: 1 addition & 0 deletions .readthedocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ python:
- docs
- db
- drf
- setup-configuration

sphinx:
configuration: docs/conf.py
6 changes: 6 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ drf = [
zds-client = [
"gemma-zds-client>=2.0.0",
]
setup-configuration = [
"django-setup-configuration>=0.4.0",
]
# These are not the test requirements! They are extras to be installed when making use of `zgw_consumers.test`
testutils = [
"Faker>=0.7.0",
Expand Down Expand Up @@ -99,6 +102,9 @@ skip = ["env", ".tox", ".history"]
[tool.pytest.ini_options]
testpaths = ["tests"]
DJANGO_SETTINGS_MODULE = "testapp.settings"
markers = [
"config_path: the path to the YAML file that is loaded for setup_configuration",
]

[tool.bumpversion]
current_version = "0.35.1"
Expand Down
1 change: 1 addition & 0 deletions testapp/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"simple_certmanager",
"solo",
"testapp",
"django_setup_configuration",
]

MIDDLEWARE = [
Expand Down
18 changes: 18 additions & 0 deletions tests/files/setup_config_services.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
zgw_consumers_config_enable: True
zgw_consumers:
services:
- identifier: objecten-test
label: Objecten API test
api_root: http://objecten.local/api/v1/
api_connection_check_path: objects
api_type: orc
auth_type: api_key
header_key: Authorization
header_value: Token foo
- identifier: zaken-test
label: Zaken API test
api_root: http://zaken.local/api/v1/
api_type: zrc
auth_type: zgw
client_id: client
secret: super-secret
20 changes: 20 additions & 0 deletions tests/files/setup_config_services_all_fields.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
zgw_consumers_config_enable: True
zgw_consumers:
services:
- identifier: objecten-test
label: Objecten API test
api_root: http://objecten.local/api/v1/
api_connection_check_path: objects
api_type: orc
auth_type: api_key
header_key: Authorization
header_value: Token foo
client_id: client
secret: super-secret
nlx: http://some-outway-adress.local:8080/
user_id: open-formulieren
user_representation: Open Formulieren
timeout: 5
# NOT SUPPORTED YET
# client_certificatie: ...
# server_certificatie: ...
8 changes: 8 additions & 0 deletions tests/files/setup_config_services_required_fields.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
zgw_consumers_config_enable: True
zgw_consumers:
services:
- identifier: objecten-test
label: Objecten API test
api_root: http://objecten.local/api/v1/
api_type: orc
auth_type: zgw
152 changes: 152 additions & 0 deletions tests/test_configuration_steps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import pytest
from django_setup_configuration.test_utils import execute_single_step

from zgw_consumers.constants import APITypes, AuthTypes
from zgw_consumers.contrib.setup_configuration.steps import ServiceConfigurationStep
from zgw_consumers.models import Service
from zgw_consumers.test.factories import ServiceFactory

CONFIG_FILE_PATH = "tests/files/setup_config_services.yaml"
CONFIG_FILE_PATH_REQUIRED_FIELDS = (
"tests/files/setup_config_services_required_fields.yaml"
)
CONFIG_FILE_PATH_ALL_FIELDS = "tests/files/setup_config_services_all_fields.yaml"


@pytest.mark.django_db
def test_execute_configuration_step_success():
execute_single_step(ServiceConfigurationStep, yaml_source=CONFIG_FILE_PATH)

assert Service.objects.count() == 2

objects_service, zaken_service = Service.objects.all()

assert objects_service.slug == "objecten-test"
assert objects_service.label == "Objecten API test"
assert objects_service.api_root == "http://objecten.local/api/v1/"
assert objects_service.api_type == APITypes.orc
assert objects_service.auth_type == AuthTypes.api_key
assert objects_service.header_key == "Authorization"
assert objects_service.header_value == "Token foo"
assert objects_service.timeout == 10

assert zaken_service.slug == "zaken-test"
assert zaken_service.label == "Zaken API test"
assert zaken_service.api_root == "http://zaken.local/api/v1/"
assert zaken_service.api_type == APITypes.zrc
assert zaken_service.auth_type == AuthTypes.zgw
assert zaken_service.client_id == "client"
assert zaken_service.secret == "super-secret"
assert zaken_service.timeout == 10


@pytest.mark.django_db
def test_execute_configuration_step_update_existing():
ServiceFactory.create(
slug="zaken-test",
label="Objecttypen",
api_root="http://some.existing.service.local/api/v1/",
)

execute_single_step(ServiceConfigurationStep, yaml_source=CONFIG_FILE_PATH)

assert Service.objects.count() == 2

objects_service, zaken_service = Service.objects.all()

assert objects_service.slug == "objecten-test"
assert objects_service.label == "Objecten API test"
assert objects_service.api_root == "http://objecten.local/api/v1/"

assert zaken_service.slug == "zaken-test"
assert zaken_service.label == "Zaken API test"
assert zaken_service.api_root == "http://zaken.local/api/v1/"


@pytest.mark.django_db
def test_execute_configuration_step_with_required_fields():
execute_single_step(
ServiceConfigurationStep, yaml_source=CONFIG_FILE_PATH_REQUIRED_FIELDS
)

assert Service.objects.count() == 1

objects_service = Service.objects.get()

assert objects_service.slug == "objecten-test"
assert objects_service.label == "Objecten API test"
assert objects_service.api_root == "http://objecten.local/api/v1/"
assert objects_service.api_type == APITypes.orc
assert objects_service.auth_type == AuthTypes.zgw
assert objects_service.timeout == 10

# Not required fields
assert objects_service.api_connection_check_path == ""
assert objects_service.header_key == ""
assert objects_service.header_value == ""
assert objects_service.client_id == ""
assert objects_service.secret == ""
assert objects_service.nlx == ""
assert objects_service.user_id == ""
assert objects_service.user_representation == ""


@pytest.mark.django_db
def test_execute_configuration_step_with_all_fields():
execute_single_step(
ServiceConfigurationStep, yaml_source=CONFIG_FILE_PATH_ALL_FIELDS
)

assert Service.objects.count() == 1

objects_service = Service.objects.get()

assert objects_service.slug == "objecten-test"
assert objects_service.label == "Objecten API test"
assert objects_service.api_root == "http://objecten.local/api/v1/"
assert objects_service.api_type == APITypes.orc
assert objects_service.auth_type == AuthTypes.api_key
assert objects_service.api_connection_check_path == "objects"
assert objects_service.header_key == "Authorization"
assert objects_service.header_value == "Token foo"
assert objects_service.client_id == "client"
assert objects_service.secret == "super-secret"
assert objects_service.nlx == "http://some-outway-adress.local:8080/"
assert objects_service.user_id == "open-formulieren"
assert objects_service.user_representation == "Open Formulieren"
assert objects_service.timeout == 5


@pytest.mark.django_db
def test_execute_configuration_step_idempotent():
def make_assertions():
assert Service.objects.count() == 1

objects_service = Service.objects.get()

assert objects_service.slug == "objecten-test"
assert objects_service.label == "Objecten API test"
assert objects_service.api_root == "http://objecten.local/api/v1/"
assert objects_service.api_type == APITypes.orc
assert objects_service.auth_type == AuthTypes.api_key
assert objects_service.api_connection_check_path == "objects"
assert objects_service.header_key == "Authorization"
assert objects_service.header_value == "Token foo"
assert objects_service.client_id == "client"
assert objects_service.secret == "super-secret"
assert objects_service.nlx == "http://some-outway-adress.local:8080/"
assert objects_service.user_id == "open-formulieren"
assert objects_service.user_representation == "Open Formulieren"
assert objects_service.timeout == 5

execute_single_step(
ServiceConfigurationStep, yaml_source=CONFIG_FILE_PATH_ALL_FIELDS
)

make_assertions()

execute_single_step(
ServiceConfigurationStep, yaml_source=CONFIG_FILE_PATH_ALL_FIELDS
)

make_assertions()
2 changes: 2 additions & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ extras =
testutils
tests
coverage
setup-configuration
deps =
django42: Django~=4.2.0
commands =
Expand Down Expand Up @@ -64,6 +65,7 @@ extras =
db
drf
docs
setup-configuration
commands=
pytest check_sphinx.py -v \
--tb=auto \
Expand Down
Empty file.
Empty file.
35 changes: 35 additions & 0 deletions zgw_consumers/contrib/setup_configuration/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from django_setup_configuration.models import ConfigurationModel, DjangoModelRef
from pydantic import Field

from zgw_consumers.models import Service


class SingleServiceConfigurationModel(ConfigurationModel):
# TODO these should probably be defined in simple_certmanager and referred to?
# client_certificate: FilePath = DjangoModelRef(Service, "client_certificate")
# server_certificate: FilePath = DjangoModelRef(Service, "server_certificate")
# Identifier is mapped to slug, because slug isn't a very descriptive name for devops
identifier: str = DjangoModelRef(Service, "slug")

class Meta:
django_model_refs = {
Service: [
"label",
"api_type",
"api_root",
"api_connection_check_path",
"auth_type",
"client_id",
"secret",
"header_key",
"header_value",
"nlx",
"user_id",
"user_representation",
"timeout",
]
}


class ServicesConfigurationModel(ConfigurationModel):
services: list[SingleServiceConfigurationModel] = Field(default_factory=list)
37 changes: 37 additions & 0 deletions zgw_consumers/contrib/setup_configuration/steps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from django_setup_configuration.configuration import BaseConfigurationStep

from zgw_consumers.models import Service

from .models import ServicesConfigurationModel


class ServiceConfigurationStep(BaseConfigurationStep[ServicesConfigurationModel]):
"""
Configure Services to connect with external APIs
"""

verbose_name = "Configuration to connect with external services"
config_model = ServicesConfigurationModel
namespace = "zgw_consumers"
enable_setting = "zgw_consumers_config_enable"

def execute(self, model: ServicesConfigurationModel):
for config in model.services:
Service.objects.update_or_create(
slug=config.identifier,
defaults={
"label": config.label,
"api_type": config.api_type,
"api_root": config.api_root,
"api_connection_check_path": config.api_connection_check_path,
"auth_type": config.auth_type,
"client_id": config.client_id,
"secret": config.secret,
"header_key": config.header_key,
"header_value": config.header_value,
"nlx": config.nlx,
"user_id": config.user_id,
"user_representation": config.user_representation,
"timeout": config.timeout,
},
)

0 comments on commit ecfa9b2

Please sign in to comment.