diff --git a/_type_stubs/awx/main/models/credential.pyi b/_type_stubs/awx/main/models/credential.pyi index 37685252..28177f9e 100644 --- a/_type_stubs/awx/main/models/credential.pyi +++ b/_type_stubs/awx/main/models/credential.pyi @@ -1,3 +1,11 @@ +from typing import Callable + +from awx_plugins.interfaces._temporary_private_credential_api import ( # noqa: WPS436 + Credential, + GenericOptionalPrimitiveType, +) + + class ManagedCredentialType: def __init__( self, @@ -7,4 +15,5 @@ class ManagedCredentialType: inputs: dict[str, list[dict[str, bool | str] | str]], injectors: dict[str, dict[str, str]] | None = None, managed: bool = False, + custom_injector: Callable[[Credential, dict[str, GenericOptionalPrimitiveType], str], str | None] | None = None, ): ... diff --git a/src/awx_plugins/interfaces/_temporary_private_api.py b/src/awx_plugins/interfaces/_temporary_private_api.py index c447ba48..058b9084 100644 --- a/src/awx_plugins/interfaces/_temporary_private_api.py +++ b/src/awx_plugins/interfaces/_temporary_private_api.py @@ -4,6 +4,13 @@ The hope is that it will be refactored into something more standardized. """ +from collections.abc import Callable + +from ._temporary_private_credential_api import ( # noqa: WPS436 + Credential as Credential, + GenericOptionalPrimitiveType, +) + try: # pylint: disable-next=unused-import @@ -35,5 +42,13 @@ class ManagedCredentialType: # type: ignore[no-redef] # noqa: WPS440 managed: bool = False """Flag for whether this plugin instance is managed.""" + custom_injectors: Callable[ + [ + Credential, + dict[str, GenericOptionalPrimitiveType], str, + ], str | None, + ] | None = None + """Function to call as an alternative to the templated injection.""" + __all__ = () # noqa: WPS410 diff --git a/src/awx_plugins/interfaces/_temporary_private_credential_api.py b/src/awx_plugins/interfaces/_temporary_private_credential_api.py new file mode 100644 index 00000000..3d27e94c --- /dev/null +++ b/src/awx_plugins/interfaces/_temporary_private_credential_api.py @@ -0,0 +1,51 @@ +"""Shared stubs from ``awx`` credential. + +The hope is that it will be refactored into something more standardized. +""" + +GenericOptionalPrimitiveType = bool | str | int | float | None # noqa: WPS465 +"""Generic type for input values.""" + + +class Credential: + """Input supplied by the user. + + Satisfies :class:`~._temporary_private_api.ManagedCredentialType` + inputs want(s). + """ + + def __init__( + self: 'Credential', + inputs: dict[str, GenericOptionalPrimitiveType] | None = None, + ) -> None: + self._inputs: dict[str, GenericOptionalPrimitiveType] = inputs or {} + + def get_input( + self: 'Credential', + field_name: str, + default: GenericOptionalPrimitiveType = None, + ) -> GenericOptionalPrimitiveType: + """Get the user supplied value for a given field. + + Given the name of a field, return the user supplied value. + + :param field_name: Input key to check if a value was supplied. + :param default: Value to return if a value was not supplied by + the user + :returns: True if user supplied a value, False otherwise. + """ + return self._inputs.get(field_name, default) + + def has_input(self: 'Credential', field_name: str) -> bool: + """Check if user supplied a value for a given field. + + Given the name of a field, return True of False as to if a value + was provided for that field. + + :param field_name: Input key to check if a value was supplied. + :returns: True if user supplied a value, False otherwise. + """ + return self._inputs.get(field_name, None) not in {'', None} + + +__all__ = () # noqa: WPS410 diff --git a/tests/_temporary_private_credential_api_test.py b/tests/_temporary_private_credential_api_test.py new file mode 100644 index 00000000..b9727e82 --- /dev/null +++ b/tests/_temporary_private_credential_api_test.py @@ -0,0 +1,55 @@ +"""Tests for the temporarily hosted private credential structure.""" + +import pytest + +from awx_plugins.interfaces._temporary_private_credential_api import ( + Credential, + GenericOptionalPrimitiveType, +) + + +def test_credential_instantiation() -> None: + """Check that credential type can be instantiated.""" + assert Credential() + + +@pytest.mark.parametrize( + ('inputs', 'key', 'expected'), + ( + pytest.param({'foo': 'bar'}, 'foo', 'bar', id='key-present'), + pytest.param({'foo1': 'bar1'}, 'baz', None, id='key-missing'), + ), +) +def test_credential_get_input( + inputs: dict[str, GenericOptionalPrimitiveType], + key: str, + expected: str, +) -> None: + """Check that get_input operates on the dict we provided.""" + assert Credential(inputs=inputs).get_input(key) == expected + + +@pytest.mark.parametrize( + ('inputs', 'key', 'expected'), + ( + pytest.param( + {'foo2': 'bar2'}, + 'foo2', + True, # noqa: WPS425 + id='key-present', + ), + pytest.param( + {'foo3': 'bar3'}, + 'baz', + False, # noqa: WPS425 + id='key-missing', + ), + ), +) +def test_credential_has_input( + inputs: dict[str, GenericOptionalPrimitiveType], + key: str, + expected: bool, +) -> None: + """Check that has_input behaves like dict in operator.""" + assert Credential(inputs=inputs).has_input(key) is expected