Skip to content

Commit a6fe1fb

Browse files
authored
feat(credentials): add referenced credential element management (#351)
1 parent 48d03e7 commit a6fe1fb

8 files changed

Lines changed: 232 additions & 0 deletions

File tree

‎pyoaev/contracts/contract_config.py‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from pyoaev import utils
88
from pyoaev.contracts.contract_utils import ContractCardinality, ContractVariable
99
from pyoaev.contracts.variable_helper import VariableHelper
10+
from pyoaev.credential.types import CredentialType
1011

1112

1213
class SupportedLanguage(str, Enum):
@@ -31,11 +32,13 @@ class ContractFieldType(str, Enum):
3132
AssetGroup: str = "asset-group"
3233
AiTarget: str = "ai-target"
3334
Payload: str = "payload"
35+
CredentialReference: str = "credential-reference"
3436

3537

3638
class ContractFieldKey(str, Enum):
3739
Asset: str = "assets"
3840
AssetGroup: str = "asset_groups"
41+
CredentialReference: str = "credential_reference"
3942

4043

4144
class ContractOutputType(str, Enum):
@@ -365,6 +368,19 @@ def get_type(self) -> str:
365368
return ContractFieldType.Attachment.value
366369

367370

371+
@dataclass
372+
class ContractReferencedCredential(ContractElement):
373+
key: str = field(default=ContractFieldKey.CredentialReference.value, init=False)
374+
label: str = "Select a credential reference"
375+
mandatory: bool = False
376+
credential_reference_type: Optional[CredentialType] = None
377+
multiple: bool = True
378+
379+
@property
380+
def get_type(self) -> str:
381+
return ContractFieldType.CredentialReference.value
382+
383+
368384
@dataclass
369385
class ContractExpectations(ContractCardinalityElement):
370386
cardinality: str = ContractCardinality.Multiple

‎pyoaev/credential/__init__.py‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from .types import CredentialType
2+
3+
4+
def build_single_referenced_credential_element(provider_name: str):
5+
from .utils import (
6+
build_single_referenced_credential_element as _build_single_referenced_credential_element,
7+
)
8+
9+
return _build_single_referenced_credential_element(provider_name)
10+
11+
12+
__all__ = [
13+
"CredentialType",
14+
"build_single_referenced_credential_element",
15+
]

‎pyoaev/credential/types.py‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
"""Credential reference types shared with the OpenAEV platform.
2+
3+
This enum is the Python mirror of the OpenAEV backend credential-type enum
4+
used by credential secret references. Values must stay label-for-label in sync
5+
with the platform so contracts can declare credential references without any
6+
translation layer.
7+
"""
8+
9+
from enum import Enum
10+
11+
12+
class CredentialType(str, Enum):
13+
IDENTITY = "IDENTITY"
14+
CLOUD_AWS = "CLOUD_AWS"
15+
CLOUD_AZURE = "CLOUD_AZURE"
16+
CLOUD_GCP = "CLOUD_GCP"

‎pyoaev/credential/utils.py‎

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
"""Helpers centralizing how injectors declare credential-reference fields.
2+
3+
Injectors can call ``build_single_referenced_credential_element`` instead of
4+
re-implementing provider-to-credential-type mapping in each project. This keeps
5+
contract generation consistent across the Python ecosystem and aligns the
6+
serialized contract payload with the OpenAEV platform's expected values.
7+
"""
8+
9+
from typing import Optional
10+
11+
from pyoaev.contracts.contract_config import ContractReferencedCredential
12+
from pyoaev.credential.types import CredentialType
13+
14+
_PROVIDER_TO_CREDENTIAL_TYPE = {
15+
"aws": CredentialType.CLOUD_AWS,
16+
"eks": CredentialType.CLOUD_AWS,
17+
"azure": CredentialType.CLOUD_AZURE,
18+
"gcp": CredentialType.CLOUD_GCP,
19+
}
20+
21+
22+
def _resolve_credential_type(provider_name: str) -> Optional[CredentialType]:
23+
normalized_provider = provider_name.casefold()
24+
return _PROVIDER_TO_CREDENTIAL_TYPE.get(normalized_provider)
25+
26+
27+
def build_single_referenced_credential_element(
28+
provider_name: str,
29+
) -> ContractReferencedCredential:
30+
"""Build a credential-reference field for a provider-specific contract
31+
with ``multiple`` value at ``False``.
32+
33+
This is the centralized entry point injectors should use when they need the
34+
OpenAEV inject form to ask for one referenced credential. The helper keeps
35+
provider-to-``CredentialType`` mapping consistent across injector projects.
36+
"""
37+
38+
return ContractReferencedCredential(
39+
credential_reference_type=_resolve_credential_type(provider_name),
40+
multiple=False,
41+
)
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import json
2+
import unittest
3+
4+
from pyoaev import utils
5+
from pyoaev.contracts.contract_config import (
6+
ContractFieldKey,
7+
ContractFieldType,
8+
ContractReferencedCredential,
9+
)
10+
from pyoaev.credential.types import CredentialType
11+
12+
13+
def _serialize(field):
14+
return json.loads(json.dumps(field, cls=utils.EnhancedJSONEncoder))
15+
16+
17+
class ContractReferencedCredentialTest(unittest.TestCase):
18+
def test_defaults_match_platform_contract_expectations(self):
19+
field = ContractReferencedCredential()
20+
serialized = _serialize(field)
21+
22+
self.assertEqual(field.key, ContractFieldKey.CredentialReference.value)
23+
self.assertEqual(field.label, "Select a credential reference")
24+
self.assertFalse(field.mandatory)
25+
self.assertTrue(field.multiple)
26+
self.assertIsNone(field.credential_reference_type)
27+
self.assertEqual(field.type, ContractFieldType.CredentialReference.value)
28+
self.assertEqual(serialized["key"], "credential_reference")
29+
self.assertEqual(serialized["type"], "credential-reference")
30+
self.assertEqual(serialized["label"], "Select a credential reference")
31+
self.assertFalse(serialized["mandatory"])
32+
self.assertTrue(serialized["multiple"])
33+
self.assertIsNone(serialized["credential_reference_type"])
34+
35+
def test_explicit_credential_type_serializes_to_platform_label(self):
36+
field = ContractReferencedCredential(
37+
credential_reference_type=CredentialType.CLOUD_AZURE
38+
)
39+
40+
self.assertEqual(field.credential_reference_type, CredentialType.CLOUD_AZURE)
41+
self.assertEqual(_serialize(field)["credential_reference_type"], "CLOUD_AZURE")
42+
43+
def test_identity_credential_type_serializes_to_platform_label(self):
44+
field = ContractReferencedCredential(
45+
credential_reference_type=CredentialType.IDENTITY
46+
)
47+
48+
self.assertEqual(field.credential_reference_type, CredentialType.IDENTITY)
49+
self.assertEqual(_serialize(field)["credential_reference_type"], "IDENTITY")
50+
51+
52+
if __name__ == "__main__":
53+
unittest.main()

‎test/credential/__init__.py‎

Whitespace-only changes.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import unittest
2+
3+
from pyoaev.credential import CredentialType as PublicCredentialType
4+
from pyoaev.credential.types import CredentialType
5+
6+
7+
class CredentialTypeTest(unittest.TestCase):
8+
def test_every_value_matches_the_supported_platform_labels(self):
9+
expected_labels = {
10+
"IDENTITY",
11+
"CLOUD_AWS",
12+
"CLOUD_AZURE",
13+
"CLOUD_GCP",
14+
}
15+
actual_labels = {member.value for member in CredentialType}
16+
self.assertEqual(actual_labels, expected_labels)
17+
18+
def test_identity_wire_label(self):
19+
self.assertEqual(CredentialType.IDENTITY.value, "IDENTITY")
20+
self.assertEqual(CredentialType.IDENTITY, "IDENTITY")
21+
22+
def test_package_re_exports_credential_type(self):
23+
self.assertIs(PublicCredentialType, CredentialType)
24+
25+
def test_aws_wire_label(self):
26+
self.assertEqual(CredentialType.CLOUD_AWS.value, "CLOUD_AWS")
27+
self.assertEqual(CredentialType.CLOUD_AWS, "CLOUD_AWS")
28+
29+
30+
if __name__ == "__main__":
31+
unittest.main()
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import json
2+
import unittest
3+
4+
from pyoaev import utils
5+
from pyoaev.contracts.contract_config import ContractReferencedCredential
6+
from pyoaev.credential import (
7+
build_single_referenced_credential_element as public_build_single_referenced_credential_element,
8+
)
9+
from pyoaev.credential.types import CredentialType
10+
from pyoaev.credential.utils import build_single_referenced_credential_element
11+
12+
13+
def _serialize(field):
14+
return json.loads(json.dumps(field, cls=utils.EnhancedJSONEncoder))
15+
16+
17+
class CredentialUtilsTest(unittest.TestCase):
18+
def test_aws_provider_maps_to_aws_credential_reference(self):
19+
field = build_single_referenced_credential_element("aws")
20+
21+
self.assertIsInstance(field, ContractReferencedCredential)
22+
self.assertEqual(field.credential_reference_type, CredentialType.CLOUD_AWS)
23+
24+
def test_eks_provider_maps_to_aws_credential_reference(self):
25+
field = build_single_referenced_credential_element("eks")
26+
27+
self.assertEqual(field.credential_reference_type, CredentialType.CLOUD_AWS)
28+
29+
def test_azure_provider_maps_to_azure_credential_reference(self):
30+
field = build_single_referenced_credential_element("azure")
31+
32+
self.assertEqual(field.credential_reference_type, CredentialType.CLOUD_AZURE)
33+
34+
def test_gcp_provider_maps_to_gcp_credential_reference(self):
35+
field = build_single_referenced_credential_element("gcp")
36+
37+
self.assertEqual(field.credential_reference_type, CredentialType.CLOUD_GCP)
38+
39+
def test_provider_mapping_is_case_insensitive(self):
40+
field = build_single_referenced_credential_element("AWS")
41+
42+
self.assertEqual(field.credential_reference_type, CredentialType.CLOUD_AWS)
43+
44+
def test_package_public_helper_builds_the_same_field(self):
45+
field = public_build_single_referenced_credential_element("azure")
46+
47+
self.assertIsInstance(field, ContractReferencedCredential)
48+
self.assertEqual(field.credential_reference_type, CredentialType.CLOUD_AZURE)
49+
self.assertEqual(_serialize(field)["credential_reference_type"], "CLOUD_AZURE")
50+
self.assertFalse(field.multiple)
51+
52+
def test_unknown_provider_leaves_credential_type_empty(self):
53+
field = build_single_referenced_credential_element("openstack")
54+
55+
self.assertIsNone(field.credential_reference_type)
56+
self.assertIsNone(_serialize(field)["credential_reference_type"])
57+
58+
59+
if __name__ == "__main__":
60+
unittest.main()

0 commit comments

Comments
 (0)