altcodepro-polydb-python 2.1.0__py3-none-any.whl
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.
- altcodepro_polydb_python-2.1.0.dist-info/METADATA +378 -0
- altcodepro_polydb_python-2.1.0.dist-info/RECORD +51 -0
- altcodepro_polydb_python-2.1.0.dist-info/WHEEL +5 -0
- altcodepro_polydb_python-2.1.0.dist-info/licenses/LICENSE +21 -0
- altcodepro_polydb_python-2.1.0.dist-info/top_level.txt +1 -0
- polydb/__init__.py +64 -0
- polydb/adapters/AzureBlobStorageAdapter.py +77 -0
- polydb/adapters/AzureFileStorageAdapter.py +79 -0
- polydb/adapters/AzureQueueAdapter.py +61 -0
- polydb/adapters/AzureTableStorageAdapter.py +182 -0
- polydb/adapters/DynamoDBAdapter.py +216 -0
- polydb/adapters/EFSAdapter.py +50 -0
- polydb/adapters/FirestoreAdapter.py +193 -0
- polydb/adapters/GCPStorageAdapter.py +81 -0
- polydb/adapters/MongoDBAdapter.py +136 -0
- polydb/adapters/PostgreSQLAdapter.py +453 -0
- polydb/adapters/PubSubAdapter.py +83 -0
- polydb/adapters/S3Adapter.py +86 -0
- polydb/adapters/S3CompatibleAdapter.py +90 -0
- polydb/adapters/SQSAdapter.py +84 -0
- polydb/adapters/VercelKVAdapter.py +327 -0
- polydb/adapters/__init__.py +0 -0
- polydb/advanced_query.py +147 -0
- polydb/audit/AuditStorage.py +136 -0
- polydb/audit/__init__.py +7 -0
- polydb/audit/context.py +53 -0
- polydb/audit/manager.py +47 -0
- polydb/audit/models.py +86 -0
- polydb/base/NoSQLKVAdapter.py +301 -0
- polydb/base/ObjectStorageAdapter.py +42 -0
- polydb/base/QueueAdapter.py +27 -0
- polydb/base/SharedFilesAdapter.py +32 -0
- polydb/base/__init__.py +0 -0
- polydb/batch.py +163 -0
- polydb/cache.py +204 -0
- polydb/databaseFactory.py +748 -0
- polydb/decorators.py +21 -0
- polydb/errors.py +82 -0
- polydb/factory.py +107 -0
- polydb/models.py +39 -0
- polydb/monitoring.py +313 -0
- polydb/multitenancy.py +197 -0
- polydb/py.typed +0 -0
- polydb/query.py +150 -0
- polydb/registry.py +71 -0
- polydb/retry.py +76 -0
- polydb/schema.py +205 -0
- polydb/security.py +458 -0
- polydb/types.py +127 -0
- polydb/utils.py +61 -0
- polydb/validation.py +131 -0
polydb/validation.py
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# src/polydb/validation.py
|
|
2
|
+
"""
|
|
3
|
+
Model metadata validation and schema enforcement
|
|
4
|
+
"""
|
|
5
|
+
from typing import Type, Dict, Any, List, Optional
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from .errors import InvalidModelMetadataError, ValidationError
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class ValidationResult:
|
|
11
|
+
valid: bool
|
|
12
|
+
errors: List[str]
|
|
13
|
+
warnings: List[str]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ModelValidator:
|
|
17
|
+
"""Validates model metadata and schema"""
|
|
18
|
+
|
|
19
|
+
REQUIRED_FIELDS = {'storage'}
|
|
20
|
+
VALID_STORAGE_TYPES = {'sql', 'nosql'}
|
|
21
|
+
SQL_REQUIRED_FIELDS = {'table'}
|
|
22
|
+
NOSQL_OPTIONAL_FIELDS = {'collection', 'pk_field', 'rk_field'}
|
|
23
|
+
|
|
24
|
+
@classmethod
|
|
25
|
+
def validate_model(cls, model: Type) -> ValidationResult:
|
|
26
|
+
"""Comprehensive model validation"""
|
|
27
|
+
errors = []
|
|
28
|
+
warnings = []
|
|
29
|
+
|
|
30
|
+
# Check __polydb__ exists
|
|
31
|
+
if not hasattr(model, '__polydb__'):
|
|
32
|
+
errors.append(f"Model {model.__name__} missing __polydb__ metadata")
|
|
33
|
+
return ValidationResult(valid=False, errors=errors, warnings=warnings)
|
|
34
|
+
|
|
35
|
+
meta = getattr(model, '__polydb__')
|
|
36
|
+
|
|
37
|
+
# Check it's a dict
|
|
38
|
+
if not isinstance(meta, dict):
|
|
39
|
+
errors.append(f"__polydb__ must be a dict, got {type(meta)}")
|
|
40
|
+
return ValidationResult(valid=False, errors=errors, warnings=warnings)
|
|
41
|
+
|
|
42
|
+
# Check required fields
|
|
43
|
+
for field in cls.REQUIRED_FIELDS:
|
|
44
|
+
if field not in meta:
|
|
45
|
+
errors.append(f"Missing required field: {field}")
|
|
46
|
+
|
|
47
|
+
# Validate storage type
|
|
48
|
+
storage = meta.get('storage')
|
|
49
|
+
if storage and storage not in cls.VALID_STORAGE_TYPES:
|
|
50
|
+
errors.append(f"Invalid storage type: {storage}. Must be one of {cls.VALID_STORAGE_TYPES}")
|
|
51
|
+
|
|
52
|
+
# Storage-specific validation
|
|
53
|
+
if storage == 'sql':
|
|
54
|
+
for field in cls.SQL_REQUIRED_FIELDS:
|
|
55
|
+
if field not in meta:
|
|
56
|
+
errors.append(f"SQL storage requires field: {field}")
|
|
57
|
+
|
|
58
|
+
elif storage == 'nosql':
|
|
59
|
+
# NoSQL has optional fields, warn if missing common ones
|
|
60
|
+
if 'pk_field' not in meta:
|
|
61
|
+
warnings.append("No pk_field specified, will default to 'id'")
|
|
62
|
+
|
|
63
|
+
if not meta.get('collection') and not meta.get('table'):
|
|
64
|
+
warnings.append("No collection/table name specified, will use model name")
|
|
65
|
+
|
|
66
|
+
# Validate cache settings
|
|
67
|
+
if meta.get('cache') and not isinstance(meta.get('cache_ttl'), (int, type(None))):
|
|
68
|
+
errors.append("cache_ttl must be an integer or None")
|
|
69
|
+
|
|
70
|
+
# Validate provider
|
|
71
|
+
valid_providers = {'azure', 'aws', 'gcp', 'vercel', 'mongodb', 'postgresql'}
|
|
72
|
+
provider = meta.get('provider')
|
|
73
|
+
if provider and provider.lower() not in valid_providers:
|
|
74
|
+
warnings.append(f"Unusual provider: {provider}")
|
|
75
|
+
|
|
76
|
+
return ValidationResult(
|
|
77
|
+
valid=len(errors) == 0,
|
|
78
|
+
errors=errors,
|
|
79
|
+
warnings=warnings
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
@classmethod
|
|
83
|
+
def validate_and_raise(cls, model: Type):
|
|
84
|
+
"""Validate and raise if invalid"""
|
|
85
|
+
result = cls.validate_model(model)
|
|
86
|
+
|
|
87
|
+
if not result.valid:
|
|
88
|
+
raise InvalidModelMetadataError(
|
|
89
|
+
f"Invalid model {model.__name__}: {', '.join(result.errors)}"
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
# Log warnings
|
|
93
|
+
if result.warnings:
|
|
94
|
+
import logging
|
|
95
|
+
logger = logging.getLogger(__name__)
|
|
96
|
+
for warning in result.warnings:
|
|
97
|
+
logger.warning(f"{model.__name__}: {warning}")
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class SchemaValidator:
|
|
101
|
+
"""Validates data against model schema"""
|
|
102
|
+
|
|
103
|
+
@staticmethod
|
|
104
|
+
def validate_data(model: Type, data: Dict[str, Any]) -> ValidationResult:
|
|
105
|
+
"""Validate data against model requirements"""
|
|
106
|
+
errors = []
|
|
107
|
+
warnings = []
|
|
108
|
+
|
|
109
|
+
meta = getattr(model, '__polydb__', {})
|
|
110
|
+
|
|
111
|
+
# Check required keys exist
|
|
112
|
+
pk_field = meta.get('pk_field', 'id')
|
|
113
|
+
if pk_field not in data and not meta.get('auto_generate'):
|
|
114
|
+
errors.append(f"Missing required field: {pk_field}")
|
|
115
|
+
|
|
116
|
+
# Check field types if type hints available
|
|
117
|
+
if hasattr(model, '__annotations__'):
|
|
118
|
+
annotations = model.__annotations__
|
|
119
|
+
for field_name, expected_type in annotations.items():
|
|
120
|
+
if field_name in data:
|
|
121
|
+
actual_value = data[field_name]
|
|
122
|
+
if not isinstance(actual_value, expected_type):
|
|
123
|
+
warnings.append(
|
|
124
|
+
f"Field {field_name} expected {expected_type}, got {type(actual_value)}"
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
return ValidationResult(
|
|
128
|
+
valid=len(errors) == 0,
|
|
129
|
+
errors=errors,
|
|
130
|
+
warnings=warnings
|
|
131
|
+
)
|