Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix type annotations for prefill (plugin) signatures #4470

Merged
merged 2 commits into from
Jul 1, 2024
Merged
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
4 changes: 3 additions & 1 deletion src/openforms/config/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@

from django.contrib.admin.templatetags.admin_list import _boolean_icon

Action: TypeAlias = tuple[str, str]
from openforms.typing import StrOrPromise

Action: TypeAlias = tuple[StrOrPromise, str] # (label, reversed URL path)


@dataclass
Expand Down
5 changes: 4 additions & 1 deletion src/openforms/plugins/plugin.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
from collections.abc import Sequence

from django.utils.translation import gettext_lazy as _

from openforms.config.data import Action
from openforms.config.models import GlobalConfiguration

from .registry import BaseRegistry
Expand Down Expand Up @@ -43,7 +46,7 @@ def check_config(self):
"""
raise NotImplementedError()

def get_config_actions(self) -> list[tuple[str, str]]:
def get_config_actions(self) -> Sequence[Action]:
sergei-maertens marked this conversation as resolved.
Show resolved Hide resolved
"""
Returns a list of tuples containing the label and URL of each action
that is related to the configuration of this plugin. This can be to
Expand Down
14 changes: 9 additions & 5 deletions src/openforms/prefill/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,18 @@ class BasePlugin(AbstractBasePlugin):
requires_auth: AuthAttribute | None = None
for_components: Container[str] = AllComponentTypes()

def get_available_attributes(self) -> Iterable[tuple[str, str]]:
@staticmethod
def get_available_attributes() -> Iterable[tuple[str, str]]:
"""
Return a choice list of available attributes this plugin offers.
"""
raise NotImplementedError(
"You must implement the 'get_available_attributes' method."
)

@classmethod
def get_prefill_values(
self,
cls,
submission: Submission,
attributes: list[str],
identifier_role: IdentifierRoles = IdentifierRoles.main,
Expand All @@ -48,8 +50,9 @@ def get_prefill_values(
"""
raise NotImplementedError("You must implement the 'get_prefill_values' method.")

@classmethod
def get_co_sign_values(
self, submission: Submission, identifier: str
cls, submission: Submission, identifier: str
) -> tuple[dict[str, Any], str]:
"""
Given an identifier, fetch the co-sign specific values.
Expand All @@ -64,8 +67,9 @@ def get_co_sign_values(
"""
raise NotImplementedError("You must implement the 'get_co_sign_values' method.")

@classmethod
def get_identifier_value(
self, submission: Submission, identifier_role: IdentifierRoles
cls, submission: Submission, identifier_role: IdentifierRoles
) -> str | None:
"""
Given a submission and the role of the identifier, return the value of the identifier.
Expand All @@ -82,6 +86,6 @@ def get_identifier_value(

if (
identifier_role == IdentifierRoles.main
and submission.auth_info.attribute == self.requires_auth
and submission.auth_info.attribute == cls.requires_auth
):
return submission.auth_info.value
17 changes: 11 additions & 6 deletions src/openforms/prefill/contrib/demo/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,17 @@
from django.utils.crypto import get_random_string
from django.utils.translation import gettext_lazy as _

from openforms.config.data import Action
from openforms.submissions.models import Submission

from ...base import BasePlugin
from ...constants import IdentifierRoles
from ...registry import register
from .constants import Attributes

CALLBACKS = {
Attributes.random_number: lambda: random.randint(1000, 10_000),
Attributes.random_string: partial(get_random_string, length=10),
Attributes.random_number.value: lambda: random.randint(1000, 10_000),
Attributes.random_string.value: partial(get_random_string, length=10),
}


Expand All @@ -26,9 +28,12 @@
def get_available_attributes():
return Attributes.choices

@staticmethod
@classmethod
def get_prefill_values(
submission: Submission, attributes: list[str], identifier_role: str
cls,
submission: Submission,
attributes: list[str],
identifier_role: IdentifierRoles = IdentifierRoles.main,
) -> dict[str, Any]:
"""
Given the requested attributes, look up the appropriate values and return them.
Expand All @@ -40,8 +45,8 @@
"""
return {attr: CALLBACKS[attr]() for attr in attributes}

def check_config(self):
def check_config(self) -> list[Action]:
"""
Demo config is always valid.
"""
pass
return []

Check warning on line 52 in src/openforms/prefill/contrib/demo/plugin.py

View check run for this annotation

Codecov / codecov/patch

src/openforms/prefill/contrib/demo/plugin.py#L52

Added line #L52 was not covered by tests
10 changes: 3 additions & 7 deletions src/openforms/prefill/contrib/haalcentraal_brp/plugin.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import logging
from collections.abc import Sequence
from typing import Any, TypeAlias
from typing import Any

from django.urls import reverse
from django.utils.translation import gettext_lazy as _
Expand Down Expand Up @@ -32,9 +31,6 @@
}


AttributesSequence: TypeAlias = Sequence[AttributesV1 | AttributesV2]


def get_attributes_cls():
config = HaalCentraalConfig.get_solo()
assert isinstance(config, HaalCentraalConfig)
Expand Down Expand Up @@ -63,7 +59,7 @@ def _get_values_for_bsn(
cls,
client: BRPClient,
bsn: str,
attributes: AttributesSequence,
attributes: list[str],
) -> dict[str, Any]:
if not (data := client.find_person(bsn, attributes=attributes)):
return {}
Expand Down Expand Up @@ -101,7 +97,7 @@ def get_identifier_value(
def get_prefill_values(
cls,
submission: Submission,
attributes: AttributesSequence,
attributes: list[str],
identifier_role: IdentifierRoles = IdentifierRoles.main,
) -> dict[str, Any]:
try:
Expand Down
19 changes: 11 additions & 8 deletions src/openforms/prefill/contrib/kvk/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,29 +37,32 @@ class KVK_KVKNumberPrefill(BasePlugin):

requires_auth = AuthAttribute.kvk

def get_available_attributes(self) -> list[tuple[str, str]]:
@staticmethod
def get_available_attributes() -> list[tuple[str, str]]:
return Attributes.choices

@classmethod
def get_identifier_value(
self, submission: Submission, identifier_role: str
cls, submission: Submission, identifier_role: str
) -> str | None:
if not submission.is_authenticated:
return

if (
identifier_role == IdentifierRoles.main
and submission.auth_info.attribute == self.requires_auth
and submission.auth_info.attribute == cls.requires_auth
):
return submission.auth_info.value

@classmethod
def get_prefill_values(
self,
cls,
submission: Submission,
attributes: list[str],
identifier_role: str = IdentifierRoles.main,
) -> dict[str, Any]:
# check if submission was logged in with the identifier we're interested
if not (kvk_value := self.get_identifier_value(submission, identifier_role)):
if not (kvk_value := cls.get_identifier_value(submission, identifier_role)):
return {}

try:
Expand All @@ -68,7 +71,7 @@ def get_prefill_values(
except (RequestException, NoServiceConfigured):
return {}

self.modify_result(result)
cls.modify_result(result)

values = dict()
for attr in attributes:
Expand All @@ -80,8 +83,8 @@ def get_prefill_values(
)
return values

@classmethod
def modify_result(cls, result: BasisProfiel):
@staticmethod
def modify_result(result: BasisProfiel):
# first try getting the addresses from the embedded 'hoofdvestiging'. Note that
# this may be absent or empty depending on the type of company (see #1299).
# If there are no addresses found, we try to get them from 'eigenaar' instead.
Expand Down
29 changes: 16 additions & 13 deletions src/openforms/prefill/contrib/stufbg/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,12 @@ class StufBgPrefill(BasePlugin):
verbose_name = _("StUF-BG")
requires_auth = AuthAttribute.bsn

def get_available_attributes(self) -> list[tuple[str, str]]:
@staticmethod
def get_available_attributes() -> list[tuple[str, str]]:
return FieldChoices.choices

def _get_values_for_bsn(
self, bsn: str, attributes: list[FieldChoices]
) -> dict[str, Any]:
@staticmethod
def _get_values_for_bsn(bsn: str, attributes: list[FieldChoices]) -> dict[str, Any]:
with get_client() as client:
data = client.get_values(bsn, [str(attr) for attr in attributes])

Expand All @@ -129,36 +129,39 @@ def _get_values_for_bsn(

return response_dict

@classmethod
def get_identifier_value(
self, submission: Submission, identifier_role: str
cls, submission: Submission, identifier_role: IdentifierRoles
) -> str | None:
if not submission.is_authenticated:
return

if (
identifier_role == IdentifierRoles.main
and submission.auth_info.attribute == self.requires_auth
and submission.auth_info.attribute == cls.requires_auth
):
return submission.auth_info.value

if identifier_role == IdentifierRoles.authorised_person:
return submission.auth_info.machtigen.get("identifier_value")

@classmethod
def get_prefill_values(
self,
cls,
submission: Submission,
attributes: list[FieldChoices],
identifier_role: str = IdentifierRoles.main,
attributes: list[str],
identifier_role: IdentifierRoles = IdentifierRoles.main,
) -> dict[str, Any]:
if not (bsn_value := self.get_identifier_value(submission, identifier_role)):
if not (bsn_value := cls.get_identifier_value(submission, identifier_role)):
# If there is no bsn we can't prefill any values so just return
logger.info("No BSN associated with submission, cannot prefill.")
return {}

return self._get_values_for_bsn(bsn_value, attributes)
return cls._get_values_for_bsn(bsn_value, attributes)

@classmethod
def get_co_sign_values(
self, submission: Submission, identifier: str
cls, submission: Submission, identifier: str
) -> tuple[dict[str, Any], str]:
"""
Given an identifier, fetch the co-sign specific values.
Expand All @@ -171,7 +174,7 @@ def get_co_sign_values(
:return: a key-value dictionary, where the key is the requested attribute and
the value is the prefill value to use for that attribute.
"""
values = self._get_values_for_bsn(
values = cls._get_values_for_bsn(
identifier,
[
FieldChoices.voornamen,
Expand Down
13 changes: 7 additions & 6 deletions src/openforms/prefill/contrib/suwinet/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from openforms.authentication.service import AuthAttribute
from openforms.plugins.exceptions import InvalidPluginConfiguration
from openforms.submissions.models import Submission
from openforms.typing import JSONSerializable
from openforms.typing import JSONEncodable, JSONObject
from suwinet.client import NoServiceConfigured, SuwinetClient, get_client
from suwinet.constants import SERVICES
from suwinet.models import SuwinetConfig
Expand Down Expand Up @@ -43,19 +43,20 @@ def get_available_attributes():
for operation in SERVICES[service_name].operations
]

@classmethod
def get_prefill_values(
self,
cls,
submission: Submission,
attributes: list[str],
identifier_role: str = IdentifierRoles.main,
) -> dict[str, JSONSerializable]:
identifier_role: IdentifierRoles = IdentifierRoles.main,
) -> dict[str, JSONEncodable]:
if not (
(client := _get_client())
and (bsn := self.get_identifier_value(submission, identifier_role))
and (bsn := cls.get_identifier_value(submission, identifier_role))
):
return {}

def get_value(attr) -> dict | None:
def get_value(attr: str) -> JSONObject | None:
service_name, operation = attr.split(".")
service = getattr(client, service_name)
perform_soap_call = getattr(service, operation)
Expand Down
Loading