updated Redis

This commit is contained in:
berkay 2025-01-13 19:05:25 +03:00
parent c4013943a1
commit 6d77c34252
12 changed files with 257 additions and 122 deletions

View File

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="VcsDirectoryMappings"> <component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
<mapping directory="$PROJECT_DIR$/wag-managment-api-service-version-4" vcs="Git" /> <mapping directory="$PROJECT_DIR$/wag-managment-api-service-version-4" vcs="Git" />
</component> </component>
</project> </project>

View File

@ -1,6 +1,7 @@
from pydantic import BaseModel from pydantic import BaseModel
from typing import List, Dict, Optional from typing import List, Dict, Optional
class EmailSendModel(BaseModel): class EmailSendModel(BaseModel):
subject: str subject: str
html: str = "" html: str = ""

View File

@ -6,3 +6,12 @@ class WagRedis:
REDIS_PASSWORD: str = "commercial_redis_password" REDIS_PASSWORD: str = "commercial_redis_password"
REDIS_PORT: int = 11222 REDIS_PORT: int = 11222
REDIS_DB: int = 0 REDIS_DB: int = 0
@classmethod
def as_dict(cls):
return dict(
host=WagRedis.REDIS_HOST,
password=WagRedis.REDIS_PASSWORD,
port=WagRedis.REDIS_PORT,
db=WagRedis.REDIS_DB,
)

View File

@ -1,3 +1,7 @@
class HostConfig: class HostConfig:
MAIN_HOST = "10.10.2.36" # http://10.10.2.36 MAIN_HOST = "10.10.2.36" # http://10.10.2.36
EMAIL_HOST = "10.10.2.34" # http://10.10.2.34 EMAIL_HOST = "10.10.2.34" # http://10.10.2.34
class MainConfig:
DATETIME_FORMAT = "YYYY-MM-DD HH:mm:ss Z"

View File

@ -8,10 +8,7 @@ email_sender = EmailSender(**EmailConfig.as_dict())
class EmailService: class EmailService:
@classmethod @classmethod
def send_email( def send_email(cls, params: EmailSendModel) -> bool:
cls,
params : EmailSendModel
) -> bool:
if not EmailConfig.EMAIL_SEND: if not EmailConfig.EMAIL_SEND:
print("Email sending is disabled", params) print("Email sending is disabled", params)
return False return False

View File

@ -1,102 +1,33 @@
import json
import arrow import arrow
from typing import (
Any,
Optional,
List,
Dict,
)
from typing import Optional, List, Dict, Union
from AllConfigs.main import MainConfig
from ApiServiceRedis.Redis.conn import redis_cli from ApiServiceRedis.Redis.conn import redis_cli
from ApiServiceRedis.Redis.Models.base import RedisRow
from ApiServiceRedis.Redis.Models.response import RedisResponse from ApiServiceRedis.Redis.Models.response import RedisResponse
class RedisRow:
key: str | bytes
value: Any
delimiter: str = "*"
expires_at: Optional[str] = None
@classmethod
def merge(cls, set_values: list[str | bytes]):
for key, set_value in enumerate(set_values):
set_value = str(set_value) if isinstance(set_value, bytes) else set_value
cls.key += f"{set_value}" if key == len(set_values) - 1 else f"{set_value}{cls.delimiter}"
cls.key = cls.key.encode()
@classmethod
def regex(cls, list_keys: list[str | bytes]):
search_regex = ""
for key, list_key in enumerate(list_keys):
list_key = list_key.decode() if isinstance(list_key, bytes) else str(list_key)
if key == 0 and list_key:
search_regex += f"{list_key}{cls.delimiter}*"
elif key == len(list_keys) - 1 and list_key:
search_regex += f"*{cls.delimiter}{list_key}"
else:
if list_key:
search_regex += f"*{cls.delimiter}{list_key}{cls.delimiter}*"
return search_regex
@classmethod
def parse(cls):
return cls.key.split(cls.delimiter) if cls.key else []
@classmethod
def feed(cls, value: Any):
cls.value = json.dumps(value)
@classmethod
def modify(cls, add_dict):
value = cls.data or {}
cls.feed({**value, **add_dict})
@classmethod
def remove(cls, key):
value = cls.data or {}
value.pop(key)
cls.feed(value)
@property
def keys(self):
return self.key if isinstance(self.key, str) else self.key.decode()
@property
def redis_key(self):
return self.key if isinstance(self.key, bytes) else self.key.encode()
@property
def data(self):
return json.loads(self.value)
@property
def as_dict(self):
return {
"keys": self.keys,
"value": self.data,
}
class RedisActions: class RedisActions:
"""Class for handling Redis operations with JSON data."""
@classmethod @classmethod
def get_expiry_time(cls, expiry_kwargs: dict) -> int: def get_expiry_time(cls, expiry_kwargs: Dict[str, int]) -> int:
expiry_time = 0 """Calculate expiry time in seconds from kwargs."""
if "days" in expiry_kwargs: time_multipliers = {"days": 86400, "hours": 3600, "minutes": 60, "seconds": 1}
expiry_time += int(expiry_kwargs["days"]) * 24 * 60 * 60 return sum(
if "hours" in expiry_kwargs: int(expiry_kwargs.get(unit, 0)) * multiplier
expiry_time += int(expiry_kwargs["hours"]) * 60 * 60 for unit, multiplier in time_multipliers.items()
if "minutes" in expiry_kwargs: )
expiry_time += int(expiry_kwargs["minutes"]) * 60
if "seconds" in expiry_kwargs:
expiry_time += int(expiry_kwargs["seconds"])
return expiry_time
@classmethod @classmethod
def set_json( def set_json(
cls, list_keys: list[str | bytes], value: Optional[dict | list], expires: Optional[dict] = None cls,
): list_keys: List[Union[str, bytes]],
value: Optional[Union[Dict, List]],
expires: Optional[Dict[str, int]] = None,
) -> RedisResponse:
"""Set JSON value in Redis with optional expiry."""
redis_row = RedisRow() redis_row = RedisRow()
redis_row.merge(set_values=list_keys) redis_row.merge(set_values=list_keys)
redis_row.feed(value) redis_row.feed(value)
@ -108,9 +39,15 @@ class RedisActions:
time=expiry_time, time=expiry_time,
value=redis_row.value, value=redis_row.value,
) )
redis_row.expires_at = arrow.now().shift(seconds=expiry_time).format("YYYY-MM-DD HH:mm:ss") redis_row.expires_at = (
arrow.now()
.shift(seconds=expiry_time)
.format(MainConfig.DATETIME_FORMAT)
)
else: else:
redis_cli.set(name=redis_row.redis_key, value=redis_row.value) redis_cli.set(name=redis_row.redis_key, value=redis_row.value)
return RedisResponse( return RedisResponse(
status=True, status=True,
message="Value is set successfully.", message="Value is set successfully.",
@ -124,21 +61,31 @@ class RedisActions:
) )
@classmethod @classmethod
def get_json(cls, list_keys: list[str | bytes]): def resolve_expires_at(cls, redis_row: RedisRow) -> str:
"""Resolve expiry time for Redis key."""
expiry_time = redis_cli.ttl(redis_row.redis_key)
if expiry_time == -1:
return "Key has no expiry time."
return arrow.now().shift(seconds=expiry_time).format(MainConfig.DATETIME_FORMAT)
@classmethod
def get_json(cls, list_keys: List[Union[str, bytes]]) -> RedisResponse:
"""Get JSON values from Redis using pattern matching."""
try: try:
redis_row = RedisRow() list_of_rows = []
json_get = redis_cli.get(redis_row.regex(list_keys=list_keys)) regex = RedisRow.regex(list_keys=list_keys)
redis_row.key = json_get json_get = redis_cli.scan_iter(match=regex)
if not json_get:
return RedisResponse( for row in list(json_get):
status=False, redis_row = RedisRow()
message="Value is not get successfully.", redis_row.set_key(key=row)
error="Value is not found in the redis.", redis_row.expires_at = cls.resolve_expires_at(redis_row=redis_row)
) redis_row.feed(redis_cli.get(redis_row.redis_key))
list_of_rows.append(redis_row)
return RedisResponse( return RedisResponse(
status=True, status=True,
message="Value is get successfully.", message="Value is get successfully.",
data=json.loads(json_get), data=list_of_rows,
) )
except Exception as e: except Exception as e:
return RedisResponse( return RedisResponse(

View File

@ -0,0 +1,99 @@
import json
from typing import Union, Dict, List, Optional, Any
class RedisRow:
"""Class for handling Redis key-value operations with structured data."""
key: Union[str, bytes]
value: Any
delimiter: str = ":"
expires_at: Optional[str] = None
@classmethod
def merge(cls, set_values: List[Union[str, bytes]]) -> None:
"""Merge list of values into a single delimited key."""
cls.key = ""
for key, set_value in enumerate(set_values):
set_value = (
set_value.decode() if isinstance(set_value, bytes) else str(set_value)
)
cls.key += (
f"{set_value}"
if key == len(set_values) - 1
else f"{set_value}{cls.delimiter}"
)
cls.key = cls.key.encode()
@classmethod
def regex(cls, list_keys: List[Union[str, bytes]]) -> str:
"""Generate Redis search pattern from list of keys."""
search_regex = ""
for key, list_key in enumerate(list_keys):
if not list_key:
continue
list_key = (
list_key.decode() if isinstance(list_key, bytes) else str(list_key)
)
if key == 0:
search_regex += f"{list_key}{cls.delimiter}*"
elif key == len(list_keys) - 1:
search_regex += f"*{cls.delimiter}{list_key}"
else:
search_regex += f"*{cls.delimiter}{list_key}{cls.delimiter}*"
return search_regex
@classmethod
def parse(cls) -> List[str]:
"""Parse the key into its component parts."""
return cls.key.split(cls.delimiter) if cls.key else []
@classmethod
def feed(cls, value: Union[bytes, Dict, List]) -> None:
"""Convert and store value in JSON format."""
if isinstance(value, (dict, list)):
cls.value = json.dumps(value)
else:
cls.value = json.dumps(json.loads(value.decode()))
@classmethod
def modify(cls, add_dict: Dict) -> None:
"""Modify existing data by merging with new dictionary."""
value = cls.data or {}
cls.feed({**value, **add_dict})
@classmethod
def remove(cls, key: str) -> None:
"""Remove a key from the stored dictionary."""
value = cls.data or {}
value.pop(key)
cls.feed(value)
@property
def keys(self) -> str:
"""Get key as string."""
return self.key.decode() if isinstance(self.key, bytes) else self.key
@classmethod
def set_key(cls, key: Union[str, bytes]) -> None:
"""Set key ensuring bytes format."""
cls.key = key if isinstance(key, bytes) else key.encode()
@property
def redis_key(self) -> bytes:
"""Get key in bytes format for Redis operations."""
return self.key if isinstance(self.key, bytes) else self.key.encode()
@property
def data(self) -> Union[Dict, List]:
"""Get stored value as Python object."""
return json.loads(self.value)
@property
def as_dict(self) -> Dict:
"""Get row data as dictionary."""
return {
"keys": self.keys,
"value": self.data,
}

View File

@ -1,19 +1,21 @@
from typing import Union, Dict, List from typing import Union, Dict, List, Optional, Any
from ApiServiceRedis.Redis.Actions.actions import RedisRow from ApiServiceRedis.Redis.Models.base import RedisRow
class RedisResponse: class RedisResponse:
"""Base class for Redis response handling."""
def __init__( def __init__(
self, self,
status: bool, status: bool,
message: str, message: str,
data: RedisRow = None, data: Any = None,
error: str = None, error: Optional[str] = None,
): ):
self.status = status self.status = status
self.message = message self.message = message
self.data = data self.data = data
if isinstance(data, dict): if isinstance(data, dict):
self.data_type = "dict" self.data_type = "dict"
elif isinstance(data, list): elif isinstance(data, list):
@ -22,7 +24,7 @@ class RedisResponse:
self.data_type = None self.data_type = None
self.error = error self.error = error
def as_dict(self): def as_dict(self) -> Dict:
return { return {
"status": self.status, "status": self.status,
"message": self.message, "message": self.message,
@ -30,3 +32,13 @@ class RedisResponse:
"dataType": self.data_type, "dataType": self.data_type,
"error": self.error, "error": self.error,
} }
@property
def all(self) -> Union[Optional[List[RedisRow]]]:
return self.data
@property
def first(self) -> Union[RedisRow, None]:
if self.data:
return self.data[0]
return None

View File

@ -22,4 +22,4 @@ class AccessToken(BaseModel):
# "accessToken": "token", # "accessToken": "token",
# "userUUID": "uuid" # "userUUID": "uuid"
# }) # })
# access_token_obj.to_list() # access_token_obj.to_list()

View File

@ -1,16 +1,11 @@
from redis import Redis from redis import Redis
from api_configs import WagRedis from AllConfigs.Redis.configs import WagRedis
class RedisConn: class RedisConn:
def __init__(self): def __init__(self):
self.redis = Redis( self.redis = Redis(**WagRedis.as_dict())
host=WagRedis.REDIS_HOST,
password=WagRedis.REDIS_PASSWORD,
port=WagRedis.REDIS_PORT,
db=WagRedis.REDIS_DB,
)
if not self.check_connection(): if not self.check_connection():
raise Exception("Connection error") raise Exception("Connection error")

View File

@ -1,10 +1,40 @@
import secrets
import uuid
from ApiServiceRedis.Redis.Actions.actions import RedisActions from ApiServiceRedis.Redis.Actions.actions import RedisActions
from ApiServiceRedis.Redis.Models.row import AccessToken from ApiServiceRedis.Redis.Models.row import AccessToken
first_user = AccessToken(
redis_cli_actions = RedisActions() accessToken=secrets.token_urlsafe(90),
userUUID=uuid.uuid4().__str__(),
access_object = AccessToken(
accessToken="token",
userUUID="uuid"
) )
second_user = AccessToken(
accessToken=secrets.token_urlsafe(90),
userUUID=uuid.uuid4().__str__(),
)
json_data = lambda uu_id, access: {
"uu_id": uu_id,
"access_token": access,
"user_type": 1,
"selected_company": None,
"selected_occupant": None,
"reachable_event_list_id": [],
}
set_response_first_json = json_data(first_user.userUUID, first_user.accessToken)
set_response_second_json = json_data(second_user.userUUID, second_user.accessToken)
set_response_first = RedisActions.set_json(
list_keys=first_user.to_list(),
value=set_response_first_json,
expires={"seconds": 140},
)
set_response_second = RedisActions.set_json(
list_keys=second_user.to_list(),
value=set_response_second_json,
expires={"seconds": 190},
)
search_keys = [None, set_response_first_json["uu_id"]]
get_response = RedisActions.get_json(list_keys=search_keys)
print("get_response", [data.expires_at for data in get_response.all])

40
test.py Normal file
View File

@ -0,0 +1,40 @@
import secrets
import uuid
from ApiServiceRedis.Redis.Actions.actions import RedisActions
from ApiServiceRedis.Redis.Models.row import AccessToken
first_user = AccessToken(
accessToken=secrets.token_urlsafe(90),
userUUID=uuid.uuid4().__str__(),
)
second_user = AccessToken(
accessToken=secrets.token_urlsafe(90),
userUUID=uuid.uuid4().__str__(),
)
json_data = lambda uu_id, access: {
"uu_id": uu_id,
"access_token": access,
"user_type": 1,
"selected_company": None,
"selected_occupant": None,
"reachable_event_list_id": [],
}
set_response_first_json = json_data(first_user.userUUID, first_user.accessToken)
set_response_second_json = json_data(second_user.userUUID, second_user.accessToken)
set_response_first = RedisActions.set_json(
list_keys=first_user.to_list(),
value=set_response_first_json,
expires={"seconds": 140},
)
set_response_second = RedisActions.set_json(
list_keys=second_user.to_list(),
value=set_response_second_json,
expires={"seconds": 190},
)
search_keys = [None, set_response_first_json["uu_id"]]
get_response = RedisActions.get_json(list_keys=search_keys)
print("get_response", [data.expires_at for data in get_response.all])