sharedkernel 2.1.2__tar.gz → 2.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {sharedkernel-2.1.2 → sharedkernel-2.2.0}/PKG-INFO +4 -2
- {sharedkernel-2.1.2 → sharedkernel-2.2.0}/README.md +2 -0
- {sharedkernel-2.1.2 → sharedkernel-2.2.0}/setup.py +2 -1
- sharedkernel-2.2.0/sharedkernel/database/__init__.py +3 -0
- sharedkernel-2.2.0/sharedkernel/database/audit_model.py +17 -0
- sharedkernel-2.2.0/sharedkernel/database/mongo_generic_audit_repository.py +72 -0
- sharedkernel-2.2.0/sharedkernel/database/mongo_generic_repository.py +122 -0
- sharedkernel-2.2.0/sharedkernel/diff_utils.py +65 -0
- {sharedkernel-2.1.2 → sharedkernel-2.2.0}/sharedkernel/jwt_service.py +14 -8
- sharedkernel-2.2.0/sharedkernel/objects/__init__.py +5 -0
- {sharedkernel-2.1.2 → sharedkernel-2.2.0}/sharedkernel/objects/base_document.py +5 -1
- sharedkernel-2.2.0/sharedkernel/objects/user_info.py +13 -0
- {sharedkernel-2.1.2 → sharedkernel-2.2.0}/sharedkernel.egg-info/PKG-INFO +4 -2
- {sharedkernel-2.1.2 → sharedkernel-2.2.0}/sharedkernel.egg-info/SOURCES.txt +5 -1
- {sharedkernel-2.1.2 → sharedkernel-2.2.0}/sharedkernel.egg-info/requires.txt +1 -1
- sharedkernel-2.1.2/sharedkernel/database/__init__.py +0 -1
- sharedkernel-2.1.2/sharedkernel/database/mongo_generic_repository.py +0 -51
- sharedkernel-2.1.2/sharedkernel/objects/__init__.py +0 -4
- {sharedkernel-2.1.2 → sharedkernel-2.2.0}/setup.cfg +0 -0
- {sharedkernel-2.1.2 → sharedkernel-2.2.0}/sharedkernel/common.py +0 -0
- {sharedkernel-2.1.2 → sharedkernel-2.2.0}/sharedkernel/data_format_converter.py +0 -0
- {sharedkernel-2.1.2 → sharedkernel-2.2.0}/sharedkernel/date_converter.py +0 -0
- {sharedkernel-2.1.2 → sharedkernel-2.2.0}/sharedkernel/enum/__init__.py +0 -0
- {sharedkernel-2.1.2 → sharedkernel-2.2.0}/sharedkernel/enum/error_code.py +0 -0
- {sharedkernel-2.1.2 → sharedkernel-2.2.0}/sharedkernel/exception/__init__.py +0 -0
- {sharedkernel-2.1.2 → sharedkernel-2.2.0}/sharedkernel/exception/exception.py +0 -0
- {sharedkernel-2.1.2 → sharedkernel-2.2.0}/sharedkernel/exception/exception_handlers.py +0 -0
- {sharedkernel-2.1.2 → sharedkernel-2.2.0}/sharedkernel/normalizer/__init__.py +0 -0
- {sharedkernel-2.1.2 → sharedkernel-2.2.0}/sharedkernel/normalizer/number_normalizer.py +0 -0
- {sharedkernel-2.1.2 → sharedkernel-2.2.0}/sharedkernel/normalizer/phone_number_normalizer.py +0 -0
- {sharedkernel-2.1.2 → sharedkernel-2.2.0}/sharedkernel/normalizer/string_normalizer.py +0 -0
- {sharedkernel-2.1.2 → sharedkernel-2.2.0}/sharedkernel/objects/json_string_model.py +0 -0
- {sharedkernel-2.1.2 → sharedkernel-2.2.0}/sharedkernel/objects/jwt_model.py +0 -0
- {sharedkernel-2.1.2 → sharedkernel-2.2.0}/sharedkernel/objects/result.py +0 -0
- {sharedkernel-2.1.2 → sharedkernel-2.2.0}/sharedkernel/regex_masking.py +0 -0
- {sharedkernel-2.1.2 → sharedkernel-2.2.0}/sharedkernel/s3_uploader.py +0 -0
- {sharedkernel-2.1.2 → sharedkernel-2.2.0}/sharedkernel/string_extentions.py +0 -0
- {sharedkernel-2.1.2 → sharedkernel-2.2.0}/sharedkernel.egg-info/dependency_links.txt +0 -0
- {sharedkernel-2.1.2 → sharedkernel-2.2.0}/sharedkernel.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sharedkernel
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: sharekernel is a shared package between all python projects
|
|
5
5
|
Author: Smilinno
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -17,7 +17,7 @@ Requires-Dist: boto3==1.35.90
|
|
|
17
17
|
Requires-Dist: python-docx
|
|
18
18
|
Requires-Dist: mammoth
|
|
19
19
|
Requires-Dist: markdown
|
|
20
|
-
Requires-Dist:
|
|
20
|
+
Requires-Dist: beautifulsoup4deepdiff
|
|
21
21
|
Dynamic: author
|
|
22
22
|
Dynamic: description
|
|
23
23
|
Dynamic: description-content-type
|
|
@@ -28,6 +28,8 @@ Dynamic: summary
|
|
|
28
28
|
this is a shared kernel package
|
|
29
29
|
|
|
30
30
|
# Change Log
|
|
31
|
+
### Version 2.2.0
|
|
32
|
+
- implement Audit logging
|
|
31
33
|
### Version 2.1.2
|
|
32
34
|
- Update pydantic parse_object with model_validate
|
|
33
35
|
### Version 2.1.0
|
|
@@ -34,9 +34,10 @@ setup(
|
|
|
34
34
|
"mammoth",
|
|
35
35
|
"markdown",
|
|
36
36
|
"beautifulsoup4"
|
|
37
|
+
"deepdiff"
|
|
37
38
|
],
|
|
38
39
|
# *strongly* suggested for sharing
|
|
39
|
-
version="2.
|
|
40
|
+
version="2.2.0",
|
|
40
41
|
description="sharekernel is a shared package between all python projects",
|
|
41
42
|
long_description=long_description,
|
|
42
43
|
long_description_content_type="text/markdown",
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from sharedkernel.objects.base_document import BaseDocument
|
|
2
|
+
|
|
3
|
+
from enum import Enum
|
|
4
|
+
|
|
5
|
+
class AuditOperation(str, Enum):
|
|
6
|
+
CREATE = "create"
|
|
7
|
+
UPDATE = "update"
|
|
8
|
+
DELETE = "delete"
|
|
9
|
+
|
|
10
|
+
class AuditLog(BaseDocument):
|
|
11
|
+
user_id: str
|
|
12
|
+
collection_name: str
|
|
13
|
+
document_id: str
|
|
14
|
+
operation: AuditOperation
|
|
15
|
+
original: dict | None = None
|
|
16
|
+
modified: dict
|
|
17
|
+
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
from typing import Generic, TypeVar, List, Type
|
|
2
|
+
from bson import ObjectId
|
|
3
|
+
from pymongo import MongoClient
|
|
4
|
+
from pydantic.v1 import BaseModel
|
|
5
|
+
|
|
6
|
+
from sharedkernel.database.audit_model import AuditLog
|
|
7
|
+
|
|
8
|
+
T = TypeVar("T", bound=BaseModel)
|
|
9
|
+
|
|
10
|
+
AUDIT_COLLECTION_NAME = "audit_log"
|
|
11
|
+
|
|
12
|
+
class MongoGenericAuditRepository():
|
|
13
|
+
def __init__(
|
|
14
|
+
self,
|
|
15
|
+
database: MongoClient,
|
|
16
|
+
audit_collection_name: str | None = None
|
|
17
|
+
):
|
|
18
|
+
self.database = database
|
|
19
|
+
self.__collection_name = audit_collection_name or AUDIT_COLLECTION_NAME
|
|
20
|
+
|
|
21
|
+
self.collection = self.database[self.__collection_name]
|
|
22
|
+
|
|
23
|
+
def _map_to_model(self, document: dict) -> AuditLog:
|
|
24
|
+
document["id"] = str(document.pop("_id"))
|
|
25
|
+
return AuditLog(**document)
|
|
26
|
+
|
|
27
|
+
def find_one(self, id: str) -> T:
|
|
28
|
+
query = {"_id": ObjectId(id), "is_deleted": False}
|
|
29
|
+
result = self.collection.find_one(query)
|
|
30
|
+
return self._map_to_model(result) if result else None
|
|
31
|
+
|
|
32
|
+
def find(
|
|
33
|
+
self,
|
|
34
|
+
document_id: str | None = None,
|
|
35
|
+
operation: str | None = None,
|
|
36
|
+
collection_name: str | None = None,
|
|
37
|
+
field_name: str | None = None,
|
|
38
|
+
) -> List[AuditLog]:
|
|
39
|
+
|
|
40
|
+
query = {}
|
|
41
|
+
|
|
42
|
+
if document_id:
|
|
43
|
+
query["document_id"] = document_id
|
|
44
|
+
if operation:
|
|
45
|
+
query["operation"] = operation
|
|
46
|
+
if collection_name:
|
|
47
|
+
query["collection_name"] = collection_name
|
|
48
|
+
|
|
49
|
+
if field_name:
|
|
50
|
+
query["$or"] = [
|
|
51
|
+
{f"original.{field_name}": {"$exists": True}},
|
|
52
|
+
{f"modified.{field_name}": {"$exists": True}},
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
results = self.collection.find(query)
|
|
56
|
+
return [self._map_to_model(bot) for bot in results]
|
|
57
|
+
|
|
58
|
+
def insert_one(self, data: AuditLog) -> str:
|
|
59
|
+
delattr(data, "id")
|
|
60
|
+
result = self.collection.insert_one(data.model_dump())
|
|
61
|
+
return str(result.inserted_id)
|
|
62
|
+
|
|
63
|
+
def insert_many(self, data: List[AuditLog]) -> List[str]:
|
|
64
|
+
data_list = [delattr(d.model_dump(), "id") for d in data]
|
|
65
|
+
result = self.collection.insert_many(data_list)
|
|
66
|
+
return [str(id_) for id_ in result.inserted_ids]
|
|
67
|
+
|
|
68
|
+
def delete_one(self, id: str) -> int:
|
|
69
|
+
query = {"_id": ObjectId(id)}
|
|
70
|
+
result = self.collection.delete_one(query)
|
|
71
|
+
return result.deleted_count
|
|
72
|
+
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
from bson import ObjectId
|
|
2
|
+
from typing import Generic, TypeVar, List, Type
|
|
3
|
+
from pydantic.v1 import BaseModel
|
|
4
|
+
from pymongo import MongoClient, ReturnDocument
|
|
5
|
+
from contextlib import suppress
|
|
6
|
+
|
|
7
|
+
from sharedkernel.objects.user_info import current_user_info
|
|
8
|
+
from sharedkernel.string_extentions import camel_to_snake
|
|
9
|
+
from sharedkernel.objects.base_document import BaseAuditDocument
|
|
10
|
+
from sharedkernel.database.mongo_generic_audit_repository import MongoGenericAuditRepository
|
|
11
|
+
from sharedkernel.database.audit_model import (
|
|
12
|
+
AuditLog,
|
|
13
|
+
AuditOperation
|
|
14
|
+
)
|
|
15
|
+
from sharedkernel.diff_utils import generate_clean_diff
|
|
16
|
+
|
|
17
|
+
T = TypeVar("T", bound=BaseModel)
|
|
18
|
+
|
|
19
|
+
class MongoGenericRepository(Generic[T]):
|
|
20
|
+
def __init__(self, database: MongoClient, model: Type[T], audit_collection_name: str | None = None):
|
|
21
|
+
self.database = database
|
|
22
|
+
self.__collection_name = camel_to_snake(model.__name__)
|
|
23
|
+
self.collection = self.database[self.__collection_name]
|
|
24
|
+
self.model = model
|
|
25
|
+
self.audit_collection = MongoGenericAuditRepository(
|
|
26
|
+
database=database,
|
|
27
|
+
audit_collection_name=audit_collection_name
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
def _map_to_model(self, document: dict) -> T:
|
|
31
|
+
document["id"] = str(document.pop("_id"))
|
|
32
|
+
return self.model.model_validate(document)
|
|
33
|
+
|
|
34
|
+
def find_one(self, id: str) -> T:
|
|
35
|
+
query = {"_id": ObjectId(id), "is_deleted": False}
|
|
36
|
+
result = self.collection.find_one(query)
|
|
37
|
+
return self._map_to_model(result) if result else None
|
|
38
|
+
|
|
39
|
+
def insert_one(self, data: T) -> str:
|
|
40
|
+
delattr(data, "id")
|
|
41
|
+
result = self.collection.insert_one(data.model_dump())
|
|
42
|
+
|
|
43
|
+
# For Audit log
|
|
44
|
+
with suppress(Exception):
|
|
45
|
+
if isinstance(data, BaseAuditDocument):
|
|
46
|
+
document = AuditLog(
|
|
47
|
+
user_id=current_user_info.get().nameid,
|
|
48
|
+
collection_name=self.__collection_name,
|
|
49
|
+
document_id=str(result.inserted_id),
|
|
50
|
+
operation=AuditOperation.CREATE,
|
|
51
|
+
original=None,
|
|
52
|
+
modified=data.model_dump(),
|
|
53
|
+
)
|
|
54
|
+
self.audit_collection.insert_one(document)
|
|
55
|
+
|
|
56
|
+
return str(result.inserted_id)
|
|
57
|
+
|
|
58
|
+
def insert_many(self, data: List[T]) -> List[str]:
|
|
59
|
+
data_list = [delattr(d.model_dump(), "id") for d in data]
|
|
60
|
+
result = self.collection.insert_many(data_list)
|
|
61
|
+
|
|
62
|
+
# For Audit log
|
|
63
|
+
with suppress(Exception):
|
|
64
|
+
if len(result.inserted_ids) > 0:
|
|
65
|
+
if isinstance(data[0], BaseAuditDocument):
|
|
66
|
+
documents = []
|
|
67
|
+
for i, d in enumerate(data):
|
|
68
|
+
document = AuditLog(
|
|
69
|
+
user_id=current_user_info.get().nameid,
|
|
70
|
+
collection_name=self.__collection_name,
|
|
71
|
+
document_id=str(result.inserted_ids[i]),
|
|
72
|
+
operation=AuditOperation.CREATE,
|
|
73
|
+
original=None,
|
|
74
|
+
modified=d.model_dump(),
|
|
75
|
+
)
|
|
76
|
+
documents.append(document)
|
|
77
|
+
|
|
78
|
+
self.audit_collection.insert_many(documents)
|
|
79
|
+
|
|
80
|
+
return [str(id_) for id_ in result.inserted_ids]
|
|
81
|
+
|
|
82
|
+
def update_one(self, id: str, data: T) -> int:
|
|
83
|
+
delattr(data, "id")
|
|
84
|
+
|
|
85
|
+
query = {"_id": ObjectId(id)}
|
|
86
|
+
before_dict = self.collection.find_one_and_update(
|
|
87
|
+
query,
|
|
88
|
+
{"$set": data.model_dump()},
|
|
89
|
+
return_document=ReturnDocument.BEFORE
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
before_model = self.model(**before_dict)
|
|
93
|
+
|
|
94
|
+
# For Audit log
|
|
95
|
+
with suppress(Exception):
|
|
96
|
+
diff_data = generate_clean_diff(before_model, data)
|
|
97
|
+
old_data, new_data = diff_data["original"], diff_data["modified"]
|
|
98
|
+
|
|
99
|
+
if isinstance(data, BaseAuditDocument) and new_data:
|
|
100
|
+
document = AuditLog(
|
|
101
|
+
user_id=current_user_info.get().nameid,
|
|
102
|
+
collection_name=self.__collection_name,
|
|
103
|
+
document_id=id,
|
|
104
|
+
operation=AuditOperation.UPDATE,
|
|
105
|
+
original=old_data,
|
|
106
|
+
modified=new_data,
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
self.audit_collection.insert_one(document)
|
|
110
|
+
|
|
111
|
+
return 1
|
|
112
|
+
|
|
113
|
+
def delete_one(self, id: str) -> int:
|
|
114
|
+
query = {"_id": ObjectId(id)}
|
|
115
|
+
result = self.collection.delete_one(query)
|
|
116
|
+
return result.deleted_count
|
|
117
|
+
|
|
118
|
+
def get_all(self, page_number=1, page_size=10) -> List[T]:
|
|
119
|
+
skip_count = (page_number - 1) * page_size
|
|
120
|
+
query = {"is_deleted": False}
|
|
121
|
+
result = self.collection.find(query).skip(skip_count).limit(page_size)
|
|
122
|
+
return [self._map_to_model(doc) for doc in result]
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
from deepdiff import DeepDiff
|
|
2
|
+
from pydantic.v1 import BaseModel
|
|
3
|
+
|
|
4
|
+
EXCLUDE_FIELDS = {"created_on", "updated_on"}
|
|
5
|
+
|
|
6
|
+
def generate_clean_diff(before: BaseModel, after: BaseModel, exclude_fields: set = EXCLUDE_FIELDS):
|
|
7
|
+
before_dict = before.model_dump()
|
|
8
|
+
after_dict = after.model_dump()
|
|
9
|
+
try:
|
|
10
|
+
clean_before = {k: v for k, v in before_dict.items() if k not in exclude_fields}
|
|
11
|
+
clean_after = {k: v for k, v in after_dict.items() if k not in exclude_fields}
|
|
12
|
+
|
|
13
|
+
diff = DeepDiff(clean_before, clean_after, ignore_order=True)
|
|
14
|
+
original = {}
|
|
15
|
+
modified = {}
|
|
16
|
+
|
|
17
|
+
for change_type in ["values_changed", "type_changes"]:
|
|
18
|
+
changes = diff.get(change_type, {})
|
|
19
|
+
for path, change in changes.items():
|
|
20
|
+
keys = path.strip("root").strip("[").strip("]").replace("][", ".").replace("'", "").split(".")
|
|
21
|
+
_apply_nested_keys(original, keys, change["old_value"])
|
|
22
|
+
_apply_nested_keys(modified, keys, change["new_value"])
|
|
23
|
+
|
|
24
|
+
return {"original": original, "modified": modified}
|
|
25
|
+
|
|
26
|
+
except Exception as e:
|
|
27
|
+
return {"original": {}, "modified": {}, "error": str(e)}
|
|
28
|
+
|
|
29
|
+
def _apply_nested_keys(base: dict, keys: list, value):
|
|
30
|
+
current = base
|
|
31
|
+
for i, key in enumerate(keys):
|
|
32
|
+
is_last = i == len(keys) - 1
|
|
33
|
+
next_key = keys[i + 1] if not is_last else None
|
|
34
|
+
|
|
35
|
+
# Determine if current key is an index (for lists)
|
|
36
|
+
is_index = key.isdigit()
|
|
37
|
+
key = int(key) if is_index else key
|
|
38
|
+
|
|
39
|
+
# Ensure current is list if key is int
|
|
40
|
+
if is_index and not isinstance(current, list):
|
|
41
|
+
# Convert current to list if it isn't already
|
|
42
|
+
raise TypeError(f"Expected list at {keys[:i]}, got {type(current).__name__}")
|
|
43
|
+
|
|
44
|
+
# Handle last key
|
|
45
|
+
if is_last:
|
|
46
|
+
if isinstance(current, list):
|
|
47
|
+
# Ensure list is big enough
|
|
48
|
+
while len(current) <= key:
|
|
49
|
+
current.append(None)
|
|
50
|
+
current[key] = value
|
|
51
|
+
else:
|
|
52
|
+
current[key] = value
|
|
53
|
+
return
|
|
54
|
+
|
|
55
|
+
# Prepare next container (dict or list)
|
|
56
|
+
if isinstance(current, list):
|
|
57
|
+
while len(current) <= key:
|
|
58
|
+
current.append(None)
|
|
59
|
+
if current[key] is None:
|
|
60
|
+
current[key] = {} if (next_key and not next_key.isdigit()) else []
|
|
61
|
+
current = current[key]
|
|
62
|
+
else:
|
|
63
|
+
if key not in current:
|
|
64
|
+
current[key] = {} if (next_key and not next_key.isdigit()) else []
|
|
65
|
+
current = current[key]
|
|
@@ -2,10 +2,9 @@ import jwt
|
|
|
2
2
|
import time
|
|
3
3
|
from fastapi import Request
|
|
4
4
|
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
|
5
|
-
|
|
6
5
|
from sharedkernel.exception.exception import UnAuthorizedException
|
|
7
6
|
from sharedkernel.objects import JwtModel
|
|
8
|
-
|
|
7
|
+
from sharedkernel.objects.user_info import current_user_info, UserInfo
|
|
9
8
|
|
|
10
9
|
class JWTBearer(HTTPBearer):
|
|
11
10
|
|
|
@@ -18,21 +17,28 @@ class JWTBearer(HTTPBearer):
|
|
|
18
17
|
credentials: HTTPAuthorizationCredentials = await super(
|
|
19
18
|
JWTBearer, self
|
|
20
19
|
).__call__(request)
|
|
21
|
-
|
|
22
20
|
decoded_token = self.verify(credentials.credentials)
|
|
23
21
|
request.state.decoded_token = decoded_token
|
|
24
22
|
|
|
23
|
+
nameid = decoded_token.get("nameid") or decoded_token.get("id")
|
|
24
|
+
|
|
25
|
+
user_info = UserInfo(
|
|
26
|
+
nameid=nameid, email=decoded_token.get("email"), unique_name=decoded_token.get("unique_name")
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
current_user_info.set(user_info)
|
|
30
|
+
|
|
25
31
|
except:
|
|
26
32
|
raise UnAuthorizedException()
|
|
27
33
|
|
|
28
34
|
|
|
29
35
|
def verify(self, token: str) -> dict:
|
|
30
36
|
decoded_token = jwt.decode(
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
37
|
+
jwt= token.replace("Bearer","").strip(),
|
|
38
|
+
key= self.jwt_config.secret_key,
|
|
39
|
+
algorithms= self.jwt_config.algorithms,
|
|
40
|
+
audience= self.jwt_config.audience,
|
|
41
|
+
issuer= self.jwt_config.issuer
|
|
36
42
|
)
|
|
37
43
|
|
|
38
44
|
if decoded_token["exp"] < time.time():
|
|
@@ -6,4 +6,8 @@ class BaseDocument(BaseModel):
|
|
|
6
6
|
id: str | None = None
|
|
7
7
|
is_deleted: bool | None = False
|
|
8
8
|
created_on: datetime = Field(default_factory=datetime.now)
|
|
9
|
-
updated_on: datetime = Field(default_factory=datetime.now)
|
|
9
|
+
updated_on: datetime = Field(default_factory=datetime.now)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class BaseAuditDocument(BaseDocument):
|
|
13
|
+
pass
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from pydantic import BaseModel
|
|
2
|
+
|
|
3
|
+
from contextvars import ContextVar
|
|
4
|
+
|
|
5
|
+
class UserInfo(BaseModel):
|
|
6
|
+
nameid: str
|
|
7
|
+
unique_name: str | None = None
|
|
8
|
+
email: str | None = None
|
|
9
|
+
role: str | None = None
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
current_user_info: ContextVar[UserInfo] = ContextVar("current_user_info")
|
|
13
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sharedkernel
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.2.0
|
|
4
4
|
Summary: sharekernel is a shared package between all python projects
|
|
5
5
|
Author: Smilinno
|
|
6
6
|
Description-Content-Type: text/markdown
|
|
@@ -17,7 +17,7 @@ Requires-Dist: boto3==1.35.90
|
|
|
17
17
|
Requires-Dist: python-docx
|
|
18
18
|
Requires-Dist: mammoth
|
|
19
19
|
Requires-Dist: markdown
|
|
20
|
-
Requires-Dist:
|
|
20
|
+
Requires-Dist: beautifulsoup4deepdiff
|
|
21
21
|
Dynamic: author
|
|
22
22
|
Dynamic: description
|
|
23
23
|
Dynamic: description-content-type
|
|
@@ -28,6 +28,8 @@ Dynamic: summary
|
|
|
28
28
|
this is a shared kernel package
|
|
29
29
|
|
|
30
30
|
# Change Log
|
|
31
|
+
### Version 2.2.0
|
|
32
|
+
- implement Audit logging
|
|
31
33
|
### Version 2.1.2
|
|
32
34
|
- Update pydantic parse_object with model_validate
|
|
33
35
|
### Version 2.1.0
|
|
@@ -3,6 +3,7 @@ setup.py
|
|
|
3
3
|
sharedkernel/common.py
|
|
4
4
|
sharedkernel/data_format_converter.py
|
|
5
5
|
sharedkernel/date_converter.py
|
|
6
|
+
sharedkernel/diff_utils.py
|
|
6
7
|
sharedkernel/jwt_service.py
|
|
7
8
|
sharedkernel/regex_masking.py
|
|
8
9
|
sharedkernel/s3_uploader.py
|
|
@@ -13,6 +14,8 @@ sharedkernel.egg-info/dependency_links.txt
|
|
|
13
14
|
sharedkernel.egg-info/requires.txt
|
|
14
15
|
sharedkernel.egg-info/top_level.txt
|
|
15
16
|
sharedkernel/database/__init__.py
|
|
17
|
+
sharedkernel/database/audit_model.py
|
|
18
|
+
sharedkernel/database/mongo_generic_audit_repository.py
|
|
16
19
|
sharedkernel/database/mongo_generic_repository.py
|
|
17
20
|
sharedkernel/enum/__init__.py
|
|
18
21
|
sharedkernel/enum/error_code.py
|
|
@@ -27,4 +30,5 @@ sharedkernel/objects/__init__.py
|
|
|
27
30
|
sharedkernel/objects/base_document.py
|
|
28
31
|
sharedkernel/objects/json_string_model.py
|
|
29
32
|
sharedkernel/objects/jwt_model.py
|
|
30
|
-
sharedkernel/objects/result.py
|
|
33
|
+
sharedkernel/objects/result.py
|
|
34
|
+
sharedkernel/objects/user_info.py
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
# from .mongo_repository_base import MongoRepositoryBase
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
from pymongo import MongoClient
|
|
2
|
-
from bson import ObjectId
|
|
3
|
-
from typing import Generic, TypeVar, List, Type
|
|
4
|
-
from pydantic.v1 import BaseModel
|
|
5
|
-
from sharedkernel.string_extentions import camel_to_snake
|
|
6
|
-
|
|
7
|
-
T = TypeVar("T", bound=BaseModel)
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
class MongoGenericRepository(Generic[T]):
|
|
11
|
-
def __init__(self, database: MongoClient, model: Type[T]):
|
|
12
|
-
self.database = database
|
|
13
|
-
self.__collection_name = camel_to_snake(model.__name__)
|
|
14
|
-
self.collection = self.database[self.__collection_name]
|
|
15
|
-
self.model = model
|
|
16
|
-
|
|
17
|
-
def _map_to_model(self, document: dict) -> T:
|
|
18
|
-
document["id"] = str(document.pop("_id"))
|
|
19
|
-
return self.model.model_validate(document)
|
|
20
|
-
|
|
21
|
-
def find_one(self, id: str) -> T:
|
|
22
|
-
query = {"_id": ObjectId(id), "is_deleted": False}
|
|
23
|
-
result = self.collection.find_one(query)
|
|
24
|
-
return self._map_to_model(result) if result else None
|
|
25
|
-
|
|
26
|
-
def insert_one(self, data: T) -> str:
|
|
27
|
-
delattr(data, "id")
|
|
28
|
-
result = self.collection.insert_one(data.dict())
|
|
29
|
-
return str(result.inserted_id)
|
|
30
|
-
|
|
31
|
-
def insert_many(self, data: List[T]) -> List[str]:
|
|
32
|
-
data_list = [delattr(d.dict(), "id") for d in data]
|
|
33
|
-
result = self.collection.insert_many(data_list)
|
|
34
|
-
return [str(id_) for id_ in result.inserted_ids]
|
|
35
|
-
|
|
36
|
-
def update_one(self, id: str, data: T) -> int:
|
|
37
|
-
delattr(data, "id")
|
|
38
|
-
query = {"_id": ObjectId(id)}
|
|
39
|
-
result = self.collection.update_one(query, {"$set": data.dict()})
|
|
40
|
-
return result.modified_count
|
|
41
|
-
|
|
42
|
-
def delete_one(self, id: str) -> int:
|
|
43
|
-
query = {"_id": ObjectId(id)}
|
|
44
|
-
result = self.collection.delete_one(query)
|
|
45
|
-
return result.deleted_count
|
|
46
|
-
|
|
47
|
-
def get_all(self, page_number=1, page_size=10) -> List[T]:
|
|
48
|
-
skip_count = (page_number - 1) * page_size
|
|
49
|
-
query = {"is_deleted": False}
|
|
50
|
-
result = self.collection.find(query).skip(skip_count).limit(page_size)
|
|
51
|
-
return [self._map_to_model(doc) for doc in result]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sharedkernel-2.1.2 → sharedkernel-2.2.0}/sharedkernel/normalizer/phone_number_normalizer.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|