Skip to content

Commit b598b87

Browse files
authored
Merge pull request #170 from negz/creds
Fix get_credentials, and move it to request
2 parents c865e8c + a4b0653 commit b598b87

File tree

4 files changed

+95
-64
lines changed

4 files changed

+95
-64
lines changed

crossplane/function/request.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,20 @@
1414

1515
"""Utilities for working with RunFunctionRequests."""
1616

17+
import dataclasses
18+
1719
import crossplane.function.proto.v1.run_function_pb2 as fnv1
1820
from crossplane.function import resource
1921

2022

23+
@dataclasses.dataclass
24+
class Credentials:
25+
"""Credentials."""
26+
27+
type: str
28+
data: dict
29+
30+
2131
def get_required_resources(req: fnv1.RunFunctionRequest, name: str) -> list[dict]:
2232
"""Get required resources by name from the request.
2333
@@ -73,3 +83,36 @@ def get_required_resource(req: fnv1.RunFunctionRequest, name: str) -> dict | Non
7383
"""
7484
resources = get_required_resources(req, name)
7585
return resources[0] if resources else None
86+
87+
88+
def get_credentials(req: fnv1.RunFunctionRequest, name: str) -> Credentials:
89+
"""Get the supplied credentials from the request.
90+
91+
Args:
92+
req: The RunFunctionRequest containing credentials.
93+
name: The name of the credentials to get.
94+
95+
Returns:
96+
The requested credentials with type and data.
97+
98+
If the credentials don't exist, returns empty credentials with type "data"
99+
and empty data dictionary.
100+
"""
101+
empty = Credentials(type="data", data={})
102+
103+
if not req or name not in req.credentials:
104+
return empty
105+
106+
cred = req.credentials[name]
107+
108+
# Use WhichOneof to determine which field in the oneof is set
109+
source_type = cred.WhichOneof("source")
110+
if source_type == "credential_data":
111+
# Convert bytes data to string data for backward compatibility
112+
data = {}
113+
for key, value in cred.credential_data.data.items():
114+
data[key] = value.decode("utf-8")
115+
return Credentials(type="credential_data", data=data)
116+
117+
# If no recognized source type is set, return empty
118+
return empty

crossplane/function/resource.py

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -140,24 +140,3 @@ def get_condition(resource: structpb.Struct, typ: str) -> Condition:
140140
return condition
141141

142142
return unknown
143-
144-
145-
@dataclasses.dataclass
146-
class Credentials:
147-
"""Credentials."""
148-
149-
type: str
150-
data: dict
151-
152-
153-
def get_credentials(req: structpb.Struct, name: str) -> Credentials:
154-
"""Get the supplied credentials."""
155-
empty = Credentials(type="data", data={})
156-
if not req or "credentials" not in req:
157-
return empty
158-
if not req["credentials"] or name not in req["credentials"]:
159-
return empty
160-
return Credentials(
161-
type=req["credentials"][name]["type"],
162-
data=struct_to_dict(req["credentials"][name]["data"]),
163-
)

tests/test_request.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,58 @@ class TestCase:
212212
got = request.get_required_resource(case.req, case.name)
213213
self.assertEqual(case.want, got, case.reason)
214214

215+
def test_get_credentials(self) -> None:
216+
@dataclasses.dataclass
217+
class TestCase:
218+
reason: str
219+
req: fnv1.RunFunctionRequest
220+
name: str
221+
want: request.Credentials
222+
223+
cases = [
224+
TestCase(
225+
reason="Should return empty credentials when no credentials exist.",
226+
req=fnv1.RunFunctionRequest(),
227+
name="test",
228+
want=request.Credentials(type="data", data={}),
229+
),
230+
TestCase(
231+
reason="Should return empty credentials when specified name not found.",
232+
req=fnv1.RunFunctionRequest(
233+
credentials={
234+
"other-cred": fnv1.Credentials(
235+
credential_data=fnv1.CredentialData(data={"key": b"value"})
236+
)
237+
}
238+
),
239+
name="test",
240+
want=request.Credentials(type="data", data={}),
241+
),
242+
TestCase(
243+
reason="Should return credentials when they exist.",
244+
req=fnv1.RunFunctionRequest(
245+
credentials={
246+
"test": fnv1.Credentials(
247+
credential_data=fnv1.CredentialData(
248+
data={"username": b"admin", "password": b"secret"}
249+
)
250+
)
251+
}
252+
),
253+
name="test",
254+
want=request.Credentials(
255+
type="credential_data",
256+
data={"username": "admin", "password": "secret"},
257+
),
258+
),
259+
]
260+
261+
for case in cases:
262+
got = request.get_credentials(case.req, case.name)
263+
self.assertEqual(
264+
dataclasses.asdict(case.want), dataclasses.asdict(got), case.reason
265+
)
266+
215267

216268
if __name__ == "__main__":
217269
unittest.main()

tests/test_resource.py

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -205,49 +205,6 @@ class TestCase:
205205
dataclasses.asdict(case.want), dataclasses.asdict(got), "-want, +got"
206206
)
207207

208-
def test_get_credentials(self) -> None:
209-
@dataclasses.dataclass
210-
class TestCase:
211-
reason: str
212-
req: structpb.Struct
213-
name: str
214-
want: resource.Credentials
215-
216-
cases = [
217-
TestCase(
218-
reason="Return the specified credentials if they exist.",
219-
req=resource.dict_to_struct(
220-
{"credentials": {"test": {"type": "data", "data": {"foo": "bar"}}}}
221-
),
222-
name="test",
223-
want=resource.Credentials(type="data", data={"foo": "bar"}),
224-
),
225-
TestCase(
226-
reason="Return empty credentials if no credentials section exists.",
227-
req=resource.dict_to_struct({}),
228-
name="test",
229-
want=resource.Credentials(type="data", data={}),
230-
),
231-
TestCase(
232-
reason="Return empty credentials if the specified name does not exist.",
233-
req=resource.dict_to_struct(
234-
{
235-
"credentials": {
236-
"nottest": {"type": "data", "data": {"foo": "bar"}}
237-
}
238-
}
239-
),
240-
name="test",
241-
want=resource.Credentials(type="data", data={}),
242-
),
243-
]
244-
245-
for case in cases:
246-
got = resource.get_credentials(case.req, case.name)
247-
self.assertEqual(
248-
dataclasses.asdict(case.want), dataclasses.asdict(got), "-want, +got"
249-
)
250-
251208
def test_dict_to_struct(self) -> None:
252209
@dataclasses.dataclass
253210
class TestCase:

0 commit comments

Comments
 (0)