Skip to content

Commit

Permalink
add generic secret block
Browse files Browse the repository at this point in the history
  • Loading branch information
zzstoatzz committed Aug 16, 2024
1 parent 51ffd99 commit 005281c
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 66 deletions.
8 changes: 4 additions & 4 deletions src/prefect/blocks/system.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from typing import Any, Dict, Generic, TypeVar, Union
from typing import Any, Dict, Generic, List, TypeVar, Union

from pydantic import (
Field,
SecretStr,
StrictFloat,
StrictInt,
StrictStr,
Expand All @@ -12,7 +13,7 @@
from prefect._internal.compatibility.deprecated import deprecated_class
from prefect.blocks.core import Block

SecretValueType = Union[StrictStr, StrictInt, StrictFloat, Dict[str, Any]]
SecretValueType = Union[StrictStr, SecretStr, StrictInt, StrictFloat, Dict, List]

T = TypeVar("T", bound=SecretValueType)

Expand Down Expand Up @@ -127,8 +128,7 @@ class Secret(Block, Generic[T]):
value: PydanticSecret[T] = Field(
default=...,
description="A value that should be kept secret.",
examples=["sk-1234567890", '{"username": "johndoe", "password": "s3cr3t"}'],
# json_schema_extra=dict(format="password"),
examples=["sk-1234567890", {"username": "johndoe", "password": "s3cr3t"}],
)

def get(self):
Expand Down
1 change: 1 addition & 0 deletions src/prefect/client/orchestration.py
Original file line number Diff line number Diff line change
Expand Up @@ -1278,6 +1278,7 @@ async def create_block_document(
exclude_unset=True,
exclude={"id", "block_schema", "block_type"},
context={"include_secrets": include_secrets},
serialize_as_any=True,
)
try:
response = await self._client.post(
Expand Down
81 changes: 19 additions & 62 deletions tests/blocks/test_system.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
from datetime import date
from uuid import UUID

import pendulum
import pytest
from pydantic import Secret as PydanticSecret
from pydantic import SecretStr
from pydantic_extra_types.pendulum_dt import DateTime as PydanticDateTime

from prefect.blocks.system import DateTime, Secret, SecretMap
from prefect.blocks.system import DateTime, Secret


def test_datetime():
Expand All @@ -18,69 +15,29 @@ def test_datetime():

@pytest.mark.parametrize(
"value",
["test", SecretStr("test")],
ids=["string", "secret_str"],
["test", {"key": "value"}, ["test"]],
ids=["string", "dict", "list"],
)
def test_secret_block(value):
Secret(value=value).save(name="test")
api_block = Secret.load("test")
assert isinstance(api_block.value, SecretStr)

assert api_block.get() == "test"


class TestSecretMap:
@pytest.mark.parametrize(
"key, value",
[
# (1, "int_secret"),
(3.14, "float_secret"),
("string_key", "string_secret"),
((1, "tuple"), "tuple_secret"),
],
)
def test_secret_map_with_various_hashable_types(self, key, value):
secret_map = SecretMap(value={key: value})

secret_map.save(name="test")

assert secret_map.get()[key] == value

assert isinstance(secret_map.value[key], PydanticSecret)

assert secret_map.value[key].get_secret_value() == value
assert isinstance(api_block.value, PydanticSecret)

breakpoint()
assert api_block.get() == value

assert SecretMap.load("test") == secret_map, secret_map.value

def test_secret_map_with_multiple_pairs(self):
secret_map = SecretMap(
value={
"string_key": "string_secret",
42: "int_secret",
(1, "tuple"): "tuple_secret",
date(2023, 8, 14): "date_secret",
UUID("12345678-1234-5678-1234-567812345678"): "uuid_secret",
}
)

assert secret_map.get() == {
"string_key": "string_secret",
42: "int_secret",
(1, "tuple"): "tuple_secret",
date(2023, 8, 14): "date_secret",
UUID("12345678-1234-5678-1234-567812345678"): "uuid_secret",
}

for key, value in secret_map.value.items():
assert isinstance(value, PydanticSecret)
assert value.get_secret_value() == secret_map.get()[key]

def test_secret_map_with_invalid_input(self):
with pytest.raises(ValueError, match="`value`|must be a mapping"):
SecretMap(value="not a mapping")
@pytest.mark.parametrize(
"value",
[
SecretStr("test"),
PydanticSecret[dict]({"key": "value"}),
PydanticSecret[list](["test"]),
],
ids=["secret_string", "secret_dict", "secret_list"],
)
def test_secret_block_with_pydantic_secret(value):
Secret(value=value).save(name="test")
api_block = Secret.load("test")
assert isinstance(api_block.value, PydanticSecret)

def test_secret_map_with_non_hashable_key(self):
with pytest.raises(TypeError, match="unhashable"):
SecretMap(value={[1, 2, 3]: "value"})
assert api_block.get() == value.get_secret_value()

0 comments on commit 005281c

Please sign in to comment.