Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Interface for the new database. #2799

Merged
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
142 changes: 142 additions & 0 deletions openslides_backend/services/database/commands.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
from typing import Any, Union

import simplejson as json

from ...shared.filters import _FilterBase as FilterInterface
from ...shared.interfaces.write_request import WriteRequest
from ...shared.patterns import Collection


class GetManyRequest:
"""
Encapsulates a single GetManyRequest to be used for get_many requests to the
datastore.
"""

mapped_fields: set[str]

def __init__(
self,
collection: Collection,
ids: list[int],
mapped_fields: set[str] | list[str] | None = None,
) -> None:
self.collection = collection
self.ids = ids
if isinstance(mapped_fields, list):
self.mapped_fields = set(mapped_fields)
else:
self.mapped_fields = mapped_fields or set()

def __eq__(self, other: object) -> bool:
return (
isinstance(other, GetManyRequest)
and self.collection == other.collection
and self.ids == other.ids
and self.mapped_fields == other.mapped_fields
)

def __repr__(self) -> str:
return str(
{
"collection": self.collection,
"ids": self.ids,
"mapped_fields": list(self.mapped_fields),
}
)


CommandData = dict[str, Union[str, int, list[str]]]


class Command:
"""
Command is the base class for commands used by the Engine interface.

The property 'name' returns by default the name of the class converted to snake case.
"""

@property
def name(self) -> str:
name = type(self).__name__
return "".join(
["_" + char.lower() if char.isupper() else char for char in name]
).lstrip("_")

@property
def data(self) -> str | None:
return json.dumps(self.get_raw_data())

def get_raw_data(self) -> CommandData:
raise NotImplementedError()

def __eq__(self, other: object) -> bool:
return isinstance(other, Command) and self.data == other.data


class ReserveIds(Command):
"""
Reserve ids command
"""

def __init__(self, collection: Collection, amount: int) -> None:
self.collection = collection
self.amount = amount

def get_raw_data(self) -> CommandData:
return {"collection": str(self.collection), "amount": self.amount}


class Write(Command):
"""
Write command
"""

def __init__(self, write_requests: list[WriteRequest]) -> None:
self.write_requests = write_requests

@property
def data(self) -> str:
class WriteRequestJSONEncoder(json.JSONEncoder):
def default(self, o: Any) -> Any:
if isinstance(o, WriteRequest):
return o.__dict__
if isinstance(o, FilterInterface):
return o.to_dict()
return super().default(o)

return json.dumps(self.write_requests, cls=WriteRequestJSONEncoder)


class WriteWithoutEvents(Write):
"""
WriteWithoutEvents command, same as Write, but on separate route
"""


class TruncateDb(Command):
"""
TruncateDb command. Does not need data.
"""

@property
def data(self) -> None:
pass


class GetEverything(Command):
"""
GetEverything command. Does not need data.
"""

def get_raw_data(self) -> CommandData:
return {}


class DeleteHistoryInformation(Command):
"""
DeleteHistoryInformation command. Does not need data.
"""

def get_raw_data(self) -> CommandData:
return {}
139 changes: 139 additions & 0 deletions openslides_backend/services/database/interface.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
from abc import abstractmethod
from collections.abc import Sequence
from typing import Any, ContextManager, Protocol, Union

from ...shared.filters import Filter
from ...shared.interfaces.write_request import WriteRequest
from ...shared.patterns import Collection, FullQualifiedId
from ...shared.typing import ModelMap
from .commands import GetManyRequest

PartialModel = dict[str, Any]


LockResult = Union[bool, list[str]]


MappedFieldsPerFqid = dict[FullQualifiedId, list[str]]


class Database(Protocol):
"""
Database defines the interface to the database.
"""

changed_models: ModelMap

@abstractmethod
def get_database_context(self) -> ContextManager[None]: ...

@abstractmethod
def apply_changed_model(
self, fqid: FullQualifiedId, instance: PartialModel, replace: bool = False
) -> None: ...

@abstractmethod
def get(
self,
fqid: FullQualifiedId,
mapped_fields: list[str],
position: int | None = None,
lock_result: LockResult = True,
use_changed_models: bool = True,
raise_exception: bool = True,
) -> PartialModel: ...

@abstractmethod
def get_many(
self,
get_many_requests: list[GetManyRequest],
position: int | None = None,
lock_result: LockResult = True,
use_changed_models: bool = True,
) -> dict[Collection, dict[int, PartialModel]]: ...

@abstractmethod
def get_all(
self,
collection: Collection,
mapped_fields: list[str],
lock_result: bool = True,
) -> dict[int, PartialModel]: ...

@abstractmethod
def filter(
self,
collection: Collection,
filter: Filter,
mapped_fields: list[str],
lock_result: bool = True,
use_changed_models: bool = True,
) -> dict[int, PartialModel]: ...

@abstractmethod
def exists(
self,
collection: Collection,
filter: Filter,
lock_result: bool = True,
use_changed_models: bool = True,
) -> bool: ...

@abstractmethod
def count(
self,
collection: Collection,
filter: Filter,
lock_result: bool = True,
use_changed_models: bool = True,
) -> int: ...

@abstractmethod
def min(
self,
collection: Collection,
filter: Filter,
field: str,
lock_result: bool = True,
use_changed_models: bool = True,
) -> int | None: ...

@abstractmethod
def max(
self,
collection: Collection,
filter: Filter,
field: str,
lock_result: bool = True,
use_changed_models: bool = True,
) -> int | None: ...

@abstractmethod
def history_information(self, fqids: list[str]) -> dict[str, list[dict]]: ...

@abstractmethod
def reserve_ids(self, collection: Collection, amount: int) -> Sequence[int]: ...

@abstractmethod
def reserve_id(self, collection: Collection) -> int: ...

@abstractmethod
def write(self, write_requests: list[WriteRequest] | WriteRequest) -> None: ...

@abstractmethod
def write_without_events(self, write_request: WriteRequest) -> None: ...

@abstractmethod
def truncate_db(self) -> None: ...

@abstractmethod
def is_deleted(self, fqid: FullQualifiedId) -> bool: ...

@abstractmethod
def reset(self, hard: bool = True) -> None: ...

@abstractmethod
def get_everything(self) -> dict[Collection, dict[int, PartialModel]]: ...

@abstractmethod
def delete_history_information(self) -> None: ...
Loading