Skip to content
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
2 changes: 0 additions & 2 deletions docs/source/api-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,6 @@

.. autoenum:: Encryption

.. autoenum:: StorageClass

.. autoenum:: TimestampingMode

.. autoclass:: Timestamping
Expand Down
2 changes: 1 addition & 1 deletion s2-specs
Submodule s2-specs updated 2 files
+75 −31 s2/v1/openapi.json
+11 −2 s2/v1/s2s.md
2 changes: 0 additions & 2 deletions src/s2_sdk/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@
Scalar,
SeqNum,
SequencedRecord,
StorageClass,
StreamConfig,
StreamInfo,
StreamMetricSet,
Expand Down Expand Up @@ -106,7 +105,6 @@
"LocationInfo",
"CommandRecord",
"metered_bytes",
"StorageClass",
"TimestampingMode",
"Timestamping",
"StreamConfig",
Expand Down
8 changes: 4 additions & 4 deletions src/s2_sdk/_mappers.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
Scalar,
SeqNum,
SequencedRecord,
StorageClass,
StreamConfig,
StreamInfo,
StreamPosition,
Expand Down Expand Up @@ -88,6 +87,8 @@ def location_info_from_json(data: dict[str, Any]) -> LocationInfo:
return LocationInfo(
name=data["name"],
is_private=data["is_private"],
storage_classes=data.get("storage_classes"),
default_storage_class=data.get("default_storage_class"),
)


Expand All @@ -96,7 +97,7 @@ def stream_config_to_json(config: StreamConfig | None) -> dict[str, Any] | None:
return None
result: dict[str, Any] = {}
if config.storage_class is not None:
result["storage_class"] = config.storage_class.value
result["storage_class"] = config.storage_class
if config.retention_policy is not None:
result["retention_policy"] = _retention_policy_to_json(config.retention_policy)
if config.timestamping is not None:
Expand Down Expand Up @@ -137,9 +138,8 @@ def stream_config_from_json(data: dict[str, Any]) -> StreamConfig:
doe = data.get("delete_on_empty")
delete_on_empty_min_age = doe.get("min_age_secs") if doe else None

sc = data.get("storage_class")
return StreamConfig(
storage_class=StorageClass(sc) if sc else None,
storage_class=data.get("storage_class"),
retention_policy=retention_policy,
timestamping=timestamping,
delete_on_empty_min_age=delete_on_empty_min_age,
Expand Down
13 changes: 6 additions & 7 deletions src/s2_sdk/_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -376,12 +376,11 @@ class LocationInfo:
is_private: bool
"""Whether this location is an account-private placement."""

storage_classes: list[str] | None = None
"""Storage classes available to the account in this location."""

class StorageClass(_DocEnum):
"""Storage class for recent appends."""

STANDARD = "standard", "Offers end-to-end latencies under 500 ms."
EXPRESS = "express", "Offers end-to-end latencies under 50 ms."
default_storage_class: str | None = None
"""Default storage class for this location."""


class TimestampingMode(_DocEnum):
Expand Down Expand Up @@ -492,8 +491,8 @@ class Timestamping:
class StreamConfig:
"""Stream configuration."""

storage_class: StorageClass | None = None
"""Storage class for recent appends. Defaults to ``EXPRESS``."""
storage_class: str | None = None
"""Storage class for recent appends. See :meth:`S2.list_locations`."""

retention_policy: int | Literal["infinite"] | None = None
"""Retention duration in seconds, or ``"infinite"``. Default is 7 days."""
Expand Down
14 changes: 4 additions & 10 deletions tests/test_account_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
PrefixMatch,
S2Basin,
S2ServerError,
StorageClass,
StreamConfig,
Timestamping,
TimestampingMode,
Expand All @@ -36,7 +35,7 @@ async def test_create_basin(self, s2: S2, basin_name: str):
async def test_create_basin_with_config(self, s2: S2, basin_name: str):
config = BasinConfig(
default_stream_config=StreamConfig(
storage_class=StorageClass.STANDARD,
storage_class="standard",
retention_policy=86400 * 7,
timestamping=Timestamping(
mode=TimestampingMode.CLIENT_REQUIRE,
Expand All @@ -54,10 +53,7 @@ async def test_create_basin_with_config(self, s2: S2, basin_name: str):

retrieved_config = await s2.get_basin_config(basin_name)
assert retrieved_config.default_stream_config is not None
assert (
retrieved_config.default_stream_config.storage_class
== StorageClass.STANDARD
)
assert retrieved_config.default_stream_config.storage_class == "standard"
assert retrieved_config.default_stream_config.retention_policy == 86400 * 7
assert (
retrieved_config.default_stream_config.timestamping.mode
Expand Down Expand Up @@ -129,7 +125,7 @@ async def test_locations(self, s2: S2):
async def test_reconfigure_basin(self, s2: S2, basin: S2Basin):
config = BasinConfig(
default_stream_config=StreamConfig(
storage_class=StorageClass.STANDARD,
storage_class="standard",
retention_policy=3600,
),
create_stream_on_append=True,
Expand All @@ -138,9 +134,7 @@ async def test_reconfigure_basin(self, s2: S2, basin: S2Basin):
updated_config = await s2.reconfigure_basin(basin.name, config=config)

assert updated_config.default_stream_config is not None
assert (
updated_config.default_stream_config.storage_class == StorageClass.STANDARD
)
assert updated_config.default_stream_config.storage_class == "standard"
assert updated_config.default_stream_config.retention_policy == 3600
assert updated_config.create_stream_on_append is True

Expand Down
41 changes: 22 additions & 19 deletions tests/test_basin_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
import pytest

from s2_sdk import (
S2,
EnsureStatus,
S2Basin,
S2ServerError,
S2Stream,
StorageClass,
StreamConfig,
Timestamping,
TimestampingMode,
Expand All @@ -34,7 +34,7 @@ async def test_create_stream_with_config(
basin = shared_basin

config = StreamConfig(
storage_class=StorageClass.STANDARD,
storage_class="standard",
retention_policy=86400 * 3,
timestamping=Timestamping(
mode=TimestampingMode.ARRIVAL,
Expand All @@ -48,7 +48,7 @@ async def test_create_stream_with_config(
assert stream_info.name == stream_name

retrieved_config = await basin.get_stream_config(stream_name)
assert retrieved_config.storage_class == StorageClass.STANDARD
assert retrieved_config.storage_class == "standard"
assert retrieved_config.retention_policy == 86400 * 3
assert retrieved_config.timestamping is not None
assert retrieved_config.timestamping.mode == TimestampingMode.ARRIVAL
Expand All @@ -58,17 +58,20 @@ async def test_create_stream_with_config(
finally:
await basin.delete_stream(stream_name)

async def test_default_stream_config(self, shared_basin: S2Basin, stream: S2Stream):
basin = shared_basin

config = await basin.get_stream_config(stream.name)
assert config.storage_class == StorageClass.EXPRESS
async def test_default_stream_config(
self, s2: S2, shared_basin: S2Basin, stream: S2Stream
):
config = await shared_basin.get_stream_config(stream.name)
basin_config = await s2.get_basin_config(shared_basin.name)
defaults = basin_config.default_stream_config
if defaults is not None and defaults.storage_class is not None:
assert config.storage_class == defaults.storage_class
assert config.retention_policy == 86400 * 7

async def test_reconfigure_stream(self, shared_basin: S2Basin, stream: S2Stream):
basin = shared_basin
config = StreamConfig(
storage_class=StorageClass.STANDARD,
storage_class="standard",
retention_policy="infinite",
timestamping=Timestamping(
mode=TimestampingMode.CLIENT_REQUIRE, uncapped=True
Expand All @@ -77,23 +80,23 @@ async def test_reconfigure_stream(self, shared_basin: S2Basin, stream: S2Stream)
)

updated_config = await basin.reconfigure_stream(stream.name, config=config)
assert updated_config.storage_class == StorageClass.STANDARD
assert updated_config.storage_class == "standard"
assert updated_config.retention_policy == "infinite"
assert updated_config.timestamping is not None
assert updated_config.timestamping.mode == TimestampingMode.CLIENT_REQUIRE
assert updated_config.timestamping.uncapped is True
assert updated_config.delete_on_empty_min_age == 1800

config = StreamConfig(
storage_class=StorageClass.EXPRESS,
storage_class="express",
retention_policy=86400 * 90,
timestamping=Timestamping(
mode=TimestampingMode.CLIENT_PREFER, uncapped=False
),
delete_on_empty_min_age=3600,
)
updated_config = await basin.reconfigure_stream(stream.name, config=config)
assert updated_config.storage_class == StorageClass.EXPRESS
assert updated_config.storage_class == "express"
assert updated_config.retention_policy == 86400 * 90
assert updated_config.timestamping.mode == TimestampingMode.CLIENT_PREFER
assert updated_config.timestamping.uncapped is False
Expand Down Expand Up @@ -225,12 +228,12 @@ async def test_list_streams_with_prefix(
async def test_create_stream_storage_class_express(
self, shared_basin: S2Basin, stream_name: str
):
config = StreamConfig(storage_class=StorageClass.EXPRESS)
config = StreamConfig(storage_class="express")
info = await shared_basin.create_stream(name=stream_name, config=config)
try:
assert info.name == stream_name
retrieved = await shared_basin.get_stream_config(stream_name)
assert retrieved.storage_class == StorageClass.EXPRESS
assert retrieved.storage_class == "express"
finally:
await shared_basin.delete_stream(stream_name)

Expand Down Expand Up @@ -290,9 +293,9 @@ async def test_reconfigure_stream_storage_class(
self, shared_basin: S2Basin, stream: S2Stream
):
updated = await shared_basin.reconfigure_stream(
stream.name, config=StreamConfig(storage_class=StorageClass.STANDARD)
stream.name, config=StreamConfig(storage_class="standard")
)
assert updated.storage_class == StorageClass.STANDARD
assert updated.storage_class == "standard"

async def test_reconfigure_stream_retention(
self, shared_basin: S2Basin, stream: S2Stream
Expand Down Expand Up @@ -500,9 +503,9 @@ async def test_reconfigure_stream_storage_class_express(
await shared_basin.create_stream(name=stream_name)
try:
updated = await shared_basin.reconfigure_stream(
stream_name, config=StreamConfig(storage_class=StorageClass.EXPRESS)
stream_name, config=StreamConfig(storage_class="express")
)
assert updated.storage_class == StorageClass.EXPRESS
assert updated.storage_class == "express"
except S2ServerError:
pass # Free tier may not support Express
finally:
Expand Down Expand Up @@ -571,7 +574,7 @@ async def test_reconfigure_stream_nonexistent_errors(self, shared_basin: S2Basin
with pytest.raises(S2ServerError):
await shared_basin.reconfigure_stream(
"nonexistent-stream-xyz",
config=StreamConfig(storage_class=StorageClass.STANDARD),
config=StreamConfig(storage_class="standard"),
)

async def test_delete_stream_already_deleting_is_idempotent(
Expand Down
11 changes: 5 additions & 6 deletions tests/test_stream_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
S2Stream,
SeqNum,
SeqNumMismatchError,
StorageClass,
StreamConfig,
TailOffset,
Timestamp,
Expand Down Expand Up @@ -861,7 +860,7 @@ async def test_auto_create_config_overrides_basin_defaults_and_inherits_unset_fi
create_stream_on_append=True,
create_stream_on_read=True,
default_stream_config=StreamConfig(
storage_class=StorageClass.STANDARD,
storage_class="standard",
retention_policy=7200,
),
),
Expand All @@ -880,7 +879,7 @@ async def test_auto_create_config_overrides_basin_defaults_and_inherits_unset_fi
)

actual = await basin.get_stream_config(stream_name)
assert actual.storage_class == StorageClass.STANDARD
assert actual.storage_class == "standard"
assert actual.retention_policy == 3600
assert actual.timestamping is not None
assert actual.timestamping.mode == TimestampingMode.CLIENT_PREFER
Expand All @@ -899,21 +898,21 @@ async def test_auto_create_config_is_ignored_for_existing_stream(
await basin.create_stream(
stream_name,
config=StreamConfig(
storage_class=StorageClass.EXPRESS,
storage_class="express",
retention_policy=7200,
),
)
await self._run_with_stream_config(
basin.stream(stream_name),
operation,
StreamConfig(
storage_class=StorageClass.STANDARD,
storage_class="standard",
retention_policy=3600,
),
)

actual = await basin.get_stream_config(stream_name)
assert actual.storage_class == StorageClass.EXPRESS
assert actual.storage_class == "express"
assert actual.retention_policy == 7200


Expand Down
Loading