Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion bin/docker_start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ until pg_isready; do
done

>&2 echo "Database is up."

# Set defaults for OTEL
export OTEL_SERVICE_NAME="${OTEL_SERVICE_NAME:-openklant}"

Expand Down
55 changes: 55 additions & 0 deletions docker/docker-compose.referentielijsten.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
services:
referentielijsten-redis:
image: redis:7
command: ["redis-server", "--appendonly", "yes"]
networks:
- open-klant-dev

referentielijsten-db:
image: postgres:${PG_VERSION:-14}
environment:
- POSTGRES_HOST_AUTH_METHOD=trust
- POSTGRES_USER=referentielijsten
- POSTGRES_DB=referentielijsten
volumes:
- ./docker-init-referentielijsten-db/:/docker-entrypoint-initdb.d
- referentielijsten-db:/var/lib/postgresql/data
networks:
- open-klant-dev

referentielijsten-web.local:
image: maykinmedia/referentielijsten-api:${REFERENTIELIJSTEN_VERSION:-latest}
environment: &referentielijsten_web_env
- DJANGO_SETTINGS_MODULE=referentielijsten.conf.docker
- SECRET_KEY=${SECRET_KEY:-7&3f^bo1(-5($bre4iv-!nt%1xr!b54b&y7+97j5f&ndm_e=lz}
- ALLOWED_HOSTS=referentielijsten-web.local,localhost
- DB_NAME=referentielijsten
- DB_USER=referentielijsten
- DB_HOST=referentielijsten-db
- DISABLE_2FA=true
- IS_HTTPS=no
- CACHE_DEFAULT=referentielijsten-redis:6379/0
- CACHE_AXES=referentielijsten-redis:6379/0
- SUBPATH=${SUBPATH:-/}
- DJANGO_SUPERUSER_PASSWORD=admin
ports:
- 8004:8000
volumes: &referentielijsten_web_volumes
# mount fixtures dir to automatically populate the DB
- ./referentielijsten/fixtures/:/app/fixtures
- media:/app/media # Shared media volume to get access to saved OAS files
- private-media:/app/private-media
depends_on:
- referentielijsten-db
- referentielijsten-redis
networks:
- open-klant-dev

volumes:
referentielijsten-db:
media:
private-media:

networks:
open-klant-dev:
name: open-klant-dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
CREATE USER referentielijsten;
CREATE DATABASE referentielijsten;
GRANT ALL PRIVILEGES ON DATABASE referentielijsten TO referentielijsten;
-- On Postgres 15+, connect to the database and grant schema permissions.
-- GRANT USAGE, CREATE ON SCHEMA public TO referentielijsten;
57 changes: 57 additions & 0 deletions docker/referentielijsten/fixtures/referentielijsten_fixtures.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
[
{
"model": "api.tabel",
"pk": 1,
"fields": {
"code": "KANAAL",
"naam": "Communication Channels",
"einddatum_geldigheid": null,
"beheerder_naam": "Mary",
"beheerder_email": "[email protected]",
"beheerder_afdeling": "",
"beheerder_organisatie": ""
}
},
{
"model": "api.item",
"pk": 1,
"fields": {
"tabel": 1,
"code": "email",
"naam": "E-mail Communication",
"begindatum_geldigheid": "2025-10-25T15:09:23Z",
"einddatum_geldigheid": null,
"aanvullende_gegevens": null
}
},
{
"model": "api.item",
"pk": 2,
"fields": {
"tabel": 1,
"code": "phone",
"naam": "Telephone",
"begindatum_geldigheid": "2025-10-25T15:09:07Z",
"einddatum_geldigheid": "2029-10-25T15:09:08Z",
"aanvullende_gegevens": null
}
},
{
"model": "accounts.user",
"pk": 1,
"fields": {
"password": "pbkdf2_sha256$1000000$Kq3GN6W9xasS9fEsGvGtr9$RADFuuEv46hrLHnbc3YHH7TVnRqSe3esatMntnz5SMk=",
"last_login": null,
"is_superuser": true,
"username": "superuser",
"first_name": "",
"last_name": "",
"email": "[email protected]",
"is_staff": true,
"is_active": true,
"date_joined": "2025-11-04T16:38:29.504Z",
"groups": [],
"user_permissions": []
}
}
]
12 changes: 12 additions & 0 deletions docker/setup_configuration/data.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,21 @@ zgw_consumers:
user_id: open-klant
secret: open-klant-secret

- identifier: referentielijsten-api
label: Referentielijsten API
api_root: http://referentielijsten-web.local:8000/api/v1/
api_type: orc
auth_type: no_auth

notifications_config_enable: true
notifications_config:
notifications_api_service_identifier: open-notificaties
notification_delivery_max_retries: 2
notification_delivery_retry_backoff: 2
notification_delivery_retry_backoff_max: 3

referentielijsten_config_enable: true
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'll have to add ReferentielijstenConfigurationStep to the SETUP_CONFIGURATION_STEPS in base.py to make sure it is run when calling setup_configuration

referentielijsten_config:
enabled: true
referentielijsten_api_service_identifier: referentielijsten-api
kanalen_tabel_code: KANAAL
2 changes: 1 addition & 1 deletion migration/cassettes/test_digitaal_adres.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,4 @@ interactions:
status:
code: 200
message: OK
version: 1
version: 1
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
)
from openklant.components.klantinteracties.api.validators import (
FKUniqueTogetherValidator,
KanaalValidator,
betrokkene_exists,
bijlage_exists,
klantcontact_exists,
Expand Down Expand Up @@ -332,6 +333,9 @@ class Meta:
"lookup_field": "uuid",
"help_text": _("De unieke URL van dit klantcontact binnen deze API."),
},
"kanaal": {
"validators": [KanaalValidator()],
},
}

@extend_schema_field(ActorSerializer(many=True))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
from pathlib import Path
from unittest.mock import patch

from django.contrib.auth import get_user_model

from maykin_common.vcr import VCRTestCase
from rest_framework import status
from vng_api_common.tests import reverse
from zgw_consumers.test.factories import ServiceFactory

from openklant.components.token.tests.api_testcase import APITestCase
from openklant.config.models import ReferentielijstenConfig

User = get_user_model()


class KanaalValidatorAPITestCase(APITestCase, VCRTestCase):
service_slug = "referentielijsten-api"
VCR_TEST_FILES = Path("src/referentielijsten_client/tests")

@classmethod
def setUpTestData(cls):
super().setUpTestData()
cls.service = ServiceFactory(
slug=cls.service_slug,
api_root="http://localhost:8004/api/v1",
)

def setUp(self):
super().setUp()
self.initial_data = {
"nummer": "9999999999",
"kanaal": "email",
"onderwerp": "Initial contact for update tests",
"inhoud": "Test inhoud",
"indicatie_contact_gelukt": True,
"taal": "nl",
"vertrouwelijk": False,
"plaatsgevonden_op": "2025-11-06T12:00:00Z",
}

self.config = ReferentielijstenConfig(
service=self.service,
enabled=True,
kanalen_tabel_code="KANAAL",
)

def _create_initial_contact(self, mock_get_solo):
create_url = reverse(
"klantinteracties:klantcontact-list", kwargs={"version": "1"}
)
response = self.client.post(create_url, self.initial_data, format="json")

if response.status_code != status.HTTP_201_CREATED:
raise AssertionError(
f"Expected 201 when creating klantcontact, got {response.status_code}: {response.data}"
)

uuid = response.json().get("uuid")
if not uuid:
raise AssertionError(f"No 'uuid' found in response: {response.json()}")

self.detail_url = reverse(
"klantinteracties:klantcontact-detail",
kwargs={"version": "1", "uuid": uuid},
)

def _mock_config(self):
return self.config

@patch(
"openklant.components.klantinteracties.api.validators.ReferentielijstenConfig.get_solo"
)
def test_valid_kanaal(self, mock_get_solo):
mock_get_solo.return_value = self._mock_config()
url = reverse("klantinteracties:klantcontact-list", kwargs={"version": "1"})
data = self.initial_data.copy()
data["nummer"] = "0000000001"

response = self.client.post(url, data, format="json")

self.assertEqual(response.status_code, status.HTTP_201_CREATED)

@patch(
"openklant.components.klantinteracties.api.validators.ReferentielijstenConfig.get_solo"
)
def test_invalid_kanaal(self, mock_get_solo):
mock_get_solo.return_value = self._mock_config()
url = reverse("klantinteracties:klantcontact-list", kwargs={"version": "1"})
data = self.initial_data.copy()
data["nummer"] = "0000000002"
data["kanaal"] = "fax"

response = self.client.post(url, data, format="json")

self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertIn("not a valid kanaal", str(response.data))

@patch(
"openklant.components.klantinteracties.api.validators.ReferentielijstenConfig.get_solo"
)
def test_patch_with_valid_kanaal(self, mock_get_solo):
mock_get_solo.return_value = self._mock_config()
self._create_initial_contact(mock_get_solo)
data = {"kanaal": "phone"}

response = self.client.patch(self.detail_url, data, format="json")

self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.json()["kanaal"], "phone")

@patch(
"openklant.components.klantinteracties.api.validators.ReferentielijstenConfig.get_solo"
)
def test_patch_with_invalid_kanaal(self, mock_get_solo):
mock_get_solo.return_value = self._mock_config()
self._create_initial_contact(mock_get_solo)
data = {"kanaal": "telepathy"}

response = self.client.patch(self.detail_url, data, format="json")

self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

self.assertIn("is not a valid kanaal", str(response.data))

error_fields = [param["name"] for param in response.data["invalid_params"]]
self.assertIn("kanaal", error_fields)

@patch(
"openklant.components.klantinteracties.api.validators.ReferentielijstenConfig.get_solo"
)
def test_put_with_valid_kanaal(self, mock_get_solo):
mock_get_solo.return_value = self._mock_config()
self._create_initial_contact(mock_get_solo)
data = self.initial_data.copy()
data["kanaal"] = "phone"
data["plaatsgevonden_op"] = "2025-11-06T13:00:00Z"

response = self.client.put(self.detail_url, data, format="json")

self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.json()["kanaal"], "phone")

@patch(
"openklant.components.klantinteracties.api.validators.ReferentielijstenConfig.get_solo"
)
def test_put_with_invalid_kanaal(self, mock_get_solo):
mock_get_solo.return_value = self._mock_config()
self._create_initial_contact(mock_get_solo)
data = self.initial_data.copy()
data["kanaal"] = "carrier-pigeon"
data["plaatsgevonden_op"] = "2025-11-06T13:00:00Z"

response = self.client.put(self.detail_url, data, format="json")

self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)

self.assertIn("is not a valid kanaal", str(response.data))

error_fields = [
param["name"] for param in response.data.get("invalid_params", [])
]
self.assertIn("kanaal", error_fields)
Loading
Loading