aws-python-helper 1.1.0__tar.gz → 1.2.0__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 (55) hide show
  1. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/PKG-INFO +1 -1
  2. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/model_query/base.py +63 -4
  3. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper.egg-info/PKG-INFO +1 -1
  4. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/pyproject.toml +1 -1
  5. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/README.md +0 -0
  6. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/__init__.py +0 -0
  7. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/api/__init__.py +0 -0
  8. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/api/auth_middleware.py +0 -0
  9. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/api/auth_validators.py +0 -0
  10. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/api/base.py +0 -0
  11. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/api/dispatcher.py +0 -0
  12. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/api/exceptions.py +0 -0
  13. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/api/fetcher.py +0 -0
  14. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/api/handler.py +0 -0
  15. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/context/__init__.py +0 -0
  16. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/context/session.py +0 -0
  17. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/context/state_validator.py +0 -0
  18. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/database/__init__.py +0 -0
  19. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/database/database_proxy.py +0 -0
  20. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/database/external_database_proxy.py +0 -0
  21. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/database/external_mongo_manager.py +0 -0
  22. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/database/mongo_manager.py +0 -0
  23. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/fargate/__init__.py +0 -0
  24. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/fargate/executor.py +0 -0
  25. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/fargate/fetcher.py +0 -0
  26. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/fargate/handler.py +0 -0
  27. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/fargate/task_base.py +0 -0
  28. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/invoker/__init__.py +0 -0
  29. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/invoker/api_client.py +0 -0
  30. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/invoker/exceptions.py +0 -0
  31. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/invoker/lambda_invoker.py +0 -0
  32. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/lambda_standalone/__init__.py +0 -0
  33. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/lambda_standalone/base.py +0 -0
  34. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/lambda_standalone/fetcher.py +0 -0
  35. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/lambda_standalone/handler.py +0 -0
  36. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/model_index_sync/__init__.py +0 -0
  37. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/model_index_sync/base.py +0 -0
  38. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/model_query/__init__.py +0 -0
  39. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/repository/__init__.py +0 -0
  40. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/repository/base.py +0 -0
  41. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/sns/__init__.py +0 -0
  42. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/sns/publisher.py +0 -0
  43. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/sqs/__init__.py +0 -0
  44. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/sqs/consumer_base.py +0 -0
  45. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/sqs/fetcher.py +0 -0
  46. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/sqs/handler.py +0 -0
  47. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/utils/__init__.py +0 -0
  48. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/utils/json_encoder.py +0 -0
  49. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/utils/response.py +0 -0
  50. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper/utils/serializer.py +0 -0
  51. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper.egg-info/SOURCES.txt +0 -0
  52. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper.egg-info/dependency_links.txt +0 -0
  53. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper.egg-info/requires.txt +0 -0
  54. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/aws_python_helper.egg-info/top_level.txt +0 -0
  55. {aws_python_helper-1.1.0 → aws_python_helper-1.2.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aws-python-helper
3
- Version: 1.1.0
3
+ Version: 1.2.0
4
4
  Summary: AWS Python Helper Framework
5
5
  Author-email: Fabian Claros <neufabiae@gmail.com>
6
6
  License-Expression: MIT
@@ -40,7 +40,7 @@ Response (via Lambda base run()):
40
40
  {"success": True, "data": [...]}
41
41
  """
42
42
 
43
- from typing import Any, List, Optional
43
+ from typing import Any, Dict, List, Optional, Union
44
44
  from bson import ObjectId
45
45
  from pydantic import BaseModel, ConfigDict, model_validator
46
46
 
@@ -111,6 +111,36 @@ class _ModelQuerySchema(BaseModel):
111
111
  return self
112
112
 
113
113
 
114
+ def _enforce_exclusions(
115
+ fields: Optional[dict], excluded: List[str]
116
+ ) -> dict:
117
+ """The projection the owner allows, whatever the caller asked for.
118
+
119
+ Mongo reads a projection as inclusive or exclusive, never both, so the
120
+ forbidden fields are dropped from an inclusive one and added to an exclusive
121
+ one. `_id` is ignored when deciding which it is: it may appear in either.
122
+ """
123
+ projection = dict(fields or {})
124
+ if not excluded:
125
+ return projection
126
+
127
+ inclusive = any(value for key, value in projection.items() if key != "_id")
128
+ if not inclusive:
129
+ for name in excluded:
130
+ projection[name] = 0
131
+ return projection
132
+
133
+ for name in excluded:
134
+ projection.pop(name, None)
135
+
136
+ # Pedir sólo el campo prohibido dejaría la proyección vacía, y una
137
+ # proyección vacía en Mongo devuelve el documento entero: exactamente lo
138
+ # contrario de lo buscado. Sin nada legítimo que pedir, sólo el id.
139
+ if not any(value for key, value in projection.items() if key != "_id"):
140
+ return {"_id": 1}
141
+ return projection
142
+
143
+
114
144
  class ModelQueryLambda(Lambda):
115
145
  """
116
146
  Base class for cross-service MongoDB query Lambdas.
@@ -120,19 +150,35 @@ class ModelQueryLambda(Lambda):
120
150
  """
121
151
 
122
152
  @property
123
- def allowed_collections(self) -> List[str]:
153
+ def allowed_collections(self) -> Union[List[str], Dict[str, dict]]:
124
154
  """
125
155
  Whitelist of collection names this Lambda is allowed to query.
126
156
 
127
157
  Must be overridden — an empty list rejects all requests.
128
158
 
129
- Example:
159
+ A list exposes each collection whole:
160
+
130
161
  @property
131
162
  def allowed_collections(self):
132
163
  return ["dockets", "tax_sales"]
164
+
165
+ A dict lets the owner keep fields in, whatever the caller projects. This
166
+ is not `fields`: that one is the caller saying what it wants, this one is
167
+ the owner saying what never leaves.
168
+
169
+ @property
170
+ def allowed_collections(self):
171
+ return {"dockets": {}, "users": {"exclude": ["password"]}}
133
172
  """
134
173
  return []
135
174
 
175
+ def excluded_fields(self, collection: str) -> List[str]:
176
+ """Fields this collection never returns. Empty for list-style whitelists."""
177
+ allowed = self.allowed_collections
178
+ if not isinstance(allowed, dict):
179
+ return []
180
+ return list((allowed.get(collection) or {}).get("exclude") or [])
181
+
136
182
  @property
137
183
  def schema(self):
138
184
  return _ModelQuerySchema
@@ -151,6 +197,16 @@ class ModelQueryLambda(Lambda):
151
197
  f"Collection '{collection}' is not allowed. Allowed: {allowed}"
152
198
  )
153
199
 
200
+ # Una pipeline puede renombrar un campo antes de que lo quitemos
201
+ # ($addFields, $replaceRoot, $lookup), así que no hay forma honesta de
202
+ # garantizar la exclusión sobre ella. Un $unset final daría una falsa
203
+ # sensación de seguridad, que es peor que no tener la función.
204
+ if self.excluded_fields(collection) and self.data.get("pipeline") is not None:
205
+ raise ValueError(
206
+ f"Collection '{collection}' hides fields, so it cannot be queried "
207
+ f"with a pipeline. Use 'filter' instead."
208
+ )
209
+
154
210
  async def process(self) -> Any:
155
211
  collection_name: str = self.data["collection"]
156
212
  database: str = self.data.get("database") or self.session.state
@@ -167,7 +223,10 @@ class ModelQueryLambda(Lambda):
167
223
  limit: int = self.data.get("limit") or 0
168
224
  skip: int = self.data.get("skip") or 0
169
225
 
170
- cursor = collection.find(mongo_filter, fields or {})
226
+ projection = _enforce_exclusions(
227
+ fields, self.excluded_fields(collection_name)
228
+ )
229
+ cursor = collection.find(mongo_filter, projection)
171
230
 
172
231
  if skip:
173
232
  cursor = cursor.skip(skip)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aws-python-helper
3
- Version: 1.1.0
3
+ Version: 1.2.0
4
4
  Summary: AWS Python Helper Framework
5
5
  Author-email: Fabian Claros <neufabiae@gmail.com>
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "aws-python-helper"
7
- version = "1.1.0"
7
+ version = "1.2.0"
8
8
  description = "AWS Python Helper Framework"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"