diff --git a/.gitignore b/.gitignore index b6e4761..8ed4472 100644 --- a/.gitignore +++ b/.gitignore @@ -8,7 +8,7 @@ __pycache__/ # Distribution / packaging .Python -build/ +build/api/ develop-eggs/ dist/ downloads/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9280f87 --- /dev/null +++ b/Makefile @@ -0,0 +1,5 @@ +init: + pip install -r requirements.txt + +protos: + ./build/compile-protos.sh diff --git a/atomix/__init__.py b/atomix/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/atomix/client.py b/atomix/client.py new file mode 100644 index 0000000..e69de29 diff --git a/atomix/proto/__init__.py b/atomix/proto/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/atomix/proto/database.py b/atomix/proto/database.py new file mode 100644 index 0000000..badd70b --- /dev/null +++ b/atomix/proto/database.py @@ -0,0 +1,136 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# sources: atomix/database/database.proto +# plugin: python-betterproto +from dataclasses import dataclass +from typing import List, Optional + +import betterproto +import grpclib + + +@dataclass +class DatabaseId(betterproto.Message): + """Database identifier""" + + # name is the name of the database + name: str = betterproto.string_field(1) + # namespace is the namespace to which the database belongs + namespace: str = betterproto.string_field(2) + + +@dataclass +class Database(betterproto.Message): + """Database configuration""" + + # id is the database identifier + id: "DatabaseId" = betterproto.message_field(1) + # partitions is a list of partitions in the cluster + partitions: List["Partition"] = betterproto.message_field(2) + + +@dataclass +class DatabaseConfig(betterproto.Message): + """Database configuration""" + + # nodes is a list of database replicas + replicas: List["ReplicaConfig"] = betterproto.message_field(1) + # partitions is a list of partitions owned by the replicas + partitions: List["PartitionId"] = betterproto.message_field(2) + + +@dataclass +class ReplicaConfig(betterproto.Message): + """Replica configuration""" + + # id is the unique member identifier + id: str = betterproto.string_field(1) + # host is the member host + host: str = betterproto.string_field(2) + # api_port is the port to use for the client API + api_port: int = betterproto.int32_field(3) + # protocol_port is the port to use for intra-cluster communication + protocol_port: int = betterproto.int32_field(4) + + +@dataclass +class PartitionId(betterproto.Message): + """Partition identifier""" + + partition: int = betterproto.int32_field(1) + + +@dataclass +class Partition(betterproto.Message): + """Partition info""" + + partition_id: "PartitionId" = betterproto.message_field(1) + endpoints: List["PartitionEndpoint"] = betterproto.message_field(2) + + +@dataclass +class PartitionEndpoint(betterproto.Message): + """Partition endpoint""" + + host: str = betterproto.string_field(1) + port: int = betterproto.int32_field(2) + + +@dataclass +class GetDatabaseRequest(betterproto.Message): + """Gets a database in a namespace""" + + id: "DatabaseId" = betterproto.message_field(1) + + +@dataclass +class GetDatabaseResponse(betterproto.Message): + """Returns a database in a namespace""" + + database: "Database" = betterproto.message_field(1) + + +@dataclass +class GetDatabasesRequest(betterproto.Message): + """Gets a list of databases in a namespace""" + + namespace: str = betterproto.string_field(1) + + +@dataclass +class GetDatabasesResponse(betterproto.Message): + """Returns a list of databases in a namespace""" + + databases: List["Database"] = betterproto.message_field(1) + + +class DatabaseServiceStub(betterproto.ServiceStub): + """Atomix database service""" + + async def get_database( + self, *, id: Optional["DatabaseId"] = None + ) -> GetDatabaseResponse: + """GetDatabase gets a database controlled by the controller""" + + request = GetDatabaseRequest() + if id is not None: + request.id = id + + return await self._unary_unary( + "/atomix.database.DatabaseService/GetDatabase", + request, + GetDatabaseResponse, + ) + + async def get_databases(self, *, namespace: str = "") -> GetDatabasesResponse: + """ + GetDatabases gets a list of databases controlled by the controller + """ + + request = GetDatabasesRequest() + request.namespace = namespace + + return await self._unary_unary( + "/atomix.database.DatabaseService/GetDatabases", + request, + GetDatabasesResponse, + ) diff --git a/atomix/proto/election.py b/atomix/proto/election.py new file mode 100644 index 0000000..6b9a6ae --- /dev/null +++ b/atomix/proto/election.py @@ -0,0 +1,256 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# sources: atomix/election/election.proto +# plugin: python-betterproto +from dataclasses import dataclass +from datetime import datetime +from typing import AsyncGenerator, List, Optional + +import betterproto + +from atomix.proto import headers + + +class EventResponseType(betterproto.Enum): + CHANGED = 0 + + +@dataclass +class CreateRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + + +@dataclass +class CreateResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + + +@dataclass +class CloseRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + delete: bool = betterproto.bool_field(2) + + +@dataclass +class CloseResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + + +@dataclass +class EnterRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + candidate_id: str = betterproto.string_field(2) + + +@dataclass +class EnterResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + term: "Term" = betterproto.message_field(2) + + +@dataclass +class WithdrawRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + candidate_id: str = betterproto.string_field(2) + + +@dataclass +class WithdrawResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + term: "Term" = betterproto.message_field(2) + + +@dataclass +class AnointRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + candidate_id: str = betterproto.string_field(2) + + +@dataclass +class AnointResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + term: "Term" = betterproto.message_field(2) + + +@dataclass +class PromoteRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + candidate_id: str = betterproto.string_field(2) + + +@dataclass +class PromoteResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + term: "Term" = betterproto.message_field(2) + + +@dataclass +class EvictRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + candidate_id: str = betterproto.string_field(2) + + +@dataclass +class EvictResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + term: "Term" = betterproto.message_field(2) + + +@dataclass +class GetTermRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + + +@dataclass +class GetTermResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + term: "Term" = betterproto.message_field(2) + + +@dataclass +class EventRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + + +@dataclass +class EventResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + type: "EventResponseType" = betterproto.enum_field(2) + term: "Term" = betterproto.message_field(3) + + +@dataclass +class Term(betterproto.Message): + id: int = betterproto.uint64_field(1) + timestamp: datetime = betterproto.message_field(2) + leader: str = betterproto.string_field(3) + candidates: List[str] = betterproto.string_field(4) + + +class LeaderElectionServiceStub(betterproto.ServiceStub): + """LeaderElectionService implements a distributed leader election""" + + async def create( + self, *, header: Optional[headers.RequestHeader] = None + ) -> CreateResponse: + """Create creates a LeaderElection instance""" + + request = CreateRequest() + if header is not None: + request.header = header + + return await self._unary_unary( + "/atomix.election.LeaderElectionService/Create", request, CreateResponse, + ) + + async def close( + self, *, header: Optional[headers.RequestHeader] = None, delete: bool = False + ) -> CloseResponse: + """Close closes a LeaderElection instance""" + + request = CloseRequest() + if header is not None: + request.header = header + request.delete = delete + + return await self._unary_unary( + "/atomix.election.LeaderElectionService/Close", request, CloseResponse, + ) + + async def enter( + self, *, header: Optional[headers.RequestHeader] = None, candidate_id: str = "" + ) -> EnterResponse: + """Enter enters the leader election""" + + request = EnterRequest() + if header is not None: + request.header = header + request.candidate_id = candidate_id + + return await self._unary_unary( + "/atomix.election.LeaderElectionService/Enter", request, EnterResponse, + ) + + async def withdraw( + self, *, header: Optional[headers.RequestHeader] = None, candidate_id: str = "" + ) -> WithdrawResponse: + """Withdraw withdraws a candidate from the leader election""" + + request = WithdrawRequest() + if header is not None: + request.header = header + request.candidate_id = candidate_id + + return await self._unary_unary( + "/atomix.election.LeaderElectionService/Withdraw", + request, + WithdrawResponse, + ) + + async def anoint( + self, *, header: Optional[headers.RequestHeader] = None, candidate_id: str = "" + ) -> AnointResponse: + """Anoint anoints a candidate leader""" + + request = AnointRequest() + if header is not None: + request.header = header + request.candidate_id = candidate_id + + return await self._unary_unary( + "/atomix.election.LeaderElectionService/Anoint", request, AnointResponse, + ) + + async def promote( + self, *, header: Optional[headers.RequestHeader] = None, candidate_id: str = "" + ) -> PromoteResponse: + """Promote promotes a candidate""" + + request = PromoteRequest() + if header is not None: + request.header = header + request.candidate_id = candidate_id + + return await self._unary_unary( + "/atomix.election.LeaderElectionService/Promote", request, PromoteResponse, + ) + + async def evict( + self, *, header: Optional[headers.RequestHeader] = None, candidate_id: str = "" + ) -> EvictResponse: + """Evict evicts a candidate from the election""" + + request = EvictRequest() + if header is not None: + request.header = header + request.candidate_id = candidate_id + + return await self._unary_unary( + "/atomix.election.LeaderElectionService/Evict", request, EvictResponse, + ) + + async def get_term( + self, *, header: Optional[headers.RequestHeader] = None + ) -> GetTermResponse: + """GetTerm gets the current leadership term""" + + request = GetTermRequest() + if header is not None: + request.header = header + + return await self._unary_unary( + "/atomix.election.LeaderElectionService/GetTerm", request, GetTermResponse, + ) + + async def events( + self, *, header: Optional[headers.RequestHeader] = None + ) -> AsyncGenerator[EventResponse, None]: + """Events listens for leadership events""" + + request = EventRequest() + if header is not None: + request.header = header + + async for response in self._unary_stream( + "/atomix.election.LeaderElectionService/Events", request, EventResponse, + ): + yield response diff --git a/atomix/proto/headers.py b/atomix/proto/headers.py new file mode 100644 index 0000000..8e039c2 --- /dev/null +++ b/atomix/proto/headers.py @@ -0,0 +1,48 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# sources: atomix/headers/headers.proto +# plugin: python-betterproto +from dataclasses import dataclass +from typing import List + +import betterproto + +from atomix.proto import primitive + + +class ResponseType(betterproto.Enum): + RESPONSE = 0 + OPEN_STREAM = 1 + CLOSE_STREAM = 2 + + +class ResponseStatus(betterproto.Enum): + OK = 0 + ERROR = 1 + NOT_LEADER = 2 + + +@dataclass +class RequestHeader(betterproto.Message): + partition: int = betterproto.int32_field(6) + primitive: primitive.PrimitiveId = betterproto.message_field(1) + session_id: int = betterproto.uint64_field(2) + request_id: int = betterproto.uint64_field(3) + index: int = betterproto.uint64_field(4) + streams: List["StreamHeader"] = betterproto.message_field(5) + + +@dataclass +class ResponseHeader(betterproto.Message): + session_id: int = betterproto.uint64_field(1) + stream_id: int = betterproto.uint64_field(2) + response_id: int = betterproto.uint64_field(3) + index: int = betterproto.uint64_field(4) + leader: str = betterproto.string_field(5) + status: "ResponseStatus" = betterproto.enum_field(6) + type: "ResponseType" = betterproto.enum_field(7) + + +@dataclass +class StreamHeader(betterproto.Message): + stream_id: int = betterproto.uint64_field(1) + response_id: int = betterproto.uint64_field(2) diff --git a/atomix/proto/indexedmap.py b/atomix/proto/indexedmap.py new file mode 100644 index 0000000..2f2230c --- /dev/null +++ b/atomix/proto/indexedmap.py @@ -0,0 +1,537 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# sources: atomix/indexedmap/indexedmap.proto +# plugin: python-betterproto +from dataclasses import dataclass +from datetime import datetime, timedelta +from typing import AsyncGenerator, Optional + +import betterproto + +from atomix.proto import headers + + +class ResponseStatus(betterproto.Enum): + OK = 0 + NOOP = 1 + WRITE_LOCK = 2 + PRECONDITION_FAILED = 3 + + +class EventResponseType(betterproto.Enum): + NONE = 0 + INSERTED = 1 + UPDATED = 2 + REMOVED = 3 + + +@dataclass +class CreateRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + + +@dataclass +class CreateResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + + +@dataclass +class CloseRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + delete: bool = betterproto.bool_field(2) + + +@dataclass +class CloseResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + + +@dataclass +class ExistsRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + key: str = betterproto.string_field(2) + + +@dataclass +class ExistsResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + contains_key: bool = betterproto.bool_field(2) + + +@dataclass +class SizeRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + + +@dataclass +class SizeResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + size: int = betterproto.int32_field(2) + + +@dataclass +class PutRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + index: int = betterproto.int64_field(2) + key: str = betterproto.string_field(3) + value: bytes = betterproto.bytes_field(4) + version: int = betterproto.int64_field(5) + ttl: timedelta = betterproto.message_field(6) + + +@dataclass +class PutResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + status: "ResponseStatus" = betterproto.enum_field(2) + index: int = betterproto.int64_field(3) + key: str = betterproto.string_field(4) + created: datetime = betterproto.message_field(5) + updated: datetime = betterproto.message_field(6) + previous_value: bytes = betterproto.bytes_field(7) + previous_version: int = betterproto.int64_field(8) + + +@dataclass +class ReplaceRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + index: int = betterproto.int64_field(2) + key: str = betterproto.string_field(3) + previous_value: bytes = betterproto.bytes_field(4) + previous_version: int = betterproto.int64_field(5) + new_value: bytes = betterproto.bytes_field(6) + ttl: timedelta = betterproto.message_field(7) + + +@dataclass +class ReplaceResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + status: "ResponseStatus" = betterproto.enum_field(2) + index: int = betterproto.int64_field(3) + key: str = betterproto.string_field(4) + created: datetime = betterproto.message_field(5) + updated: datetime = betterproto.message_field(6) + previous_value: bytes = betterproto.bytes_field(7) + previous_version: int = betterproto.int64_field(8) + + +@dataclass +class GetRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + index: int = betterproto.int64_field(2) + key: str = betterproto.string_field(3) + + +@dataclass +class GetResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + index: int = betterproto.int64_field(2) + key: str = betterproto.string_field(3) + value: bytes = betterproto.bytes_field(4) + version: int = betterproto.int64_field(5) + created: datetime = betterproto.message_field(6) + updated: datetime = betterproto.message_field(7) + + +@dataclass +class FirstEntryRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + + +@dataclass +class FirstEntryResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + index: int = betterproto.int64_field(2) + key: str = betterproto.string_field(3) + value: bytes = betterproto.bytes_field(4) + version: int = betterproto.int64_field(5) + created: datetime = betterproto.message_field(6) + updated: datetime = betterproto.message_field(7) + + +@dataclass +class LastEntryRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + + +@dataclass +class LastEntryResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + index: int = betterproto.int64_field(2) + key: str = betterproto.string_field(3) + value: bytes = betterproto.bytes_field(4) + version: int = betterproto.int64_field(5) + created: datetime = betterproto.message_field(6) + updated: datetime = betterproto.message_field(7) + + +@dataclass +class PrevEntryRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + index: int = betterproto.int64_field(2) + + +@dataclass +class PrevEntryResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + index: int = betterproto.int64_field(2) + key: str = betterproto.string_field(3) + value: bytes = betterproto.bytes_field(4) + version: int = betterproto.int64_field(5) + created: datetime = betterproto.message_field(6) + updated: datetime = betterproto.message_field(7) + + +@dataclass +class NextEntryRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + index: int = betterproto.int64_field(2) + + +@dataclass +class NextEntryResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + index: int = betterproto.int64_field(2) + key: str = betterproto.string_field(3) + value: bytes = betterproto.bytes_field(4) + version: int = betterproto.int64_field(5) + created: datetime = betterproto.message_field(6) + updated: datetime = betterproto.message_field(7) + + +@dataclass +class RemoveRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + index: int = betterproto.int64_field(2) + key: str = betterproto.string_field(3) + value: bytes = betterproto.bytes_field(4) + version: int = betterproto.int64_field(5) + created: datetime = betterproto.message_field(6) + updated: datetime = betterproto.message_field(7) + + +@dataclass +class RemoveResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + status: "ResponseStatus" = betterproto.enum_field(2) + index: int = betterproto.int64_field(3) + key: str = betterproto.string_field(4) + previous_value: bytes = betterproto.bytes_field(5) + previous_version: int = betterproto.int64_field(6) + + +@dataclass +class ClearRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + + +@dataclass +class ClearResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + + +@dataclass +class EntriesRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + + +@dataclass +class EntriesResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + key: str = betterproto.string_field(2) + index: int = betterproto.int64_field(3) + value: bytes = betterproto.bytes_field(4) + version: int = betterproto.int64_field(5) + created: datetime = betterproto.message_field(6) + updated: datetime = betterproto.message_field(7) + + +@dataclass +class EventRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + replay: bool = betterproto.bool_field(2) + key: str = betterproto.string_field(3) + index: int = betterproto.int64_field(4) + + +@dataclass +class EventResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + type: "EventResponseType" = betterproto.enum_field(2) + key: str = betterproto.string_field(3) + index: int = betterproto.int64_field(4) + value: bytes = betterproto.bytes_field(5) + version: int = betterproto.int64_field(6) + created: datetime = betterproto.message_field(7) + updated: datetime = betterproto.message_field(8) + + +class IndexedMapServiceStub(betterproto.ServiceStub): + """IndexedMap service""" + + async def create( + self, *, header: Optional[headers.RequestHeader] = None + ) -> CreateResponse: + """Create creates an indexed map""" + + request = CreateRequest() + if header is not None: + request.header = header + + return await self._unary_unary( + "/atomix.indexedmap.IndexedMapService/Create", request, CreateResponse, + ) + + async def close( + self, *, header: Optional[headers.RequestHeader] = None, delete: bool = False + ) -> CloseResponse: + """Close closes an indexed map""" + + request = CloseRequest() + if header is not None: + request.header = header + request.delete = delete + + return await self._unary_unary( + "/atomix.indexedmap.IndexedMapService/Close", request, CloseResponse, + ) + + async def size( + self, *, header: Optional[headers.RequestHeader] = None + ) -> SizeResponse: + """Size returns the size of the map""" + + request = SizeRequest() + if header is not None: + request.header = header + + return await self._unary_unary( + "/atomix.indexedmap.IndexedMapService/Size", request, SizeResponse, + ) + + async def exists( + self, *, header: Optional[headers.RequestHeader] = None, key: str = "" + ) -> ExistsResponse: + """Exists checks whether a key exists in the map""" + + request = ExistsRequest() + if header is not None: + request.header = header + request.key = key + + return await self._unary_unary( + "/atomix.indexedmap.IndexedMapService/Exists", request, ExistsResponse, + ) + + async def put( + self, + *, + header: Optional[headers.RequestHeader] = None, + index: int = 0, + key: str = "", + value: bytes = b"", + version: int = 0, + ttl: Optional[timedelta] = None, + ) -> PutResponse: + """Put puts an entry into the map""" + + request = PutRequest() + if header is not None: + request.header = header + request.index = index + request.key = key + request.value = value + request.version = version + if ttl is not None: + request.ttl = ttl + + return await self._unary_unary( + "/atomix.indexedmap.IndexedMapService/Put", request, PutResponse, + ) + + async def replace( + self, + *, + header: Optional[headers.RequestHeader] = None, + index: int = 0, + key: str = "", + previous_value: bytes = b"", + previous_version: int = 0, + new_value: bytes = b"", + ttl: Optional[timedelta] = None, + ) -> ReplaceResponse: + """ + Replace performs a check-and-set operation on an entry in the map + """ + + request = ReplaceRequest() + if header is not None: + request.header = header + request.index = index + request.key = key + request.previous_value = previous_value + request.previous_version = previous_version + request.new_value = new_value + if ttl is not None: + request.ttl = ttl + + return await self._unary_unary( + "/atomix.indexedmap.IndexedMapService/Replace", request, ReplaceResponse, + ) + + async def get( + self, + *, + header: Optional[headers.RequestHeader] = None, + index: int = 0, + key: str = "", + ) -> GetResponse: + """Get gets the entry for a key""" + + request = GetRequest() + if header is not None: + request.header = header + request.index = index + request.key = key + + return await self._unary_unary( + "/atomix.indexedmap.IndexedMapService/Get", request, GetResponse, + ) + + async def first_entry( + self, *, header: Optional[headers.RequestHeader] = None + ) -> FirstEntryResponse: + """FirstEntry gets the first entry in the map""" + + request = FirstEntryRequest() + if header is not None: + request.header = header + + return await self._unary_unary( + "/atomix.indexedmap.IndexedMapService/FirstEntry", + request, + FirstEntryResponse, + ) + + async def last_entry( + self, *, header: Optional[headers.RequestHeader] = None + ) -> LastEntryResponse: + """LastEntry gets the last entry in the map""" + + request = LastEntryRequest() + if header is not None: + request.header = header + + return await self._unary_unary( + "/atomix.indexedmap.IndexedMapService/LastEntry", + request, + LastEntryResponse, + ) + + async def prev_entry( + self, *, header: Optional[headers.RequestHeader] = None, index: int = 0 + ) -> PrevEntryResponse: + """PrevEntry gets the previous entry in the map""" + + request = PrevEntryRequest() + if header is not None: + request.header = header + request.index = index + + return await self._unary_unary( + "/atomix.indexedmap.IndexedMapService/PrevEntry", + request, + PrevEntryResponse, + ) + + async def next_entry( + self, *, header: Optional[headers.RequestHeader] = None, index: int = 0 + ) -> NextEntryResponse: + """NextEntry gets the next entry in the map""" + + request = NextEntryRequest() + if header is not None: + request.header = header + request.index = index + + return await self._unary_unary( + "/atomix.indexedmap.IndexedMapService/NextEntry", + request, + NextEntryResponse, + ) + + async def remove( + self, + *, + header: Optional[headers.RequestHeader] = None, + index: int = 0, + key: str = "", + value: bytes = b"", + version: int = 0, + created: Optional[datetime] = None, + updated: Optional[datetime] = None, + ) -> RemoveResponse: + """Remove removes an entry from the map""" + + request = RemoveRequest() + if header is not None: + request.header = header + request.index = index + request.key = key + request.value = value + request.version = version + if created is not None: + request.created = created + if updated is not None: + request.updated = updated + + return await self._unary_unary( + "/atomix.indexedmap.IndexedMapService/Remove", request, RemoveResponse, + ) + + async def clear( + self, *, header: Optional[headers.RequestHeader] = None + ) -> ClearResponse: + """Clear removes all entries from the map""" + + request = ClearRequest() + if header is not None: + request.header = header + + return await self._unary_unary( + "/atomix.indexedmap.IndexedMapService/Clear", request, ClearResponse, + ) + + async def events( + self, + *, + header: Optional[headers.RequestHeader] = None, + replay: bool = False, + key: str = "", + index: int = 0, + ) -> AsyncGenerator[EventResponse, None]: + """Events listens for change events""" + + request = EventRequest() + if header is not None: + request.header = header + request.replay = replay + request.key = key + request.index = index + + async for response in self._unary_stream( + "/atomix.indexedmap.IndexedMapService/Events", request, EventResponse, + ): + yield response + + async def entries( + self, *, header: Optional[headers.RequestHeader] = None + ) -> AsyncGenerator[EntriesResponse, None]: + """Entries lists all entries in the map""" + + request = EntriesRequest() + if header is not None: + request.header = header + + async for response in self._unary_stream( + "/atomix.indexedmap.IndexedMapService/Entries", request, EntriesResponse, + ): + yield response diff --git a/atomix/proto/leader.py b/atomix/proto/leader.py new file mode 100644 index 0000000..030acc9 --- /dev/null +++ b/atomix/proto/leader.py @@ -0,0 +1,151 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# sources: atomix/leader/latch.proto +# plugin: python-betterproto +from dataclasses import dataclass +from typing import AsyncGenerator, List, Optional + +import betterproto + +from atomix.proto import headers + + +class EventResponseType(betterproto.Enum): + CHANGED = 0 + + +@dataclass +class CreateRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + + +@dataclass +class CreateResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + + +@dataclass +class CloseRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + delete: bool = betterproto.bool_field(2) + + +@dataclass +class CloseResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + + +@dataclass +class LatchRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + participant_id: str = betterproto.string_field(2) + + +@dataclass +class LatchResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + latch: "Latch" = betterproto.message_field(2) + + +@dataclass +class GetRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + + +@dataclass +class GetResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + latch: "Latch" = betterproto.message_field(2) + + +@dataclass +class EventRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + + +@dataclass +class EventResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + type: "EventResponseType" = betterproto.enum_field(2) + latch: "Latch" = betterproto.message_field(3) + + +@dataclass +class Latch(betterproto.Message): + id: int = betterproto.uint64_field(1) + leader: str = betterproto.string_field(2) + participants: List[str] = betterproto.string_field(3) + + +class LeaderLatchServiceStub(betterproto.ServiceStub): + """Leader latch service""" + + async def create( + self, *, header: Optional[headers.RequestHeader] = None + ) -> CreateResponse: + """Create creates a leader latch""" + + request = CreateRequest() + if header is not None: + request.header = header + + return await self._unary_unary( + "/atomix.leader.LeaderLatchService/Create", request, CreateResponse, + ) + + async def close( + self, *, header: Optional[headers.RequestHeader] = None, delete: bool = False + ) -> CloseResponse: + """Close closes a leader latch""" + + request = CloseRequest() + if header is not None: + request.header = header + request.delete = delete + + return await self._unary_unary( + "/atomix.leader.LeaderLatchService/Close", request, CloseResponse, + ) + + async def latch( + self, + *, + header: Optional[headers.RequestHeader] = None, + participant_id: str = "", + ) -> LatchResponse: + """Latch attempts to acquire the leader latch""" + + request = LatchRequest() + if header is not None: + request.header = header + request.participant_id = participant_id + + return await self._unary_unary( + "/atomix.leader.LeaderLatchService/Latch", request, LatchResponse, + ) + + async def get( + self, *, header: Optional[headers.RequestHeader] = None + ) -> GetResponse: + """Get gets the current leader""" + + request = GetRequest() + if header is not None: + request.header = header + + return await self._unary_unary( + "/atomix.leader.LeaderLatchService/Get", request, GetResponse, + ) + + async def events( + self, *, header: Optional[headers.RequestHeader] = None + ) -> AsyncGenerator[EventResponse, None]: + """Events listens for leader change events""" + + request = EventRequest() + if header is not None: + request.header = header + + async for response in self._unary_stream( + "/atomix.leader.LeaderLatchService/Events", request, EventResponse, + ): + yield response diff --git a/atomix/proto/list.py b/atomix/proto/list.py new file mode 100644 index 0000000..9c68073 --- /dev/null +++ b/atomix/proto/list.py @@ -0,0 +1,345 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# sources: atomix/list/list.proto +# plugin: python-betterproto +from dataclasses import dataclass +from typing import AsyncGenerator, Optional + +import betterproto + +from atomix.proto import headers + + +class ResponseStatus(betterproto.Enum): + OK = 0 + NOOP = 1 + WRITE_LOCK = 2 + OUT_OF_BOUNDS = 3 + + +class EventResponseType(betterproto.Enum): + NONE = 0 + ADDED = 1 + REMOVED = 2 + + +@dataclass +class CreateRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + + +@dataclass +class CreateResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + + +@dataclass +class CloseRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + delete: bool = betterproto.bool_field(2) + + +@dataclass +class CloseResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + + +@dataclass +class SizeRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + + +@dataclass +class SizeResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + size: int = betterproto.int32_field(2) + + +@dataclass +class ContainsRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + value: str = betterproto.string_field(2) + + +@dataclass +class ContainsResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + contains: bool = betterproto.bool_field(2) + + +@dataclass +class AppendRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + value: str = betterproto.string_field(2) + + +@dataclass +class AppendResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + status: "ResponseStatus" = betterproto.enum_field(2) + + +@dataclass +class GetRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + index: int = betterproto.uint32_field(2) + + +@dataclass +class GetResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + status: "ResponseStatus" = betterproto.enum_field(2) + value: str = betterproto.string_field(3) + + +@dataclass +class SetRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + index: int = betterproto.uint32_field(2) + value: str = betterproto.string_field(3) + + +@dataclass +class SetResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + status: "ResponseStatus" = betterproto.enum_field(2) + + +@dataclass +class InsertRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + index: int = betterproto.uint32_field(2) + value: str = betterproto.string_field(3) + + +@dataclass +class InsertResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + status: "ResponseStatus" = betterproto.enum_field(2) + + +@dataclass +class RemoveRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + index: int = betterproto.uint32_field(2) + + +@dataclass +class RemoveResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + status: "ResponseStatus" = betterproto.enum_field(2) + value: str = betterproto.string_field(3) + + +@dataclass +class ClearRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + + +@dataclass +class ClearResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + + +@dataclass +class EventRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + replay: bool = betterproto.bool_field(2) + + +@dataclass +class EventResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + type: "EventResponseType" = betterproto.enum_field(2) + index: int = betterproto.uint32_field(3) + value: str = betterproto.string_field(4) + + +@dataclass +class IterateRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + + +@dataclass +class IterateResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + value: str = betterproto.string_field(2) + + +class ListServiceStub(betterproto.ServiceStub): + """ListService implements a distributed list""" + + async def create( + self, *, header: Optional[headers.RequestHeader] = None + ) -> CreateResponse: + """Create creates a list session""" + + request = CreateRequest() + if header is not None: + request.header = header + + return await self._unary_unary( + "/atomix.list.ListService/Create", request, CreateResponse, + ) + + async def close( + self, *, header: Optional[headers.RequestHeader] = None, delete: bool = False + ) -> CloseResponse: + """Close closes a list""" + + request = CloseRequest() + if header is not None: + request.header = header + request.delete = delete + + return await self._unary_unary( + "/atomix.list.ListService/Close", request, CloseResponse, + ) + + async def size( + self, *, header: Optional[headers.RequestHeader] = None + ) -> SizeResponse: + """Size gets the number of elements in the list""" + + request = SizeRequest() + if header is not None: + request.header = header + + return await self._unary_unary( + "/atomix.list.ListService/Size", request, SizeResponse, + ) + + async def contains( + self, *, header: Optional[headers.RequestHeader] = None, value: str = "" + ) -> ContainsResponse: + """Contains returns whether the list contains a value""" + + request = ContainsRequest() + if header is not None: + request.header = header + request.value = value + + return await self._unary_unary( + "/atomix.list.ListService/Contains", request, ContainsResponse, + ) + + async def append( + self, *, header: Optional[headers.RequestHeader] = None, value: str = "" + ) -> AppendResponse: + """Append appends a value to the list""" + + request = AppendRequest() + if header is not None: + request.header = header + request.value = value + + return await self._unary_unary( + "/atomix.list.ListService/Append", request, AppendResponse, + ) + + async def insert( + self, + *, + header: Optional[headers.RequestHeader] = None, + index: int = 0, + value: str = "", + ) -> InsertResponse: + """Insert inserts a value at a specific index in the list""" + + request = InsertRequest() + if header is not None: + request.header = header + request.index = index + request.value = value + + return await self._unary_unary( + "/atomix.list.ListService/Insert", request, InsertResponse, + ) + + async def get( + self, *, header: Optional[headers.RequestHeader] = None, index: int = 0 + ) -> GetResponse: + """Get gets the value at an index in the list""" + + request = GetRequest() + if header is not None: + request.header = header + request.index = index + + return await self._unary_unary( + "/atomix.list.ListService/Get", request, GetResponse, + ) + + async def set( + self, + *, + header: Optional[headers.RequestHeader] = None, + index: int = 0, + value: str = "", + ) -> SetResponse: + """Set sets the value at an index in the list""" + + request = SetRequest() + if header is not None: + request.header = header + request.index = index + request.value = value + + return await self._unary_unary( + "/atomix.list.ListService/Set", request, SetResponse, + ) + + async def remove( + self, *, header: Optional[headers.RequestHeader] = None, index: int = 0 + ) -> RemoveResponse: + """Remove removes an element from the list""" + + request = RemoveRequest() + if header is not None: + request.header = header + request.index = index + + return await self._unary_unary( + "/atomix.list.ListService/Remove", request, RemoveResponse, + ) + + async def clear( + self, *, header: Optional[headers.RequestHeader] = None + ) -> ClearResponse: + """Clear removes all elements from the list""" + + request = ClearRequest() + if header is not None: + request.header = header + + return await self._unary_unary( + "/atomix.list.ListService/Clear", request, ClearResponse, + ) + + async def events( + self, *, header: Optional[headers.RequestHeader] = None, replay: bool = False + ) -> AsyncGenerator[EventResponse, None]: + """Events listens for change events""" + + request = EventRequest() + if header is not None: + request.header = header + request.replay = replay + + async for response in self._unary_stream( + "/atomix.list.ListService/Events", request, EventResponse, + ): + yield response + + async def iterate( + self, *, header: Optional[headers.RequestHeader] = None + ) -> AsyncGenerator[IterateResponse, None]: + """Iterate streams all values in the list""" + + request = IterateRequest() + if header is not None: + request.header = header + + async for response in self._unary_stream( + "/atomix.list.ListService/Iterate", request, IterateResponse, + ): + yield response diff --git a/atomix/proto/lock.py b/atomix/proto/lock.py new file mode 100644 index 0000000..603018d --- /dev/null +++ b/atomix/proto/lock.py @@ -0,0 +1,144 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# sources: atomix/lock/lock.proto +# plugin: python-betterproto +from dataclasses import dataclass +from datetime import timedelta +from typing import Optional + +import betterproto + +from atomix.proto import headers + + +@dataclass +class CreateRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + + +@dataclass +class CreateResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + + +@dataclass +class CloseRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + delete: bool = betterproto.bool_field(2) + + +@dataclass +class CloseResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + + +@dataclass +class LockRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + timeout: timedelta = betterproto.message_field(2) + + +@dataclass +class LockResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + version: int = betterproto.uint64_field(2) + + +@dataclass +class UnlockRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + version: int = betterproto.uint64_field(2) + + +@dataclass +class UnlockResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + unlocked: bool = betterproto.bool_field(2) + + +@dataclass +class IsLockedRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + version: int = betterproto.uint64_field(2) + + +@dataclass +class IsLockedResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + is_locked: bool = betterproto.bool_field(2) + + +class LockServiceStub(betterproto.ServiceStub): + """LockService implements a distributed lock""" + + async def create( + self, *, header: Optional[headers.RequestHeader] = None + ) -> CreateResponse: + """Create creates a lock""" + + request = CreateRequest() + if header is not None: + request.header = header + + return await self._unary_unary( + "/atomix.lock.LockService/Create", request, CreateResponse, + ) + + async def close( + self, *, header: Optional[headers.RequestHeader] = None, delete: bool = False + ) -> CloseResponse: + """Close closes a lock""" + + request = CloseRequest() + if header is not None: + request.header = header + request.delete = delete + + return await self._unary_unary( + "/atomix.lock.LockService/Close", request, CloseResponse, + ) + + async def lock( + self, + *, + header: Optional[headers.RequestHeader] = None, + timeout: Optional[timedelta] = None, + ) -> LockResponse: + """Lock attempts to acquire the lock""" + + request = LockRequest() + if header is not None: + request.header = header + if timeout is not None: + request.timeout = timeout + + return await self._unary_unary( + "/atomix.lock.LockService/Lock", request, LockResponse, + ) + + async def unlock( + self, *, header: Optional[headers.RequestHeader] = None, version: int = 0 + ) -> UnlockResponse: + """Unlock releases the lock""" + + request = UnlockRequest() + if header is not None: + request.header = header + request.version = version + + return await self._unary_unary( + "/atomix.lock.LockService/Unlock", request, UnlockResponse, + ) + + async def is_locked( + self, *, header: Optional[headers.RequestHeader] = None, version: int = 0 + ) -> IsLockedResponse: + """IsLocked checks whether the lock is held""" + + request = IsLockedRequest() + if header is not None: + request.header = header + request.version = version + + return await self._unary_unary( + "/atomix.lock.LockService/IsLocked", request, IsLockedResponse, + ) diff --git a/atomix/proto/log.py b/atomix/proto/log.py new file mode 100644 index 0000000..e167370 --- /dev/null +++ b/atomix/proto/log.py @@ -0,0 +1,419 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# sources: atomix/log/log.proto +# plugin: python-betterproto +from dataclasses import dataclass +from datetime import datetime +from typing import AsyncGenerator, Optional + +import betterproto + +from atomix.proto import headers + + +class ResponseStatus(betterproto.Enum): + OK = 0 + NOOP = 1 + WRITE_LOCK = 2 + PRECONDITION_FAILED = 3 + + +class EventResponseType(betterproto.Enum): + NONE = 0 + APPENDED = 1 + REMOVED = 2 + + +@dataclass +class CreateRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + + +@dataclass +class CreateResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + + +@dataclass +class CloseRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + delete: bool = betterproto.bool_field(2) + + +@dataclass +class CloseResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + + +@dataclass +class ExistsRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + index: int = betterproto.uint64_field(2) + + +@dataclass +class ExistsResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + contains_index: bool = betterproto.bool_field(2) + + +@dataclass +class SizeRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + + +@dataclass +class SizeResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + size: int = betterproto.int32_field(2) + + +@dataclass +class AppendRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + index: int = betterproto.int64_field(2) + value: bytes = betterproto.bytes_field(3) + + +@dataclass +class AppendResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + status: "ResponseStatus" = betterproto.enum_field(2) + index: int = betterproto.int64_field(3) + timestamp: datetime = betterproto.message_field(4) + + +@dataclass +class GetRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + index: int = betterproto.int64_field(2) + + +@dataclass +class GetResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + index: int = betterproto.int64_field(2) + value: bytes = betterproto.bytes_field(3) + timestamp: datetime = betterproto.message_field(5) + + +@dataclass +class FirstEntryRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + + +@dataclass +class FirstEntryResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + index: int = betterproto.int64_field(2) + value: bytes = betterproto.bytes_field(3) + timestamp: datetime = betterproto.message_field(4) + + +@dataclass +class LastEntryRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + + +@dataclass +class LastEntryResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + index: int = betterproto.int64_field(2) + value: bytes = betterproto.bytes_field(3) + timestamp: datetime = betterproto.message_field(4) + + +@dataclass +class PrevEntryRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + index: int = betterproto.int64_field(2) + + +@dataclass +class PrevEntryResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + index: int = betterproto.int64_field(2) + value: bytes = betterproto.bytes_field(3) + timestamp: datetime = betterproto.message_field(4) + + +@dataclass +class NextEntryRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + index: int = betterproto.int64_field(2) + + +@dataclass +class NextEntryResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + index: int = betterproto.int64_field(2) + value: bytes = betterproto.bytes_field(3) + timestamp: datetime = betterproto.message_field(4) + + +@dataclass +class RemoveRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + index: int = betterproto.int64_field(2) + value: bytes = betterproto.bytes_field(3) + timestamp: datetime = betterproto.message_field(4) + + +@dataclass +class RemoveResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + status: "ResponseStatus" = betterproto.enum_field(2) + index: int = betterproto.int64_field(3) + previous_value: bytes = betterproto.bytes_field(4) + + +@dataclass +class ClearRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + + +@dataclass +class ClearResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + + +@dataclass +class EntriesRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + + +@dataclass +class EntriesResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + index: int = betterproto.int64_field(2) + value: bytes = betterproto.bytes_field(3) + timestamp: datetime = betterproto.message_field(4) + + +@dataclass +class EventRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + replay: bool = betterproto.bool_field(2) + index: int = betterproto.int64_field(3) + + +@dataclass +class EventResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + type: "EventResponseType" = betterproto.enum_field(2) + index: int = betterproto.int64_field(3) + value: bytes = betterproto.bytes_field(4) + timestamp: datetime = betterproto.message_field(5) + + +class LogServiceStub(betterproto.ServiceStub): + """LogService log service""" + + async def create( + self, *, header: Optional[headers.RequestHeader] = None + ) -> CreateResponse: + """Create creates a log""" + + request = CreateRequest() + if header is not None: + request.header = header + + return await self._unary_unary( + "/atomix.log.LogService/Create", request, CreateResponse, + ) + + async def close( + self, *, header: Optional[headers.RequestHeader] = None, delete: bool = False + ) -> CloseResponse: + """Close closes a log""" + + request = CloseRequest() + if header is not None: + request.header = header + request.delete = delete + + return await self._unary_unary( + "/atomix.log.LogService/Close", request, CloseResponse, + ) + + async def size( + self, *, header: Optional[headers.RequestHeader] = None + ) -> SizeResponse: + """Size returns the size of the log""" + + request = SizeRequest() + if header is not None: + request.header = header + + return await self._unary_unary( + "/atomix.log.LogService/Size", request, SizeResponse, + ) + + async def exists( + self, *, header: Optional[headers.RequestHeader] = None, index: int = 0 + ) -> ExistsResponse: + """Exists checks whether an index exists in the log""" + + request = ExistsRequest() + if header is not None: + request.header = header + request.index = index + + return await self._unary_unary( + "/atomix.log.LogService/Exists", request, ExistsResponse, + ) + + async def append( + self, + *, + header: Optional[headers.RequestHeader] = None, + index: int = 0, + value: bytes = b"", + ) -> AppendResponse: + """Appends appends an entry into the log""" + + request = AppendRequest() + if header is not None: + request.header = header + request.index = index + request.value = value + + return await self._unary_unary( + "/atomix.log.LogService/Append", request, AppendResponse, + ) + + async def get( + self, *, header: Optional[headers.RequestHeader] = None, index: int = 0 + ) -> GetResponse: + """Get gets the entry for an index""" + + request = GetRequest() + if header is not None: + request.header = header + request.index = index + + return await self._unary_unary( + "/atomix.log.LogService/Get", request, GetResponse, + ) + + async def first_entry( + self, *, header: Optional[headers.RequestHeader] = None + ) -> FirstEntryResponse: + """FirstEntry gets the first entry in the log""" + + request = FirstEntryRequest() + if header is not None: + request.header = header + + return await self._unary_unary( + "/atomix.log.LogService/FirstEntry", request, FirstEntryResponse, + ) + + async def last_entry( + self, *, header: Optional[headers.RequestHeader] = None + ) -> LastEntryResponse: + """LastEntry gets the last entry in the log""" + + request = LastEntryRequest() + if header is not None: + request.header = header + + return await self._unary_unary( + "/atomix.log.LogService/LastEntry", request, LastEntryResponse, + ) + + async def prev_entry( + self, *, header: Optional[headers.RequestHeader] = None, index: int = 0 + ) -> PrevEntryResponse: + """PrevEntry gets the previous entry in the log""" + + request = PrevEntryRequest() + if header is not None: + request.header = header + request.index = index + + return await self._unary_unary( + "/atomix.log.LogService/PrevEntry", request, PrevEntryResponse, + ) + + async def next_entry( + self, *, header: Optional[headers.RequestHeader] = None, index: int = 0 + ) -> NextEntryResponse: + """NextEntry gets the next entry in the log""" + + request = NextEntryRequest() + if header is not None: + request.header = header + request.index = index + + return await self._unary_unary( + "/atomix.log.LogService/NextEntry", request, NextEntryResponse, + ) + + async def remove( + self, + *, + header: Optional[headers.RequestHeader] = None, + index: int = 0, + value: bytes = b"", + timestamp: Optional[datetime] = None, + ) -> RemoveResponse: + """Remove removes an entry from the log""" + + request = RemoveRequest() + if header is not None: + request.header = header + request.index = index + request.value = value + if timestamp is not None: + request.timestamp = timestamp + + return await self._unary_unary( + "/atomix.log.LogService/Remove", request, RemoveResponse, + ) + + async def clear( + self, *, header: Optional[headers.RequestHeader] = None + ) -> ClearResponse: + """Clear removes all entries from the log""" + + request = ClearRequest() + if header is not None: + request.header = header + + return await self._unary_unary( + "/atomix.log.LogService/Clear", request, ClearResponse, + ) + + async def events( + self, + *, + header: Optional[headers.RequestHeader] = None, + replay: bool = False, + index: int = 0, + ) -> AsyncGenerator[EventResponse, None]: + """Events listens for change events""" + + request = EventRequest() + if header is not None: + request.header = header + request.replay = replay + request.index = index + + async for response in self._unary_stream( + "/atomix.log.LogService/Events", request, EventResponse, + ): + yield response + + async def entries( + self, *, header: Optional[headers.RequestHeader] = None + ) -> AsyncGenerator[EntriesResponse, None]: + """Entries lists all entries in the log""" + + request = EntriesRequest() + if header is not None: + request.header = header + + async for response in self._unary_stream( + "/atomix.log.LogService/Entries", request, EntriesResponse, + ): + yield response diff --git a/atomix/proto/map.py b/atomix/proto/map.py new file mode 100644 index 0000000..0a3662c --- /dev/null +++ b/atomix/proto/map.py @@ -0,0 +1,381 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# sources: atomix/map/map.proto +# plugin: python-betterproto +from dataclasses import dataclass +from datetime import datetime, timedelta +from typing import AsyncGenerator, Optional + +import betterproto + +from atomix.proto import headers + + +class ResponseStatus(betterproto.Enum): + OK = 0 + NOOP = 1 + WRITE_LOCK = 2 + PRECONDITION_FAILED = 3 + + +class EventResponseType(betterproto.Enum): + NONE = 0 + INSERTED = 1 + UPDATED = 2 + REMOVED = 3 + + +@dataclass +class CreateRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + + +@dataclass +class CreateResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + + +@dataclass +class CloseRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + delete: bool = betterproto.bool_field(2) + + +@dataclass +class CloseResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + + +@dataclass +class ExistsRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + key: str = betterproto.string_field(2) + + +@dataclass +class ExistsResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + contains_key: bool = betterproto.bool_field(2) + + +@dataclass +class SizeRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + + +@dataclass +class SizeResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + size: int = betterproto.int32_field(2) + + +@dataclass +class PutRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + key: str = betterproto.string_field(2) + value: bytes = betterproto.bytes_field(3) + version: int = betterproto.int64_field(4) + ttl: timedelta = betterproto.message_field(5) + + +@dataclass +class PutResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + status: "ResponseStatus" = betterproto.enum_field(2) + created: datetime = betterproto.message_field(3) + updated: datetime = betterproto.message_field(4) + previous_value: bytes = betterproto.bytes_field(5) + previous_version: int = betterproto.int64_field(6) + + +@dataclass +class ReplaceRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + key: str = betterproto.string_field(2) + previous_value: bytes = betterproto.bytes_field(3) + previous_version: int = betterproto.int64_field(4) + new_value: bytes = betterproto.bytes_field(5) + ttl: timedelta = betterproto.message_field(6) + + +@dataclass +class ReplaceResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + status: "ResponseStatus" = betterproto.enum_field(2) + created: datetime = betterproto.message_field(3) + updated: datetime = betterproto.message_field(4) + previous_value: bytes = betterproto.bytes_field(5) + previous_version: int = betterproto.int64_field(6) + + +@dataclass +class GetRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + key: str = betterproto.string_field(2) + + +@dataclass +class GetResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + value: bytes = betterproto.bytes_field(2) + version: int = betterproto.int64_field(3) + created: datetime = betterproto.message_field(4) + updated: datetime = betterproto.message_field(5) + + +@dataclass +class RemoveRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + key: str = betterproto.string_field(2) + value: bytes = betterproto.bytes_field(3) + version: int = betterproto.int64_field(4) + created: datetime = betterproto.message_field(5) + updated: datetime = betterproto.message_field(6) + + +@dataclass +class RemoveResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + status: "ResponseStatus" = betterproto.enum_field(2) + previous_value: bytes = betterproto.bytes_field(3) + previous_version: int = betterproto.int64_field(4) + + +@dataclass +class ClearRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + + +@dataclass +class ClearResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + + +@dataclass +class EntriesRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + + +@dataclass +class EntriesResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + key: str = betterproto.string_field(2) + value: bytes = betterproto.bytes_field(3) + version: int = betterproto.int64_field(4) + created: datetime = betterproto.message_field(5) + updated: datetime = betterproto.message_field(6) + + +@dataclass +class EventRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + replay: bool = betterproto.bool_field(2) + key: str = betterproto.string_field(3) + + +@dataclass +class EventResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + type: "EventResponseType" = betterproto.enum_field(2) + key: str = betterproto.string_field(3) + value: bytes = betterproto.bytes_field(4) + version: int = betterproto.int64_field(5) + created: datetime = betterproto.message_field(6) + updated: datetime = betterproto.message_field(7) + + +class MapServiceStub(betterproto.ServiceStub): + """MapService implements a distributed map""" + + async def create( + self, *, header: Optional[headers.RequestHeader] = None + ) -> CreateResponse: + """Create creates an indexed map""" + + request = CreateRequest() + if header is not None: + request.header = header + + return await self._unary_unary( + "/atomix.map.MapService/Create", request, CreateResponse, + ) + + async def close( + self, *, header: Optional[headers.RequestHeader] = None, delete: bool = False + ) -> CloseResponse: + """Close closes an indexed map""" + + request = CloseRequest() + if header is not None: + request.header = header + request.delete = delete + + return await self._unary_unary( + "/atomix.map.MapService/Close", request, CloseResponse, + ) + + async def size( + self, *, header: Optional[headers.RequestHeader] = None + ) -> SizeResponse: + """Size returns the size of the map""" + + request = SizeRequest() + if header is not None: + request.header = header + + return await self._unary_unary( + "/atomix.map.MapService/Size", request, SizeResponse, + ) + + async def exists( + self, *, header: Optional[headers.RequestHeader] = None, key: str = "" + ) -> ExistsResponse: + """Exists checks whether a key exists in the map""" + + request = ExistsRequest() + if header is not None: + request.header = header + request.key = key + + return await self._unary_unary( + "/atomix.map.MapService/Exists", request, ExistsResponse, + ) + + async def put( + self, + *, + header: Optional[headers.RequestHeader] = None, + key: str = "", + value: bytes = b"", + version: int = 0, + ttl: Optional[timedelta] = None, + ) -> PutResponse: + """Put puts an entry into the map""" + + request = PutRequest() + if header is not None: + request.header = header + request.key = key + request.value = value + request.version = version + if ttl is not None: + request.ttl = ttl + + return await self._unary_unary( + "/atomix.map.MapService/Put", request, PutResponse, + ) + + async def replace( + self, + *, + header: Optional[headers.RequestHeader] = None, + key: str = "", + previous_value: bytes = b"", + previous_version: int = 0, + new_value: bytes = b"", + ttl: Optional[timedelta] = None, + ) -> ReplaceResponse: + """ + Replace performs a check-and-set operation on an entry in the map + """ + + request = ReplaceRequest() + if header is not None: + request.header = header + request.key = key + request.previous_value = previous_value + request.previous_version = previous_version + request.new_value = new_value + if ttl is not None: + request.ttl = ttl + + return await self._unary_unary( + "/atomix.map.MapService/Replace", request, ReplaceResponse, + ) + + async def get( + self, *, header: Optional[headers.RequestHeader] = None, key: str = "" + ) -> GetResponse: + """Get gets the entry for a key""" + + request = GetRequest() + if header is not None: + request.header = header + request.key = key + + return await self._unary_unary( + "/atomix.map.MapService/Get", request, GetResponse, + ) + + async def remove( + self, + *, + header: Optional[headers.RequestHeader] = None, + key: str = "", + value: bytes = b"", + version: int = 0, + created: Optional[datetime] = None, + updated: Optional[datetime] = None, + ) -> RemoveResponse: + """Remove removes an entry from the map""" + + request = RemoveRequest() + if header is not None: + request.header = header + request.key = key + request.value = value + request.version = version + if created is not None: + request.created = created + if updated is not None: + request.updated = updated + + return await self._unary_unary( + "/atomix.map.MapService/Remove", request, RemoveResponse, + ) + + async def clear( + self, *, header: Optional[headers.RequestHeader] = None + ) -> ClearResponse: + """Clear removes all entries from the map""" + + request = ClearRequest() + if header is not None: + request.header = header + + return await self._unary_unary( + "/atomix.map.MapService/Clear", request, ClearResponse, + ) + + async def events( + self, + *, + header: Optional[headers.RequestHeader] = None, + replay: bool = False, + key: str = "", + ) -> AsyncGenerator[EventResponse, None]: + """Events listens for change events""" + + request = EventRequest() + if header is not None: + request.header = header + request.replay = replay + request.key = key + + async for response in self._unary_stream( + "/atomix.map.MapService/Events", request, EventResponse, + ): + yield response + + async def entries( + self, *, header: Optional[headers.RequestHeader] = None + ) -> AsyncGenerator[EntriesResponse, None]: + """Entries lists all entries in the map""" + + request = EntriesRequest() + if header is not None: + request.header = header + + async for response in self._unary_stream( + "/atomix.map.MapService/Entries", request, EntriesResponse, + ): + yield response diff --git a/atomix/proto/membership.py b/atomix/proto/membership.py new file mode 100644 index 0000000..b66e385 --- /dev/null +++ b/atomix/proto/membership.py @@ -0,0 +1,71 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# sources: atomix/membership/membership.proto +# plugin: python-betterproto +from dataclasses import dataclass +from typing import AsyncGenerator, List, Optional + +import betterproto +import grpclib + + +@dataclass +class GroupId(betterproto.Message): + """Membership group identifier""" + + name: str = betterproto.string_field(1) + namespace: str = betterproto.string_field(2) + + +@dataclass +class MemberId(betterproto.Message): + """Member identifier""" + + name: str = betterproto.string_field(1) + namespace: str = betterproto.string_field(2) + + +@dataclass +class Member(betterproto.Message): + """Member is a membership member""" + + id: "MemberId" = betterproto.message_field(1) + host: str = betterproto.string_field(2) + port: int = betterproto.int32_field(3) + + +@dataclass +class JoinGroupRequest(betterproto.Message): + """JoinGroupRequest is a request to join a membership group""" + + member: "Member" = betterproto.message_field(1) + group_id: "GroupId" = betterproto.message_field(2) + + +@dataclass +class JoinGroupResponse(betterproto.Message): + """JoinGroupResponse is a response to joining a membership group""" + + group_id: "GroupId" = betterproto.message_field(1) + members: List["Member"] = betterproto.message_field(2) + + +class MembershipServiceStub(betterproto.ServiceStub): + """Atomix membership service""" + + async def join_group( + self, *, member: Optional["Member"] = None, group_id: Optional["GroupId"] = None + ) -> AsyncGenerator[JoinGroupResponse, None]: + """Joins a member to a cluster""" + + request = JoinGroupRequest() + if member is not None: + request.member = member + if group_id is not None: + request.group_id = group_id + + async for response in self._unary_stream( + "/atomix.membership.MembershipService/JoinGroup", + request, + JoinGroupResponse, + ): + yield response diff --git a/atomix/proto/primitive.py b/atomix/proto/primitive.py new file mode 100644 index 0000000..56d2966 --- /dev/null +++ b/atomix/proto/primitive.py @@ -0,0 +1,194 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# sources: atomix/primitive/primitive.proto, atomix/primitive/metadata.proto +# plugin: python-betterproto +from dataclasses import dataclass +from typing import List, Optional + +import betterproto + +from atomix.proto import database + + +class PrimitiveType(betterproto.Enum): + """PrimitiveType is a primitive type""" + + UNKNOWN = 0 + COUNTER = 1 + ELECTION = 2 + INDEXED_MAP = 3 + LEADER_LATCH = 4 + LIST = 5 + LOCK = 6 + LOG = 7 + MAP = 8 + SET = 9 + VALUE = 10 + + +@dataclass +class PrimitiveId(betterproto.Message): + """Namespaced primitive identifier""" + + name: str = betterproto.string_field(1) + namespace: str = betterproto.string_field(2) + + +@dataclass +class CreatePrimitiveRequest(betterproto.Message): + """CreatePrimitiveRequest is a request to create a primitive""" + + database: database.DatabaseId = betterproto.message_field(1) + primitive: "PrimitiveId" = betterproto.message_field(2) + type: "PrimitiveType" = betterproto.enum_field(3) + + +@dataclass +class CreatePrimitiveResponse(betterproto.Message): + """CreatePrimitiveResponse is a response for creating a primitive""" + + primitive: "PrimitiveMetadata" = betterproto.message_field(1) + + +@dataclass +class GetPrimitiveRequest(betterproto.Message): + """GetPrimitiveRequest is a request for primitive metadata""" + + database: database.DatabaseId = betterproto.message_field(1) + primitive: "PrimitiveId" = betterproto.message_field(2) + + +@dataclass +class GetPrimitiveResponse(betterproto.Message): + """GetPrimitiveResponse is a response containing primitive metadata""" + + primitive: "PrimitiveMetadata" = betterproto.message_field(1) + + +@dataclass +class GetPrimitivesRequest(betterproto.Message): + """GetPrimitivesRequest is a request for primitive metadata""" + + database: database.DatabaseId = betterproto.message_field(1) + primitive: "PrimitiveId" = betterproto.message_field(2) + type: "PrimitiveType" = betterproto.enum_field(3) + + +@dataclass +class GetPrimitivesResponse(betterproto.Message): + """GetPrimitivesResponse is a response containing primitive metadata""" + + primitives: List["PrimitiveMetadata"] = betterproto.message_field(1) + + +@dataclass +class PrimitiveMetadata(betterproto.Message): + """PrimitiveMetadata indicates the type and name of a primitive""" + + database: database.DatabaseId = betterproto.message_field(1) + primitive: "PrimitiveId" = betterproto.message_field(2) + type: "PrimitiveType" = betterproto.enum_field(3) + + +@dataclass +class DeletePrimitiveRequest(betterproto.Message): + """DeletePrimitiveRequest is a request to delete a primitive""" + + database: database.DatabaseId = betterproto.message_field(1) + primitive: "PrimitiveId" = betterproto.message_field(2) + + +@dataclass +class DeletePrimitiveResponse(betterproto.Message): + """DeletePrimitiveResponse is a response for deleting a primitive""" + + primitive: "PrimitiveMetadata" = betterproto.message_field(1) + + +class PrimitiveServiceStub(betterproto.ServiceStub): + """ + PrimitiveService is a service for providing partition/primitive metadata + """ + + async def create_primitive( + self, + *, + database: Optional[database.DatabaseId] = None, + primitive: Optional["PrimitiveId"] = None, + type: "PrimitiveType" = 0, + ) -> CreatePrimitiveResponse: + """CreatePrimitive creates a new primitive""" + + request = CreatePrimitiveRequest() + if database is not None: + request.database = database + if primitive is not None: + request.primitive = primitive + request.type = type + + return await self._unary_unary( + "/atomix.primitive.PrimitiveService/CreatePrimitive", + request, + CreatePrimitiveResponse, + ) + + async def get_primitive( + self, + *, + database: Optional[database.DatabaseId] = None, + primitive: Optional["PrimitiveId"] = None, + ) -> GetPrimitiveResponse: + """GetPrimitive returns a primitive in the system""" + + request = GetPrimitiveRequest() + if database is not None: + request.database = database + if primitive is not None: + request.primitive = primitive + + return await self._unary_unary( + "/atomix.primitive.PrimitiveService/GetPrimitive", + request, + GetPrimitiveResponse, + ) + + async def get_primitives( + self, + *, + database: Optional[database.DatabaseId] = None, + primitive: Optional["PrimitiveId"] = None, + type: "PrimitiveType" = 0, + ) -> GetPrimitivesResponse: + """GetPrimitives returns a list of primitives in the system""" + + request = GetPrimitivesRequest() + if database is not None: + request.database = database + if primitive is not None: + request.primitive = primitive + request.type = type + + return await self._unary_unary( + "/atomix.primitive.PrimitiveService/GetPrimitives", + request, + GetPrimitivesResponse, + ) + + async def delete_primitive( + self, + *, + database: Optional[database.DatabaseId] = None, + primitive: Optional["PrimitiveId"] = None, + ) -> DeletePrimitiveResponse: + """DeletePrimitive deletes a primitive""" + + request = DeletePrimitiveRequest() + if database is not None: + request.database = database + if primitive is not None: + request.primitive = primitive + + return await self._unary_unary( + "/atomix.primitive.PrimitiveService/DeletePrimitive", + request, + DeletePrimitiveResponse, + ) diff --git a/atomix/proto/session.py b/atomix/proto/session.py new file mode 100644 index 0000000..a0301cd --- /dev/null +++ b/atomix/proto/session.py @@ -0,0 +1,93 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# sources: atomix/session/session.proto +# plugin: python-betterproto +from dataclasses import dataclass +from datetime import timedelta +from typing import Optional + +import betterproto + +from atomix.proto import headers + + +@dataclass +class OpenSessionRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + timeout: timedelta = betterproto.message_field(2) + + +@dataclass +class OpenSessionResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + + +@dataclass +class KeepAliveRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + + +@dataclass +class KeepAliveResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + + +@dataclass +class CloseSessionRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + delete: bool = betterproto.bool_field(2) + + +@dataclass +class CloseSessionResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + + +class SessionServiceStub(betterproto.ServiceStub): + """Session service""" + + async def open_session( + self, + *, + header: Optional[headers.RequestHeader] = None, + timeout: Optional[timedelta] = None, + ) -> OpenSessionResponse: + """OpenSession opens a new session""" + + request = OpenSessionRequest() + if header is not None: + request.header = header + if timeout is not None: + request.timeout = timeout + + return await self._unary_unary( + "/atomix.session.SessionService/OpenSession", request, OpenSessionResponse, + ) + + async def keep_alive( + self, *, header: Optional[headers.RequestHeader] = None + ) -> KeepAliveResponse: + """KeepAlive keeps a session alive""" + + request = KeepAliveRequest() + if header is not None: + request.header = header + + return await self._unary_unary( + "/atomix.session.SessionService/KeepAlive", request, KeepAliveResponse, + ) + + async def close_session( + self, *, header: Optional[headers.RequestHeader] = None, delete: bool = False + ) -> CloseSessionResponse: + """CloseSession closes a session""" + + request = CloseSessionRequest() + if header is not None: + request.header = header + request.delete = delete + + return await self._unary_unary( + "/atomix.session.SessionService/CloseSession", + request, + CloseSessionResponse, + ) diff --git a/atomix/proto/set.py b/atomix/proto/set.py new file mode 100644 index 0000000..1e4e336 --- /dev/null +++ b/atomix/proto/set.py @@ -0,0 +1,253 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# sources: atomix/set/set.proto +# plugin: python-betterproto +from dataclasses import dataclass +from typing import AsyncGenerator, Optional + +import betterproto + +from atomix.proto import headers + + +class ResponseStatus(betterproto.Enum): + OK = 0 + NOOP = 1 + WRITE_LOCK = 2 + + +class EventResponseType(betterproto.Enum): + NONE = 0 + ADDED = 1 + REMOVED = 2 + + +@dataclass +class CreateRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + + +@dataclass +class CreateResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + + +@dataclass +class CloseRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + delete: bool = betterproto.bool_field(2) + + +@dataclass +class CloseResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + + +@dataclass +class SizeRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + + +@dataclass +class SizeResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + size: int = betterproto.int32_field(2) + + +@dataclass +class ContainsRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + value: str = betterproto.string_field(2) + + +@dataclass +class ContainsResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + contains: bool = betterproto.bool_field(2) + + +@dataclass +class AddRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + value: str = betterproto.string_field(2) + + +@dataclass +class AddResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + status: "ResponseStatus" = betterproto.enum_field(2) + added: bool = betterproto.bool_field(3) + + +@dataclass +class RemoveRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + value: str = betterproto.string_field(2) + + +@dataclass +class RemoveResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + status: "ResponseStatus" = betterproto.enum_field(2) + removed: bool = betterproto.bool_field(3) + + +@dataclass +class ClearRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + + +@dataclass +class ClearResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + + +@dataclass +class EventRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + replay: bool = betterproto.bool_field(2) + + +@dataclass +class EventResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + type: "EventResponseType" = betterproto.enum_field(2) + value: str = betterproto.string_field(3) + + +@dataclass +class IterateRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + + +@dataclass +class IterateResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + value: str = betterproto.string_field(2) + + +class SetServiceStub(betterproto.ServiceStub): + """Set service""" + + async def create( + self, *, header: Optional[headers.RequestHeader] = None + ) -> CreateResponse: + """Create creates a set session""" + + request = CreateRequest() + if header is not None: + request.header = header + + return await self._unary_unary( + "/atomix.set.SetService/Create", request, CreateResponse, + ) + + async def close( + self, *, header: Optional[headers.RequestHeader] = None, delete: bool = False + ) -> CloseResponse: + """Close closes a set""" + + request = CloseRequest() + if header is not None: + request.header = header + request.delete = delete + + return await self._unary_unary( + "/atomix.set.SetService/Close", request, CloseResponse, + ) + + async def size( + self, *, header: Optional[headers.RequestHeader] = None + ) -> SizeResponse: + """Size gets the number of elements in the set""" + + request = SizeRequest() + if header is not None: + request.header = header + + return await self._unary_unary( + "/atomix.set.SetService/Size", request, SizeResponse, + ) + + async def contains( + self, *, header: Optional[headers.RequestHeader] = None, value: str = "" + ) -> ContainsResponse: + """Contains returns whether the set contains a value""" + + request = ContainsRequest() + if header is not None: + request.header = header + request.value = value + + return await self._unary_unary( + "/atomix.set.SetService/Contains", request, ContainsResponse, + ) + + async def add( + self, *, header: Optional[headers.RequestHeader] = None, value: str = "" + ) -> AddResponse: + """Add adds a value to the set""" + + request = AddRequest() + if header is not None: + request.header = header + request.value = value + + return await self._unary_unary( + "/atomix.set.SetService/Add", request, AddResponse, + ) + + async def remove( + self, *, header: Optional[headers.RequestHeader] = None, value: str = "" + ) -> RemoveResponse: + """Remove removes a value from the set""" + + request = RemoveRequest() + if header is not None: + request.header = header + request.value = value + + return await self._unary_unary( + "/atomix.set.SetService/Remove", request, RemoveResponse, + ) + + async def clear( + self, *, header: Optional[headers.RequestHeader] = None + ) -> ClearResponse: + """Clear removes all values from the set""" + + request = ClearRequest() + if header is not None: + request.header = header + + return await self._unary_unary( + "/atomix.set.SetService/Clear", request, ClearResponse, + ) + + async def events( + self, *, header: Optional[headers.RequestHeader] = None, replay: bool = False + ) -> AsyncGenerator[EventResponse, None]: + """Events listens for set change events""" + + request = EventRequest() + if header is not None: + request.header = header + request.replay = replay + + async for response in self._unary_stream( + "/atomix.set.SetService/Events", request, EventResponse, + ): + yield response + + async def iterate( + self, *, header: Optional[headers.RequestHeader] = None + ) -> AsyncGenerator[IterateResponse, None]: + """Iterate iterates through all values in the set""" + + request = IterateRequest() + if header is not None: + request.header = header + + async for response in self._unary_stream( + "/atomix.set.SetService/Iterate", request, IterateResponse, + ): + yield response diff --git a/atomix/proto/value.py b/atomix/proto/value.py new file mode 100644 index 0000000..9957945 --- /dev/null +++ b/atomix/proto/value.py @@ -0,0 +1,155 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# sources: atomix/value/value.proto +# plugin: python-betterproto +from dataclasses import dataclass +from typing import AsyncGenerator, Optional + +import betterproto + +from atomix.proto import headers + + +class EventResponseType(betterproto.Enum): + UPDATED = 0 + + +@dataclass +class CreateRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + + +@dataclass +class CreateResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + + +@dataclass +class CloseRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + delete: bool = betterproto.bool_field(2) + + +@dataclass +class CloseResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + + +@dataclass +class GetRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + + +@dataclass +class GetResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + value: bytes = betterproto.bytes_field(2) + version: int = betterproto.uint64_field(3) + + +@dataclass +class SetRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + expect_version: int = betterproto.uint64_field(2) + expect_value: bytes = betterproto.bytes_field(3) + value: bytes = betterproto.bytes_field(4) + + +@dataclass +class SetResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + version: int = betterproto.uint64_field(2) + succeeded: bool = betterproto.bool_field(3) + + +@dataclass +class EventRequest(betterproto.Message): + header: headers.RequestHeader = betterproto.message_field(1) + + +@dataclass +class EventResponse(betterproto.Message): + header: headers.ResponseHeader = betterproto.message_field(1) + type: "EventResponseType" = betterproto.enum_field(2) + previous_value: bytes = betterproto.bytes_field(3) + previous_version: int = betterproto.uint64_field(4) + new_value: bytes = betterproto.bytes_field(5) + new_version: int = betterproto.uint64_field(6) + + +class ValueServiceStub(betterproto.ServiceStub): + """ValueService implements a distributed atomic value""" + + async def create( + self, *, header: Optional[headers.RequestHeader] = None + ) -> CreateResponse: + """Create creates a new value session""" + + request = CreateRequest() + if header is not None: + request.header = header + + return await self._unary_unary( + "/atomix.value.ValueService/Create", request, CreateResponse, + ) + + async def close( + self, *, header: Optional[headers.RequestHeader] = None, delete: bool = False + ) -> CloseResponse: + """Close closes the value session""" + + request = CloseRequest() + if header is not None: + request.header = header + request.delete = delete + + return await self._unary_unary( + "/atomix.value.ValueService/Close", request, CloseResponse, + ) + + async def set( + self, + *, + header: Optional[headers.RequestHeader] = None, + expect_version: int = 0, + expect_value: bytes = b"", + value: bytes = b"", + ) -> SetResponse: + """Set sets the value""" + + request = SetRequest() + if header is not None: + request.header = header + request.expect_version = expect_version + request.expect_value = expect_value + request.value = value + + return await self._unary_unary( + "/atomix.value.ValueService/Set", request, SetResponse, + ) + + async def get( + self, *, header: Optional[headers.RequestHeader] = None + ) -> GetResponse: + """Get gets the value""" + + request = GetRequest() + if header is not None: + request.header = header + + return await self._unary_unary( + "/atomix.value.ValueService/Get", request, GetResponse, + ) + + async def events( + self, *, header: Optional[headers.RequestHeader] = None + ) -> AsyncGenerator[EventResponse, None]: + """Events listens for value change events""" + + request = EventRequest() + if header is not None: + request.header = header + + async for response in self._unary_stream( + "/atomix.value.ValueService/Events", request, EventResponse, + ): + yield response diff --git a/build/compile-protos.sh b/build/compile-protos.sh new file mode 100755 index 0000000..507fc11 --- /dev/null +++ b/build/compile-protos.sh @@ -0,0 +1,22 @@ +#!/bin/sh + +proto_imports="./build/api/proto:${GOPATH}/src/github.com/gogo/protobuf/protobuf:${GOPATH}/src/github.com/gogo/protobuf:${GOPATH}/src" + +git clone --branch master https://github.com/atomix/api.git build/api + +mkdir -p build/gen + +protoc -I=$proto_imports --python_betterproto_out=build/gen \ + build/api/proto/atomix/membership/*.proto \ + build/api/proto/atomix/database/*.proto \ + build/api/proto/atomix/primitive/*.proto \ + build/api/proto/atomix/session/*.proto \ + build/api/proto/atomix/election/*.proto \ + build/api/proto/atomix/indexedmap/*.proto \ + build/api/proto/atomix/leader/*.proto \ + build/api/proto/atomix/list/*.proto \ + build/api/proto/atomix/lock/*.proto \ + build/api/proto/atomix/log/*.proto \ + build/api/proto/atomix/map/*.proto \ + build/api/proto/atomix/set/*.proto \ + build/api/proto/atomix/value/*.proto diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..671dc91 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +betterproto \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..86dc00d --- /dev/null +++ b/setup.py @@ -0,0 +1,15 @@ +from setuptools import setup, find_packages + +with open('LICENSE') as f: + license = f.read() + +setup( + name='atomix-py', + version='0.1.0', + description='Python client for Atomix Cloud', + author='Jordan Halterman', + author_email='jordan.halterman@gmail.com', + url='https://cloud.atomix.io', + license=license, + packages=find_packages(exclude=('tests', 'docs')) +) \ No newline at end of file