aws-python-helper 1.9.1__tar.gz → 1.9.3__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.
Files changed (62) hide show
  1. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/PKG-INFO +1 -1
  2. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/model_query/base.py +4 -0
  3. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/sns/publisher.py +11 -7
  4. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper.egg-info/PKG-INFO +1 -1
  5. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper.egg-info/SOURCES.txt +2 -1
  6. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/pyproject.toml +1 -1
  7. aws_python_helper-1.9.3/tests/test_sns_publisher.py +125 -0
  8. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/README.md +0 -0
  9. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/__init__.py +0 -0
  10. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/api/__init__.py +0 -0
  11. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/api/auth_middleware.py +0 -0
  12. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/api/auth_validators.py +0 -0
  13. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/api/base.py +0 -0
  14. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/api/dispatcher.py +0 -0
  15. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/api/exceptions.py +0 -0
  16. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/api/fetcher.py +0 -0
  17. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/api/handler.py +0 -0
  18. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/context/__init__.py +0 -0
  19. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/context/session.py +0 -0
  20. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/context/state_validator.py +0 -0
  21. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/database/__init__.py +0 -0
  22. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/database/database_proxy.py +0 -0
  23. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/database/external_database_proxy.py +0 -0
  24. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/database/external_mongo_manager.py +0 -0
  25. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/database/mongo_manager.py +0 -0
  26. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/fargate/__init__.py +0 -0
  27. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/fargate/executor.py +0 -0
  28. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/fargate/fetcher.py +0 -0
  29. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/fargate/handler.py +0 -0
  30. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/fargate/task_base.py +0 -0
  31. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/invoker/__init__.py +0 -0
  32. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/invoker/api_client.py +0 -0
  33. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/invoker/exceptions.py +0 -0
  34. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/invoker/lambda_invoker.py +0 -0
  35. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/lambda_standalone/__init__.py +0 -0
  36. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/lambda_standalone/base.py +0 -0
  37. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/lambda_standalone/fetcher.py +0 -0
  38. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/lambda_standalone/handler.py +0 -0
  39. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/model_index_sync/__init__.py +0 -0
  40. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/model_index_sync/base.py +0 -0
  41. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/model_query/__init__.py +0 -0
  42. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/monitoring/__init__.py +0 -0
  43. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/monitoring/run.py +0 -0
  44. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/permissions/__init__.py +0 -0
  45. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/permissions/catalog.py +0 -0
  46. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/repository/__init__.py +0 -0
  47. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/repository/audit.py +0 -0
  48. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/repository/base.py +0 -0
  49. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/sns/__init__.py +0 -0
  50. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/sqs/__init__.py +0 -0
  51. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/sqs/consumer_base.py +0 -0
  52. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/sqs/fetcher.py +0 -0
  53. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/sqs/handler.py +0 -0
  54. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/utils/__init__.py +0 -0
  55. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/utils/json_encoder.py +0 -0
  56. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/utils/response.py +0 -0
  57. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper/utils/serializer.py +0 -0
  58. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper.egg-info/dependency_links.txt +0 -0
  59. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper.egg-info/requires.txt +0 -0
  60. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/aws_python_helper.egg-info/top_level.txt +0 -0
  61. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/setup.cfg +0 -0
  62. {aws_python_helper-1.9.1 → aws_python_helper-1.9.3}/tests/test_audit_stamp.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aws-python-helper
3
- Version: 1.9.1
3
+ Version: 1.9.3
4
4
  Summary: AWS Python Helper Framework
5
5
  Author-email: Fabian Claros <neufabiae@gmail.com>
6
6
  License-Expression: MIT
@@ -88,6 +88,10 @@ class _ModelQuerySchema(BaseModel):
88
88
  model_config = ConfigDict(extra="ignore", arbitrary_types_allowed=True)
89
89
 
90
90
  collection: str
91
+ # La base donde buscar. Estaba documentada como parámetro pero no declarada,
92
+ # y `extra="ignore"` la descartaba en silencio: la consulta terminaba en la
93
+ # base del estado de la sesión y devolvía vacío sin error.
94
+ database: Optional[str] = None
91
95
  filter: Optional[dict] = None
92
96
  pipeline: Optional[list] = None
93
97
  fields: Optional[dict] = None
@@ -276,13 +276,18 @@ class SNSPublisher(ABC):
276
276
 
277
277
  return formatted
278
278
 
279
- def _format_message(self, message: Dict[str, Any], index: int) -> Dict[str, Any]:
279
+ def _format_message(
280
+ self, message: Dict[str, Any], index: Optional[int] = None
281
+ ) -> Dict[str, Any]:
280
282
  """
281
283
  Formats the message for SNS
282
-
284
+
283
285
  Args:
284
286
  message: Message to format
285
-
287
+ index: Position inside a batch. `publish_batch` needs an `Id` per
288
+ entry; plain `publish` does not take one at all, so a single
289
+ message leaves it out.
290
+
286
291
  Returns:
287
292
  Formatted message
288
293
  """
@@ -305,10 +310,9 @@ class SNSPublisher(ABC):
305
310
  session_json = self._serialize_message(session_dict)
306
311
  attributes['session'] = base64.b64encode(session_json.encode('utf-8')).decode('utf-8')
307
312
 
308
- params = {
309
- 'Id': str(index),
310
- 'Message': content
311
- }
313
+ params: Dict[str, Any] = {'Message': content}
314
+ if index is not None:
315
+ params['Id'] = str(index)
312
316
 
313
317
  # Add subject if exists
314
318
  if subject:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aws-python-helper
3
- Version: 1.9.1
3
+ Version: 1.9.3
4
4
  Summary: AWS Python Helper Framework
5
5
  Author-email: Fabian Claros <neufabiae@gmail.com>
6
6
  License-Expression: MIT
@@ -56,4 +56,5 @@ aws_python_helper/utils/__init__.py
56
56
  aws_python_helper/utils/json_encoder.py
57
57
  aws_python_helper/utils/response.py
58
58
  aws_python_helper/utils/serializer.py
59
- tests/test_audit_stamp.py
59
+ tests/test_audit_stamp.py
60
+ tests/test_sns_publisher.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "aws-python-helper"
7
- version = "1.9.1"
7
+ version = "1.9.3"
8
8
  description = "AWS Python Helper Framework"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -0,0 +1,125 @@
1
+ """What the publisher must hand to boto3.
2
+
3
+ Runs on its own, no dependencies:
4
+
5
+ python tests/test_sns_publisher.py
6
+
7
+ These exist because a single publish crashed in production with
8
+ `_format_message() missing 1 required positional argument: 'index'`. Every test
9
+ until then mocked `publish` itself, so the code that builds the boto3 call was
10
+ never executed — the mock was above the bug.
11
+
12
+ So here the fake is the **boto3 client**, not the publisher: everything the
13
+ publisher actually does runs.
14
+ """
15
+
16
+ import asyncio
17
+ import json
18
+ import os
19
+ import sys
20
+
21
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))
22
+
23
+ from aws_python_helper.sns.publisher import MAX_BATCH_ENTRIES, SNSPublisher # noqa: E402
24
+
25
+ failures = []
26
+
27
+
28
+ def check(label, condition, detail=""):
29
+ print(f" {'ok ' if condition else 'FAIL'} {label}{' ' + detail if detail else ''}")
30
+ if not condition:
31
+ failures.append(label)
32
+
33
+
34
+ class FakeSNS:
35
+ """Se queja igual que boto3 ante un parámetro que SNS no acepta."""
36
+
37
+ VALID_PUBLISH = {"TopicArn", "Message", "Subject", "MessageAttributes",
38
+ "MessageStructure", "MessageDeduplicationId", "MessageGroupId"}
39
+
40
+ def __init__(self):
41
+ self.published = []
42
+ self.batches = []
43
+
44
+ def publish(self, **kwargs):
45
+ extra = set(kwargs) - self.VALID_PUBLISH
46
+ if extra:
47
+ raise TypeError(f"Unknown parameter in input: {sorted(extra)}")
48
+ self.published.append(kwargs)
49
+ return {"MessageId": f"m{len(self.published)}"}
50
+
51
+ def publish_batch(self, TopicArn, PublishBatchRequestEntries):
52
+ if len(PublishBatchRequestEntries) > MAX_BATCH_ENTRIES:
53
+ raise Exception("TooManyEntriesInBatchRequest")
54
+ for entry in PublishBatchRequestEntries:
55
+ if "Id" not in entry:
56
+ raise Exception("An entry in a batch needs an Id")
57
+ self.batches.append(PublishBatchRequestEntries)
58
+ return {"Successful": [{"MessageId": f"b{i}"} for i in range(len(PublishBatchRequestEntries))],
59
+ "Failed": []}
60
+
61
+
62
+ class Publisher(SNSPublisher):
63
+ pass
64
+
65
+
66
+ def publisher():
67
+ p = Publisher("arn:aws:sns:us-east-2:1:Topic-dev")
68
+ p._sns_client = FakeSNS()
69
+ return p
70
+
71
+
72
+ async def main():
73
+ print("a single message")
74
+ p = publisher()
75
+ result = await p.publish({"content": {"run_id": "abc", "status": "launched"}})
76
+ sent = p._sns_client.published[0]
77
+ check("it publishes", len(p._sns_client.published) == 1)
78
+ check("no Id, which publish does not accept", "Id" not in sent)
79
+ check("the body travels", json.loads(sent["Message"])["run_id"] == "abc")
80
+ check("returns the message id", result == "m1")
81
+
82
+ print("\na single message with attributes and subject")
83
+ p = publisher()
84
+ await p.publish({
85
+ "content": {"x": 1},
86
+ "attributes": {"service": "business"},
87
+ "subject": "hello",
88
+ })
89
+ sent = p._sns_client.published[0]
90
+ check("subject", sent.get("Subject") == "hello")
91
+ check("attributes", "MessageAttributes" in sent)
92
+
93
+ print("\na batch")
94
+ p = publisher()
95
+ ok, failed = await p.publish([{"content": {"n": i}} for i in range(188)])
96
+ check("split into tens", len(p._sns_client.batches) == 19,
97
+ f"batches={len(p._sns_client.batches)}")
98
+ check("none over the limit", all(len(b) <= MAX_BATCH_ENTRIES for b in p._sns_client.batches))
99
+ check("every entry has an Id", all("Id" in e for b in p._sns_client.batches for e in b))
100
+ check("ids unique inside each call",
101
+ all(len({e["Id"] for e in b}) == len(b) for b in p._sns_client.batches))
102
+ check("all reported published", len(ok) == 188 and not failed)
103
+
104
+ print("\na batch of one still goes as a batch")
105
+ p = publisher()
106
+ await p.publish([{"content": {"n": 1}}])
107
+ check("one call", len(p._sns_client.batches) == 1)
108
+ check("nothing through publish()", not p._sns_client.published)
109
+
110
+ print("\na message with no content is refused, not sent")
111
+ p = publisher()
112
+ try:
113
+ await p.publish({"attributes": {"service": "business"}})
114
+ check("raises", False)
115
+ except ValueError:
116
+ check("raises", True)
117
+ check("nothing published", not p._sns_client.published)
118
+
119
+
120
+ asyncio.run(main())
121
+
122
+ if failures:
123
+ print(f"\n{len(failures)} failed: {failures}")
124
+ raise SystemExit(1)
125
+ print("\nall good")