altcodepro-polydb-python 2.5.8__tar.gz → 2.5.9__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.
- {altcodepro_polydb_python-2.5.8/src/altcodepro_polydb_python.egg-info → altcodepro_polydb_python-2.5.9}/PKG-INFO +1 -1
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/pyproject.toml +1 -1
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9/src/altcodepro_polydb_python.egg-info}/PKG-INFO +1 -1
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/altcodepro_polydb_python.egg-info/SOURCES.txt +1 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/__init__.py +1 -1
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/adapters/AzureTableStorageAdapter.py +27 -0
- altcodepro_polydb_python-2.5.9/tests/test_azure_table_id_property_persisted.py +175 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/LICENSE +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/MANIFEST.in +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/README.md +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/requirements-aws.txt +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/requirements-azure.txt +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/requirements-dev.txt +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/requirements-gcp.txt +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/requirements-generic.txt +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/requirements.txt +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/setup.cfg +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/setup.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/altcodepro_polydb_python.egg-info/dependency_links.txt +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/altcodepro_polydb_python.egg-info/requires.txt +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/altcodepro_polydb_python.egg-info/top_level.txt +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/PolyDB.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/adapters/AWSSecretsManagerAdapter.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/adapters/AzureBlobStorageAdapter.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/adapters/AzureFileStorageAdapter.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/adapters/AzureKeyVaultAdapter.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/adapters/AzureQueueAdapter.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/adapters/BlockchainBlobAdapter.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/adapters/BlockchainFileAdapter.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/adapters/BlockchainKVAdapter.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/adapters/BlockchainQueueAdapter.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/adapters/DynamoDBAdapter.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/adapters/EFSAdapter.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/adapters/FirestoreAdapter.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/adapters/GCPFilestoreAdapter.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/adapters/GCPPubSubAdapter.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/adapters/GCPSecretManagerAdapter.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/adapters/GCPStorageAdapter.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/adapters/KafkaQueueAdapter.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/adapters/MongoDBAdapter.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/adapters/PostgreSQLAdapter.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/adapters/RabbitMQAdapter.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/adapters/S3Adapter.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/adapters/S3CompatibleAdapter.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/adapters/SQSAdapter.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/adapters/VaultAdapter.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/adapters/VercelBlobAdapter.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/adapters/VercelFileAdapter.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/adapters/VercelKVAdapter.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/adapters/VercelQueueAdapter.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/adapters/__init__.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/advanced_query.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/audit/AuditStorage.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/audit/__init__.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/audit/context.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/audit/manager.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/audit/models.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/base/NoSQLKVAdapter.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/base/ObjectStorageAdapter.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/base/QueueAdapter.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/base/SecretsAdapter.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/base/SharedFilesAdapter.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/base/__init__.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/batch.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/cache.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/cloudDatabaseFactory.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/databaseFactory.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/decorators.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/errors.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/json_safe.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/models.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/monitoring.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/multitenancy.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/observability/__init__.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/observability/logging.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/py.typed +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/query.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/registry.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/retry.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/schema.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/security.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/services/__init__.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/services/compliance_service.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/services/security_service.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/types.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/utils.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/validation.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/tests/test_advanced_query_injection.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/tests/test_atomic_decrement.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/tests/test_audit_chain_cutover.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/tests/test_audit_hmac.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/tests/test_aws.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/tests/test_azure.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/tests/test_azure_queue_receipt_handle.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/tests/test_azure_table_empty_filter.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/tests/test_azure_table_unpack_entity.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/tests/test_blockchain.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/tests/test_cloud_factory.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/tests/test_field_encryption.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/tests/test_gcp.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/tests/test_kafka.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/tests/test_like_pattern_escaping.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/tests/test_mongodb.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/tests/test_multi_engine.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/tests/test_multitenancy_ddl_injection.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/tests/test_nosql_operator_injection.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/tests/test_observability.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/tests/test_postgresql.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/tests/test_queue_extend_delay_cancel.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/tests/test_rabbitmq.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/tests/test_redis_kv.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/tests/test_schema_ddl_injection.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/tests/test_secrets_aws.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/tests/test_secrets_vault.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/tests/test_session_vars.py +0 -0
- {altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/tests/test_vercel.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: altcodepro-polydb-python
|
|
3
|
-
Version: 2.5.
|
|
3
|
+
Version: 2.5.9
|
|
4
4
|
Summary: Production-ready multi-cloud database abstraction layer with connection pooling, retry logic, and thread safety
|
|
5
5
|
Author: AltCodePro
|
|
6
6
|
Project-URL: Homepage, https://github.com/altcodepro/polydb-python
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "altcodepro-polydb-python"
|
|
7
|
-
version = "2.5.
|
|
7
|
+
version = "2.5.9"
|
|
8
8
|
description = "Production-ready multi-cloud database abstraction layer with connection pooling, retry logic, and thread safety"
|
|
9
9
|
readme = { file = "README.md", content-type = "text/markdown" }
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: altcodepro-polydb-python
|
|
3
|
-
Version: 2.5.
|
|
3
|
+
Version: 2.5.9
|
|
4
4
|
Summary: Production-ready multi-cloud database abstraction layer with connection pooling, retry logic, and thread safety
|
|
5
5
|
Author: AltCodePro
|
|
6
6
|
Project-URL: Homepage, https://github.com/altcodepro/polydb-python
|
|
@@ -90,6 +90,7 @@ tests/test_aws.py
|
|
|
90
90
|
tests/test_azure.py
|
|
91
91
|
tests/test_azure_queue_receipt_handle.py
|
|
92
92
|
tests/test_azure_table_empty_filter.py
|
|
93
|
+
tests/test_azure_table_id_property_persisted.py
|
|
93
94
|
tests/test_azure_table_unpack_entity.py
|
|
94
95
|
tests/test_blockchain.py
|
|
95
96
|
tests/test_cloud_factory.py
|
|
@@ -4,7 +4,7 @@ PolyDB - Enterprise Cloud-Independent Database Abstraction
|
|
|
4
4
|
Full LINQ support, field-level audit, cache, soft delete, overflow storage
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
__version__ = "2.5.
|
|
7
|
+
__version__ = "2.5.9"
|
|
8
8
|
|
|
9
9
|
from .cloudDatabaseFactory import CloudDatabaseFactory
|
|
10
10
|
from .databaseFactory import DatabaseFactory
|
|
@@ -493,6 +493,33 @@ class AzureTableStorageAdapter(NoSQLKVAdapter):
|
|
|
493
493
|
table_client = self._get_table_client(model)
|
|
494
494
|
safe_pk = self._sanitize_pk_rk(pk)
|
|
495
495
|
safe_rk = self._sanitize_pk_rk(rk)
|
|
496
|
+
# Persist "id" as a REAL, queryable property whenever the
|
|
497
|
+
# caller's own data didn't already supply one -- not just
|
|
498
|
+
# synthesized onto this call's own return value below
|
|
499
|
+
# (`restored["id"] = safe_rk`), which only ever helped the
|
|
500
|
+
# ONE caller holding that return value. _query_raw already
|
|
501
|
+
# does the equivalent synthesis on the READ side (`out["id"]
|
|
502
|
+
# = ent_dict["RowKey"]` when absent) for a query's own
|
|
503
|
+
# results, but that can't fix a *filtered* read: a later
|
|
504
|
+
# `read_one(model, {"id": X, ...})` -- the ordinary
|
|
505
|
+
# id-addressed lookup every `update()`/`patch()` call makes
|
|
506
|
+
# for its own "before" read -- builds a real OData `id eq
|
|
507
|
+
# 'X'` filter against whatever property is actually named
|
|
508
|
+
# "id" on the stored entity. When the original create's own
|
|
509
|
+
# data never included one (true for essentially any model
|
|
510
|
+
# whose caller doesn't explicitly generate an id, default
|
|
511
|
+
# pk_field/rk_field or a custom mapping alike), that property
|
|
512
|
+
# never existed, so the filter matched nothing -- `before`
|
|
513
|
+
# came back None, and DatabaseFactory.update()'s own pk/rk
|
|
514
|
+
# recovery (see this file's own 2.5.7 changelog entry for the
|
|
515
|
+
# closely related bug) had nothing to recover from, falling
|
|
516
|
+
# through to a literal `str(None) == "None"` PartitionKey.
|
|
517
|
+
# Reproduced directly before this fix, not assumed: a create
|
|
518
|
+
# with no "id" in its own payload, followed by
|
|
519
|
+
# read_one(model, {"id": <the id the create call itself
|
|
520
|
+
# returned>}), always returned None.
|
|
521
|
+
if "id" not in data:
|
|
522
|
+
data = {**data, "id": safe_rk}
|
|
496
523
|
entity = self._pack_entity(model, safe_pk, safe_rk, data)
|
|
497
524
|
MAX_PROPERTY_CHARS = 30 * 1024
|
|
498
525
|
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"""Unit tests (no Azurite needed) for a real, reproduced bug: `_put_raw`
|
|
2
|
+
never persisted a real "id" property on the entity unless the caller's own
|
|
3
|
+
write payload already had one -- only synthesized it onto that one call's
|
|
4
|
+
own RETURN value (`restored["id"] = safe_rk`). A later id-addressed lookup
|
|
5
|
+
(`read_one(model, {"id": X, ...})`, the "before" read every
|
|
6
|
+
`DatabaseFactory.update()`/`patch()` call makes) builds a real OData
|
|
7
|
+
`id eq 'X'` filter against whatever property is actually named "id" on the
|
|
8
|
+
stored entity -- which, for any model whose caller doesn't explicitly set
|
|
9
|
+
"id" at create time (default pk_field/rk_field or a custom deterministic
|
|
10
|
+
mapping alike), never existed. `before` therefore always came back None,
|
|
11
|
+
cascading into `DatabaseFactory.update()`'s own pk/rk recovery logic
|
|
12
|
+
finding nothing to recover from and falling through to a literal
|
|
13
|
+
`str(None) == "None"` PartitionKey -- the same class of bug this file's own
|
|
14
|
+
2.5.7 changelog entry already documents for a related codepath.
|
|
15
|
+
|
|
16
|
+
Reproduces the bug on the OLD code shape directly (a create whose own
|
|
17
|
+
payload has no "id" never lands a real "id" property), then proves the fix
|
|
18
|
+
closes it (a create with no "id" DOES land a real, filterable "id"
|
|
19
|
+
property equal to the physical RowKey, and a create that ALREADY supplies
|
|
20
|
+
its own "id" is left completely untouched).
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
import pytest
|
|
24
|
+
|
|
25
|
+
pytest.importorskip("azure.data.tables") # adapter needs the SDK to import
|
|
26
|
+
|
|
27
|
+
from polydb.adapters.AzureTableStorageAdapter import AzureTableStorageAdapter # noqa: E402
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class _Model:
|
|
31
|
+
__name__ = "users"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class _FakeTableClient:
|
|
35
|
+
"""A minimal, real filter-evaluating fake -- unlike
|
|
36
|
+
test_azure_table_empty_filter.py's own fake (which only needs to tell
|
|
37
|
+
a None filter apart from a non-None one), this one has to actually
|
|
38
|
+
match `_query_raw`'s real OData-shaped filter string against stored
|
|
39
|
+
entities, since that's the exact mechanism this bug lives in."""
|
|
40
|
+
|
|
41
|
+
def __init__(self):
|
|
42
|
+
self.store: dict[tuple, dict] = {}
|
|
43
|
+
|
|
44
|
+
def upsert_entity(self, entity, **kw):
|
|
45
|
+
self.store[(entity["PartitionKey"], entity["RowKey"])] = dict(entity)
|
|
46
|
+
|
|
47
|
+
def query_entities(self, query_filter=None, **kw):
|
|
48
|
+
if query_filter is None:
|
|
49
|
+
return list(self.store.values())
|
|
50
|
+
conditions = []
|
|
51
|
+
for part in query_filter.split(" and "):
|
|
52
|
+
prop, _, raw_val = part.partition(" eq ")
|
|
53
|
+
if raw_val.startswith("'") and raw_val.endswith("'"):
|
|
54
|
+
val = raw_val[1:-1].replace("''", "'")
|
|
55
|
+
elif raw_val == "null":
|
|
56
|
+
val = None
|
|
57
|
+
elif raw_val in ("true", "false"):
|
|
58
|
+
val = raw_val == "true"
|
|
59
|
+
else:
|
|
60
|
+
val = float(raw_val) if "." in raw_val else int(raw_val)
|
|
61
|
+
conditions.append((prop, val))
|
|
62
|
+
return [
|
|
63
|
+
e for e in self.store.values() if all(e.get(prop) == val for prop, val in conditions)
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _adapter(fake):
|
|
68
|
+
adp = object.__new__(AzureTableStorageAdapter) # bypass live-connection __init__
|
|
69
|
+
adp._get_table_client = lambda model: fake
|
|
70
|
+
adp._blob_service = None
|
|
71
|
+
adp.object_storage = None
|
|
72
|
+
return adp
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def test_a_create_with_no_id_in_its_own_payload_still_lands_a_real_filterable_id_property():
|
|
76
|
+
"""The actual bug: users.identity_key-style deterministic addressing
|
|
77
|
+
(pk_field=rk_field="identity_key", "id" never in the create payload)."""
|
|
78
|
+
fake = _FakeTableClient()
|
|
79
|
+
adapter = _adapter(fake)
|
|
80
|
+
|
|
81
|
+
data = {
|
|
82
|
+
"identity_key": "email:sireesh.psvs@gmail.com",
|
|
83
|
+
"tenant_id": "altcodepro",
|
|
84
|
+
"email": "sireesh.psvs@gmail.com",
|
|
85
|
+
"auth_method": "password",
|
|
86
|
+
}
|
|
87
|
+
created = adapter._put_raw(
|
|
88
|
+
_Model, "email:sireesh.psvs@gmail.com", "email:sireesh.psvs@gmail.com", data
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
returned_id = created["id"]
|
|
92
|
+
assert returned_id == "email_sireesh.psvs@gmail.com" # sanitized (colon -> underscore)
|
|
93
|
+
|
|
94
|
+
# The load-bearing assertion: the STORED entity itself (not just this
|
|
95
|
+
# one call's own return value) now has a real "id" property.
|
|
96
|
+
stored = fake.store[("email_sireesh.psvs@gmail.com", "email_sireesh.psvs@gmail.com")]
|
|
97
|
+
assert stored["id"] == returned_id
|
|
98
|
+
|
|
99
|
+
# And the ordinary id-addressed lookup every update()/patch() "before"
|
|
100
|
+
# read performs now actually finds the row -- reproduced BROKEN before
|
|
101
|
+
# this fix (would have returned []).
|
|
102
|
+
found = adapter._query_raw(_Model, {"id": returned_id, "tenant_id": "altcodepro"}, None)
|
|
103
|
+
assert len(found) == 1
|
|
104
|
+
assert found[0]["email"] == "sireesh.psvs@gmail.com"
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def test_a_create_that_already_supplies_its_own_id_is_left_untouched():
|
|
108
|
+
fake = _FakeTableClient()
|
|
109
|
+
adapter = _adapter(fake)
|
|
110
|
+
|
|
111
|
+
data = {"id": "caller-chosen-id", "tenant_id": "t1", "name": "x"}
|
|
112
|
+
created = adapter._put_raw(_Model, "t1", "caller-chosen-id", data)
|
|
113
|
+
|
|
114
|
+
assert created["id"] == "caller-chosen-id"
|
|
115
|
+
stored = fake.store[("t1", "caller-chosen-id")]
|
|
116
|
+
assert stored["id"] == "caller-chosen-id"
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def test_two_different_identities_never_collide_and_both_are_independently_findable():
|
|
120
|
+
fake = _FakeTableClient()
|
|
121
|
+
adapter = _adapter(fake)
|
|
122
|
+
|
|
123
|
+
for email in ("sireesh.psvs@gmail.com", "pangaluri2010@gmail.com"):
|
|
124
|
+
key = f"email:{email}"
|
|
125
|
+
adapter._put_raw(
|
|
126
|
+
_Model,
|
|
127
|
+
key,
|
|
128
|
+
key,
|
|
129
|
+
{
|
|
130
|
+
"identity_key": key,
|
|
131
|
+
"tenant_id": "altcodepro",
|
|
132
|
+
"email": email,
|
|
133
|
+
},
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
assert len(fake.store) == 2
|
|
137
|
+
for email in ("sireesh.psvs@gmail.com", "pangaluri2010@gmail.com"):
|
|
138
|
+
rows = adapter._query_raw(_Model, {"email": email}, None)
|
|
139
|
+
assert len(rows) == 1 and rows[0]["email"] == email
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def test_repeat_put_for_the_same_identity_key_upserts_the_same_row_not_a_duplicate():
|
|
143
|
+
"""The actual, originally-reported symptom this whole chain of fixes
|
|
144
|
+
exists to close: a second signup attempt for the same email must
|
|
145
|
+
collapse onto the SAME physical row, never mint a new one."""
|
|
146
|
+
fake = _FakeTableClient()
|
|
147
|
+
adapter = _adapter(fake)
|
|
148
|
+
|
|
149
|
+
key = "email:sireesh.psvs@gmail.com"
|
|
150
|
+
adapter._put_raw(
|
|
151
|
+
_Model,
|
|
152
|
+
key,
|
|
153
|
+
key,
|
|
154
|
+
{
|
|
155
|
+
"identity_key": key,
|
|
156
|
+
"tenant_id": "altcodepro",
|
|
157
|
+
"email": "sireesh.psvs@gmail.com",
|
|
158
|
+
"display_name": "Sireesh",
|
|
159
|
+
},
|
|
160
|
+
)
|
|
161
|
+
adapter._put_raw(
|
|
162
|
+
_Model,
|
|
163
|
+
key,
|
|
164
|
+
key,
|
|
165
|
+
{
|
|
166
|
+
"identity_key": key,
|
|
167
|
+
"tenant_id": "altcodepro",
|
|
168
|
+
"email": "sireesh.psvs@gmail.com",
|
|
169
|
+
"display_name": "Sireesh Updated",
|
|
170
|
+
},
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
assert len(fake.store) == 1
|
|
174
|
+
only_row = next(iter(fake.store.values()))
|
|
175
|
+
assert only_row["display_name"] == "Sireesh Updated"
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/adapters/EFSAdapter.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
|
{altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/adapters/S3Adapter.py
RENAMED
|
File without changes
|
|
File without changes
|
{altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/adapters/SQSAdapter.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/adapters/__init__.py
RENAMED
|
File without changes
|
{altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/advanced_query.py
RENAMED
|
File without changes
|
{altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/audit/AuditStorage.py
RENAMED
|
File without changes
|
{altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/audit/__init__.py
RENAMED
|
File without changes
|
{altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/audit/context.py
RENAMED
|
File without changes
|
{altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/audit/manager.py
RENAMED
|
File without changes
|
{altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/audit/models.py
RENAMED
|
File without changes
|
{altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/base/NoSQLKVAdapter.py
RENAMED
|
File without changes
|
|
File without changes
|
{altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/base/QueueAdapter.py
RENAMED
|
File without changes
|
{altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/base/SecretsAdapter.py
RENAMED
|
File without changes
|
|
File without changes
|
{altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/base/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/cloudDatabaseFactory.py
RENAMED
|
File without changes
|
{altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/databaseFactory.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/multitenancy.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
|
{altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/src/polydb/services/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/tests/test_atomic_decrement.py
RENAMED
|
File without changes
|
{altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/tests/test_audit_chain_cutover.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
|
{altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/tests/test_cloud_factory.py
RENAMED
|
File without changes
|
{altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/tests/test_field_encryption.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/tests/test_multi_engine.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/tests/test_observability.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/tests/test_schema_ddl_injection.py
RENAMED
|
File without changes
|
|
File without changes
|
{altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/tests/test_secrets_vault.py
RENAMED
|
File without changes
|
{altcodepro_polydb_python-2.5.8 → altcodepro_polydb_python-2.5.9}/tests/test_session_vars.py
RENAMED
|
File without changes
|
|
File without changes
|