Files
bakery-ia/shared/utils/demo_id_transformer.py

113 lines
3.9 KiB
Python
Raw Normal View History

2026-01-21 17:17:16 +01:00
"""
Demo ID Transformer Utility
Provides XOR-based ID transformation for creating unique but deterministic
IDs across different demo tenants while maintaining cross-service consistency.
This ensures that:
1. Same base ID + same tenant ID = same transformed ID (deterministic)
2. Different tenant IDs = different transformed IDs (isolation)
3. Cross-service relationships are preserved (consistency)
"""
import uuid
from typing import Union
def transform_id(base_id: Union[str, uuid.UUID], tenant_id: Union[str, uuid.UUID]) -> uuid.UUID:
"""
Transform a base ID using XOR with tenant ID to create unique but deterministic IDs.
Args:
base_id: Original UUID (string or UUID object)
tenant_id: Tenant UUID (string or UUID object)
Returns:
Transformed UUID that is unique to this tenant but deterministic
Example:
>>> base_uuid = UUID('10000000-0000-0000-0000-000000000001')
>>> tenant_uuid = UUID('a1b2c3d4-e5f6-47a8-b9c0-d1e2f3a4b5c6')
>>> transform_id(base_uuid, tenant_uuid)
# Returns deterministic UUID based on XOR of the two
"""
# Convert inputs to UUID objects if they aren't already
if isinstance(base_id, str):
base_uuid = uuid.UUID(base_id)
else:
base_uuid = base_id
if isinstance(tenant_id, str):
tenant_uuid = uuid.UUID(tenant_id)
else:
tenant_uuid = tenant_id
# Convert UUIDs to 16-byte arrays
base_bytes = base_uuid.bytes
tenant_bytes = tenant_uuid.bytes
# Apply XOR transformation
transformed_bytes = bytes(b1 ^ b2 for b1, b2 in zip(base_bytes, tenant_bytes))
# Create new UUID from transformed bytes
transformed_uuid = uuid.UUID(bytes=transformed_bytes)
return transformed_uuid
def generate_deterministic_uuid_from_string(input_string: str, tenant_id: Union[str, uuid.UUID]) -> uuid.UUID:
"""
Generate a deterministic UUID from a string input and tenant ID.
Useful for transforming non-UUID identifiers (like SKUs) into UUIDs
while maintaining determinism across services.
Args:
input_string: String identifier (e.g., SKU, product code)
tenant_id: Tenant UUID for isolation
Returns:
Deterministic UUID based on the input string and tenant
"""
if isinstance(tenant_id, str):
tenant_uuid = uuid.UUID(tenant_id)
else:
tenant_uuid = tenant_id
# Create a combined string for hashing
combined = f"{input_string}-{tenant_uuid}"
# Use SHA-256 hash to create deterministic UUID
import hashlib
hash_obj = hashlib.sha256(combined.encode('utf-8'))
# Use first 16 bytes for UUID v5 namespace
hash_bytes = hash_obj.digest()[:16]
# Create UUID v5 using a standard namespace
namespace_uuid = uuid.NAMESPACE_DNS # Using DNS namespace as base
deterministic_uuid = uuid.uuid5(namespace_uuid, combined)
return deterministic_uuid
# Utility functions for common transformations
def transform_ingredient_id(base_ingredient_id: Union[str, uuid.UUID], tenant_id: Union[str, uuid.UUID]) -> uuid.UUID:
"""Transform an ingredient ID for a specific tenant"""
return transform_id(base_ingredient_id, tenant_id)
def transform_recipe_id(base_recipe_id: Union[str, uuid.UUID], tenant_id: Union[str, uuid.UUID]) -> uuid.UUID:
"""Transform a recipe ID for a specific tenant"""
return transform_id(base_recipe_id, tenant_id)
def transform_supplier_id(base_supplier_id: Union[str, uuid.UUID], tenant_id: Union[str, uuid.UUID]) -> uuid.UUID:
"""Transform a supplier ID for a specific tenant"""
return transform_id(base_supplier_id, tenant_id)
def transform_production_batch_id(base_batch_id: Union[str, uuid.UUID], tenant_id: Union[str, uuid.UUID]) -> uuid.UUID:
"""Transform a production batch ID for a specific tenant"""
return transform_id(base_batch_id, tenant_id)