fraiseql-confiture 0.3.4__cp311-cp311-win_amd64.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.
- confiture/__init__.py +48 -0
- confiture/_core.cp311-win_amd64.pyd +0 -0
- confiture/cli/__init__.py +0 -0
- confiture/cli/dry_run.py +116 -0
- confiture/cli/lint_formatter.py +193 -0
- confiture/cli/main.py +1656 -0
- confiture/config/__init__.py +0 -0
- confiture/config/environment.py +263 -0
- confiture/core/__init__.py +51 -0
- confiture/core/anonymization/__init__.py +0 -0
- confiture/core/anonymization/audit.py +485 -0
- confiture/core/anonymization/benchmarking.py +372 -0
- confiture/core/anonymization/breach_notification.py +652 -0
- confiture/core/anonymization/compliance.py +617 -0
- confiture/core/anonymization/composer.py +298 -0
- confiture/core/anonymization/data_subject_rights.py +669 -0
- confiture/core/anonymization/factory.py +319 -0
- confiture/core/anonymization/governance.py +737 -0
- confiture/core/anonymization/performance.py +1092 -0
- confiture/core/anonymization/profile.py +284 -0
- confiture/core/anonymization/registry.py +195 -0
- confiture/core/anonymization/security/kms_manager.py +547 -0
- confiture/core/anonymization/security/lineage.py +888 -0
- confiture/core/anonymization/security/token_store.py +686 -0
- confiture/core/anonymization/strategies/__init__.py +41 -0
- confiture/core/anonymization/strategies/address.py +359 -0
- confiture/core/anonymization/strategies/credit_card.py +374 -0
- confiture/core/anonymization/strategies/custom.py +161 -0
- confiture/core/anonymization/strategies/date.py +218 -0
- confiture/core/anonymization/strategies/differential_privacy.py +398 -0
- confiture/core/anonymization/strategies/email.py +141 -0
- confiture/core/anonymization/strategies/format_preserving_encryption.py +310 -0
- confiture/core/anonymization/strategies/hash.py +150 -0
- confiture/core/anonymization/strategies/ip_address.py +235 -0
- confiture/core/anonymization/strategies/masking_retention.py +252 -0
- confiture/core/anonymization/strategies/name.py +298 -0
- confiture/core/anonymization/strategies/phone.py +119 -0
- confiture/core/anonymization/strategies/preserve.py +85 -0
- confiture/core/anonymization/strategies/redact.py +101 -0
- confiture/core/anonymization/strategies/salted_hashing.py +322 -0
- confiture/core/anonymization/strategies/text_redaction.py +183 -0
- confiture/core/anonymization/strategies/tokenization.py +334 -0
- confiture/core/anonymization/strategy.py +241 -0
- confiture/core/anonymization/syncer_audit.py +357 -0
- confiture/core/blue_green.py +683 -0
- confiture/core/builder.py +500 -0
- confiture/core/checksum.py +358 -0
- confiture/core/connection.py +132 -0
- confiture/core/differ.py +522 -0
- confiture/core/drift.py +564 -0
- confiture/core/dry_run.py +182 -0
- confiture/core/health.py +313 -0
- confiture/core/hooks/__init__.py +87 -0
- confiture/core/hooks/base.py +232 -0
- confiture/core/hooks/context.py +146 -0
- confiture/core/hooks/execution_strategies.py +57 -0
- confiture/core/hooks/observability.py +220 -0
- confiture/core/hooks/phases.py +53 -0
- confiture/core/hooks/registry.py +295 -0
- confiture/core/large_tables.py +775 -0
- confiture/core/linting/__init__.py +70 -0
- confiture/core/linting/composer.py +192 -0
- confiture/core/linting/libraries/__init__.py +17 -0
- confiture/core/linting/libraries/gdpr.py +168 -0
- confiture/core/linting/libraries/general.py +184 -0
- confiture/core/linting/libraries/hipaa.py +144 -0
- confiture/core/linting/libraries/pci_dss.py +104 -0
- confiture/core/linting/libraries/sox.py +120 -0
- confiture/core/linting/schema_linter.py +491 -0
- confiture/core/linting/versioning.py +151 -0
- confiture/core/locking.py +389 -0
- confiture/core/migration_generator.py +298 -0
- confiture/core/migrator.py +793 -0
- confiture/core/observability/__init__.py +44 -0
- confiture/core/observability/audit.py +323 -0
- confiture/core/observability/logging.py +187 -0
- confiture/core/observability/metrics.py +174 -0
- confiture/core/observability/tracing.py +192 -0
- confiture/core/pg_version.py +418 -0
- confiture/core/pool.py +406 -0
- confiture/core/risk/__init__.py +39 -0
- confiture/core/risk/predictor.py +188 -0
- confiture/core/risk/scoring.py +248 -0
- confiture/core/rollback_generator.py +388 -0
- confiture/core/schema_analyzer.py +769 -0
- confiture/core/schema_to_schema.py +590 -0
- confiture/core/security/__init__.py +32 -0
- confiture/core/security/logging.py +201 -0
- confiture/core/security/validation.py +416 -0
- confiture/core/signals.py +371 -0
- confiture/core/syncer.py +540 -0
- confiture/exceptions.py +192 -0
- confiture/integrations/__init__.py +0 -0
- confiture/models/__init__.py +0 -0
- confiture/models/lint.py +193 -0
- confiture/models/migration.py +180 -0
- confiture/models/schema.py +203 -0
- confiture/scenarios/__init__.py +36 -0
- confiture/scenarios/compliance.py +586 -0
- confiture/scenarios/ecommerce.py +199 -0
- confiture/scenarios/financial.py +253 -0
- confiture/scenarios/healthcare.py +315 -0
- confiture/scenarios/multi_tenant.py +340 -0
- confiture/scenarios/saas.py +295 -0
- confiture/testing/FRAMEWORK_API.md +722 -0
- confiture/testing/__init__.py +38 -0
- confiture/testing/fixtures/__init__.py +11 -0
- confiture/testing/fixtures/data_validator.py +229 -0
- confiture/testing/fixtures/migration_runner.py +167 -0
- confiture/testing/fixtures/schema_snapshotter.py +352 -0
- confiture/testing/frameworks/__init__.py +10 -0
- confiture/testing/frameworks/mutation.py +587 -0
- confiture/testing/frameworks/performance.py +479 -0
- confiture/testing/utils/__init__.py +0 -0
- fraiseql_confiture-0.3.4.dist-info/METADATA +438 -0
- fraiseql_confiture-0.3.4.dist-info/RECORD +119 -0
- fraiseql_confiture-0.3.4.dist-info/WHEEL +4 -0
- fraiseql_confiture-0.3.4.dist-info/entry_points.txt +2 -0
- fraiseql_confiture-0.3.4.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"""Data models for schema representation.
|
|
2
|
+
|
|
3
|
+
These models represent database schema objects (tables, columns, indexes, etc.)
|
|
4
|
+
in a structured format for diff detection and comparison.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
|
+
from enum import Enum
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ColumnType(str, Enum):
|
|
12
|
+
"""PostgreSQL column types."""
|
|
13
|
+
|
|
14
|
+
# Integer types
|
|
15
|
+
SMALLINT = "SMALLINT"
|
|
16
|
+
INTEGER = "INTEGER"
|
|
17
|
+
BIGINT = "BIGINT"
|
|
18
|
+
SERIAL = "SERIAL"
|
|
19
|
+
BIGSERIAL = "BIGSERIAL"
|
|
20
|
+
|
|
21
|
+
# Numeric types
|
|
22
|
+
NUMERIC = "NUMERIC"
|
|
23
|
+
DECIMAL = "DECIMAL"
|
|
24
|
+
REAL = "REAL"
|
|
25
|
+
DOUBLE_PRECISION = "DOUBLE PRECISION"
|
|
26
|
+
|
|
27
|
+
# Text types
|
|
28
|
+
VARCHAR = "VARCHAR"
|
|
29
|
+
CHAR = "CHAR"
|
|
30
|
+
TEXT = "TEXT"
|
|
31
|
+
|
|
32
|
+
# Boolean
|
|
33
|
+
BOOLEAN = "BOOLEAN"
|
|
34
|
+
|
|
35
|
+
# Date/Time
|
|
36
|
+
DATE = "DATE"
|
|
37
|
+
TIME = "TIME"
|
|
38
|
+
TIMESTAMP = "TIMESTAMP"
|
|
39
|
+
TIMESTAMPTZ = "TIMESTAMPTZ"
|
|
40
|
+
|
|
41
|
+
# UUID
|
|
42
|
+
UUID = "UUID"
|
|
43
|
+
|
|
44
|
+
# JSON
|
|
45
|
+
JSON = "JSON"
|
|
46
|
+
JSONB = "JSONB"
|
|
47
|
+
|
|
48
|
+
# Binary
|
|
49
|
+
BYTEA = "BYTEA"
|
|
50
|
+
|
|
51
|
+
# Unknown/Custom
|
|
52
|
+
UNKNOWN = "UNKNOWN"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@dataclass
|
|
56
|
+
class Column:
|
|
57
|
+
"""Represents a database column."""
|
|
58
|
+
|
|
59
|
+
name: str
|
|
60
|
+
type: ColumnType
|
|
61
|
+
nullable: bool = True
|
|
62
|
+
default: str | None = None
|
|
63
|
+
primary_key: bool = False
|
|
64
|
+
unique: bool = False
|
|
65
|
+
length: int | None = None # For VARCHAR(n), etc.
|
|
66
|
+
|
|
67
|
+
def __eq__(self, other: object) -> bool:
|
|
68
|
+
"""Compare columns for equality."""
|
|
69
|
+
if not isinstance(other, Column):
|
|
70
|
+
return NotImplemented
|
|
71
|
+
return (
|
|
72
|
+
self.name == other.name
|
|
73
|
+
and self.type == other.type
|
|
74
|
+
and self.nullable == other.nullable
|
|
75
|
+
and self.default == other.default
|
|
76
|
+
and self.primary_key == other.primary_key
|
|
77
|
+
and self.unique == other.unique
|
|
78
|
+
and self.length == other.length
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
def __hash__(self) -> int:
|
|
82
|
+
"""Make column hashable for use in sets."""
|
|
83
|
+
return hash(
|
|
84
|
+
(
|
|
85
|
+
self.name,
|
|
86
|
+
self.type,
|
|
87
|
+
self.nullable,
|
|
88
|
+
self.default,
|
|
89
|
+
self.primary_key,
|
|
90
|
+
self.unique,
|
|
91
|
+
self.length,
|
|
92
|
+
)
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@dataclass
|
|
97
|
+
class Table:
|
|
98
|
+
"""Represents a database table."""
|
|
99
|
+
|
|
100
|
+
name: str
|
|
101
|
+
columns: list[Column] = field(default_factory=list)
|
|
102
|
+
indexes: list[str] = field(default_factory=list) # Simplified for MVP
|
|
103
|
+
constraints: list[str] = field(default_factory=list) # Simplified for MVP
|
|
104
|
+
|
|
105
|
+
def get_column(self, name: str) -> Column | None:
|
|
106
|
+
"""Get column by name."""
|
|
107
|
+
for col in self.columns:
|
|
108
|
+
if col.name == name:
|
|
109
|
+
return col
|
|
110
|
+
return None
|
|
111
|
+
|
|
112
|
+
def has_column(self, name: str) -> bool:
|
|
113
|
+
"""Check if table has column."""
|
|
114
|
+
return self.get_column(name) is not None
|
|
115
|
+
|
|
116
|
+
def __eq__(self, other: object) -> bool:
|
|
117
|
+
"""Compare tables for equality."""
|
|
118
|
+
if not isinstance(other, Table):
|
|
119
|
+
return NotImplemented
|
|
120
|
+
return (
|
|
121
|
+
self.name == other.name
|
|
122
|
+
and self.columns == other.columns
|
|
123
|
+
and self.indexes == other.indexes
|
|
124
|
+
and self.constraints == other.constraints
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
@dataclass
|
|
129
|
+
class Schema:
|
|
130
|
+
"""Represents a complete database schema."""
|
|
131
|
+
|
|
132
|
+
tables: list[Table] = field(default_factory=list)
|
|
133
|
+
|
|
134
|
+
def get_table(self, name: str) -> Table | None:
|
|
135
|
+
"""Get table by name."""
|
|
136
|
+
for table in self.tables:
|
|
137
|
+
if table.name == name:
|
|
138
|
+
return table
|
|
139
|
+
return None
|
|
140
|
+
|
|
141
|
+
def has_table(self, name: str) -> bool:
|
|
142
|
+
"""Check if schema has table."""
|
|
143
|
+
return self.get_table(name) is not None
|
|
144
|
+
|
|
145
|
+
def table_names(self) -> list[str]:
|
|
146
|
+
"""Get list of all table names."""
|
|
147
|
+
return [table.name for table in self.tables]
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
@dataclass
|
|
151
|
+
class SchemaChange:
|
|
152
|
+
"""Represents a single change between two schemas."""
|
|
153
|
+
|
|
154
|
+
type: str # ADD_TABLE, DROP_TABLE, ADD_COLUMN, etc.
|
|
155
|
+
table: str | None = None
|
|
156
|
+
column: str | None = None
|
|
157
|
+
old_value: str | None = None
|
|
158
|
+
new_value: str | None = None
|
|
159
|
+
details: dict[str, str] | None = None
|
|
160
|
+
|
|
161
|
+
def __str__(self) -> str:
|
|
162
|
+
"""String representation of change."""
|
|
163
|
+
if self.type == "ADD_TABLE":
|
|
164
|
+
return f"ADD TABLE {self.table}"
|
|
165
|
+
elif self.type == "DROP_TABLE":
|
|
166
|
+
return f"DROP TABLE {self.table}"
|
|
167
|
+
elif self.type == "RENAME_TABLE":
|
|
168
|
+
return f"RENAME TABLE {self.old_value} TO {self.new_value}"
|
|
169
|
+
elif self.type == "ADD_COLUMN":
|
|
170
|
+
return f"ADD COLUMN {self.table}.{self.column}"
|
|
171
|
+
elif self.type == "DROP_COLUMN":
|
|
172
|
+
return f"DROP COLUMN {self.table}.{self.column}"
|
|
173
|
+
elif self.type == "RENAME_COLUMN":
|
|
174
|
+
return f"RENAME COLUMN {self.table}.{self.old_value} TO {self.new_value}"
|
|
175
|
+
elif self.type == "CHANGE_COLUMN_TYPE":
|
|
176
|
+
return f"CHANGE COLUMN TYPE {self.table}.{self.column} FROM {self.old_value} TO {self.new_value}"
|
|
177
|
+
elif self.type == "CHANGE_COLUMN_NULLABLE":
|
|
178
|
+
return f"CHANGE COLUMN NULLABLE {self.table}.{self.column} FROM {self.old_value} TO {self.new_value}"
|
|
179
|
+
elif self.type == "CHANGE_COLUMN_DEFAULT":
|
|
180
|
+
return f"CHANGE COLUMN DEFAULT {self.table}.{self.column}"
|
|
181
|
+
else:
|
|
182
|
+
return f"{self.type}: {self.table}.{self.column if self.column else ''}"
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
@dataclass
|
|
186
|
+
class SchemaDiff:
|
|
187
|
+
"""Represents the difference between two schemas."""
|
|
188
|
+
|
|
189
|
+
changes: list[SchemaChange] = field(default_factory=list)
|
|
190
|
+
|
|
191
|
+
def has_changes(self) -> bool:
|
|
192
|
+
"""Check if there are any changes."""
|
|
193
|
+
return len(self.changes) > 0
|
|
194
|
+
|
|
195
|
+
def count_by_type(self, change_type: str) -> int:
|
|
196
|
+
"""Count changes of a specific type."""
|
|
197
|
+
return sum(1 for c in self.changes if c.type == change_type)
|
|
198
|
+
|
|
199
|
+
def __str__(self) -> str:
|
|
200
|
+
"""String representation of diff."""
|
|
201
|
+
if not self.has_changes():
|
|
202
|
+
return "No changes detected"
|
|
203
|
+
return "\n".join(str(c) for c in self.changes)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""Real-world anonymization scenarios.
|
|
2
|
+
|
|
3
|
+
Provides ready-to-use anonymization profiles for common business domains:
|
|
4
|
+
- E-commerce: Customer data, orders, payments
|
|
5
|
+
- Healthcare: HIPAA-compliant PHI anonymization
|
|
6
|
+
- Financial: Loan applications, credit data
|
|
7
|
+
- SaaS: User accounts, subscription data
|
|
8
|
+
- Multi-tenant: Data isolation with deterministic seeding
|
|
9
|
+
|
|
10
|
+
Each scenario includes:
|
|
11
|
+
- Pre-configured strategy profiles
|
|
12
|
+
- Batch anonymization support
|
|
13
|
+
- Domain-specific validation
|
|
14
|
+
- Usage examples
|
|
15
|
+
|
|
16
|
+
Usage:
|
|
17
|
+
>>> from confiture.scenarios import ECommerceScenario
|
|
18
|
+
>>> data = {"first_name": "John", "email": "john@example.com"}
|
|
19
|
+
>>> anonymized = ECommerceScenario.anonymize(data)
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
# Import strategies to ensure registration (must come before scenario imports)
|
|
23
|
+
import confiture.core.anonymization.strategies # noqa: F401
|
|
24
|
+
from confiture.scenarios.ecommerce import ECommerceScenario
|
|
25
|
+
from confiture.scenarios.financial import FinancialScenario
|
|
26
|
+
from confiture.scenarios.healthcare import HealthcareScenario
|
|
27
|
+
from confiture.scenarios.multi_tenant import MultiTenantScenario
|
|
28
|
+
from confiture.scenarios.saas import SaaSScenario
|
|
29
|
+
|
|
30
|
+
__all__ = [
|
|
31
|
+
"ECommerceScenario",
|
|
32
|
+
"HealthcareScenario",
|
|
33
|
+
"FinancialScenario",
|
|
34
|
+
"SaaSScenario",
|
|
35
|
+
"MultiTenantScenario",
|
|
36
|
+
]
|