aws-python-helper 1.6.0__tar.gz → 1.7.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 (58) hide show
  1. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/PKG-INFO +1 -1
  2. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/api/base.py +21 -0
  3. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/api/fetcher.py +29 -24
  4. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/permissions/catalog.py +38 -1
  5. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper.egg-info/PKG-INFO +1 -1
  6. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/pyproject.toml +1 -1
  7. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/README.md +0 -0
  8. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/__init__.py +0 -0
  9. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/api/__init__.py +0 -0
  10. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/api/auth_middleware.py +0 -0
  11. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/api/auth_validators.py +0 -0
  12. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/api/dispatcher.py +0 -0
  13. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/api/exceptions.py +0 -0
  14. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/api/handler.py +0 -0
  15. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/context/__init__.py +0 -0
  16. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/context/session.py +0 -0
  17. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/context/state_validator.py +0 -0
  18. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/database/__init__.py +0 -0
  19. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/database/database_proxy.py +0 -0
  20. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/database/external_database_proxy.py +0 -0
  21. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/database/external_mongo_manager.py +0 -0
  22. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/database/mongo_manager.py +0 -0
  23. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/fargate/__init__.py +0 -0
  24. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/fargate/executor.py +0 -0
  25. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/fargate/fetcher.py +0 -0
  26. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/fargate/handler.py +0 -0
  27. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/fargate/task_base.py +0 -0
  28. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/invoker/__init__.py +0 -0
  29. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/invoker/api_client.py +0 -0
  30. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/invoker/exceptions.py +0 -0
  31. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/invoker/lambda_invoker.py +0 -0
  32. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/lambda_standalone/__init__.py +0 -0
  33. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/lambda_standalone/base.py +0 -0
  34. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/lambda_standalone/fetcher.py +0 -0
  35. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/lambda_standalone/handler.py +0 -0
  36. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/model_index_sync/__init__.py +0 -0
  37. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/model_index_sync/base.py +0 -0
  38. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/model_query/__init__.py +0 -0
  39. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/model_query/base.py +0 -0
  40. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/permissions/__init__.py +0 -0
  41. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/repository/__init__.py +0 -0
  42. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/repository/audit.py +0 -0
  43. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/repository/base.py +0 -0
  44. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/sns/__init__.py +0 -0
  45. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/sns/publisher.py +0 -0
  46. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/sqs/__init__.py +0 -0
  47. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/sqs/consumer_base.py +0 -0
  48. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/sqs/fetcher.py +0 -0
  49. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/sqs/handler.py +0 -0
  50. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/utils/__init__.py +0 -0
  51. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/utils/json_encoder.py +0 -0
  52. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/utils/response.py +0 -0
  53. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper/utils/serializer.py +0 -0
  54. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper.egg-info/SOURCES.txt +0 -0
  55. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper.egg-info/dependency_links.txt +0 -0
  56. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper.egg-info/requires.txt +0 -0
  57. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/aws_python_helper.egg-info/top_level.txt +0 -0
  58. {aws_python_helper-1.6.0 → aws_python_helper-1.7.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aws-python-helper
3
- Version: 1.6.0
3
+ Version: 1.7.0
4
4
  Summary: AWS Python Helper Framework
5
5
  Author-email: Fabian Claros <neufabiae@gmail.com>
6
6
  License-Expression: MIT
@@ -288,6 +288,27 @@ class API(ABC):
288
288
  'headers': self._response_headers
289
289
  }
290
290
 
291
+ @property
292
+ def public(self) -> bool:
293
+ """
294
+ Whether this endpoint answers without a token.
295
+
296
+ Declared here, next to the code, so the permission catalog can leave it
297
+ out: a permission for something nobody needs permission for is noise in
298
+ every role screen, and an administrator ticking it would be granting
299
+ nothing.
300
+
301
+ This does not make an endpoint public — terraform does, by leaving
302
+ `authorization` unset on its lambda. This says so, so the rest of the
303
+ framework can act on it.
304
+
305
+ class AuthLoginAPI(API):
306
+ @property
307
+ def public(self):
308
+ return True
309
+ """
310
+ return False
311
+
291
312
  @property
292
313
  def schema(self):
293
314
  """
@@ -7,6 +7,34 @@ import importlib.util
7
7
  from pathlib import Path
8
8
  from typing import List
9
9
 
10
+ def load_controller_class(file_path: str):
11
+ """The API class a controller file defines.
12
+
13
+ Shared so the permission catalog asks the class the same questions the
14
+ dispatcher does — a second way of finding it would answer differently the
15
+ day one of them changes.
16
+ """
17
+ spec = importlib.util.spec_from_file_location("api_module", file_path)
18
+ if not spec or not spec.loader:
19
+ raise ImportError(f"Could not load module spec from: {file_path}")
20
+
21
+ module = importlib.util.module_from_spec(spec)
22
+ spec.loader.exec_module(module)
23
+
24
+ for item_name in dir(module):
25
+ item = getattr(module, item_name)
26
+ if (isinstance(item, type) and
27
+ hasattr(item, 'process') and
28
+ item.__name__ not in ['API', 'ABC'] and
29
+ item.__module__ == module.__name__):
30
+ return item
31
+
32
+ raise ValueError(
33
+ f"No API class found in {file_path}\n"
34
+ f"Make sure your file exports a class that inherits from API"
35
+ )
36
+
37
+
10
38
  class Fetcher:
11
39
  """
12
40
  Dynamically load API controllers
@@ -187,30 +215,7 @@ class Fetcher:
187
215
  f"Expected file for endpoint '{self.endpoint}' with method '{self.method}'"
188
216
  )
189
217
 
190
- # Load module dynamically
191
- spec = importlib.util.spec_from_file_location("api_module", file_path)
192
- if not spec or not spec.loader:
193
- raise ImportError(f"Could not load module spec from: {file_path}")
194
-
195
- module = importlib.util.module_from_spec(spec)
196
- spec.loader.exec_module(module)
197
-
198
- # Search for a class that inherits from API
199
- controller_class = None
200
- for item_name in dir(module):
201
- item = getattr(module, item_name)
202
- if (isinstance(item, type) and
203
- hasattr(item, 'process') and
204
- item.__name__ not in ['API', 'ABC'] and
205
- item.__module__ == module.__name__):
206
- controller_class = item
207
- break
208
-
209
- if not controller_class:
210
- raise ValueError(
211
- f"No API class found in {file_path}\n"
212
- f"Make sure your file exports a class that inherits from API"
213
- )
218
+ controller_class = load_controller_class(file_path)
214
219
 
215
220
  # Cache the class (not the instance)
216
221
  self._cache[file_path] = controller_class
@@ -12,10 +12,14 @@ otherwise silently stop matching in the other, and an access check that never
12
12
  matches fails open or shut without anyone noticing.
13
13
  """
14
14
 
15
+ import logging
15
16
  import os
17
+ import re
16
18
  from typing import Dict, List
17
19
 
18
- from ..api.fetcher import Fetcher
20
+ from ..api.fetcher import Fetcher, load_controller_class
21
+
22
+ logger = logging.getLogger(__name__)
19
23
 
20
24
  # Cómo se llama el archivo según el método. `list` y `get` son ambos GET: uno
21
25
  # responde la colección, el otro un elemento.
@@ -46,6 +50,10 @@ def discover(api_root: str, service: str) -> List[Dict[str, str]]:
46
50
  if extension != ".py" or stem not in FILE_METHODS:
47
51
  continue
48
52
 
53
+ controller = _controller(os.path.join(current, filename))
54
+ if controller and getattr(controller(), "public", False):
55
+ continue
56
+
49
57
  relative = os.path.relpath(current, api_root)
50
58
  parts = [] if relative == "." else relative.split(os.sep)
51
59
  path = "/" + "/".join(parts)
@@ -62,11 +70,40 @@ def discover(api_root: str, service: str) -> List[Dict[str, str]]:
62
70
  "method": method,
63
71
  "path": path,
64
72
  "scope": "item" if stem == "get" else "collection",
73
+ "name": _readable(controller),
65
74
  })
66
75
 
67
76
  return sorted(found, key=lambda item: item["code"])
68
77
 
69
78
 
79
+ def _controller(file_path: str):
80
+ """The controller class, or None if it cannot be read.
81
+
82
+ None errs towards listing the endpoint. A permission that appears for a
83
+ public endpoint is noise; one that disappears for a protected endpoint
84
+ leaves the check with nothing to match and locks everybody out of it.
85
+ """
86
+ try:
87
+ return load_controller_class(file_path)
88
+ except Exception as exc:
89
+ logger.warning("Could not read %s, listing it anyway: %s", file_path, exc)
90
+ return None
91
+
92
+
93
+ def _readable(controller) -> str:
94
+ """The class name as words: `UsersInviteAPI` -> `Users Invite`.
95
+
96
+ A panel showing raw paths makes people read routing to answer "what does
97
+ this let someone do". The class already names the thing; this only spaces it
98
+ out. Empty when the class could not be read, so the panel falls back to the
99
+ path rather than inventing a name.
100
+ """
101
+ if controller is None:
102
+ return ""
103
+ name = re.sub(r"(API|Api)$", "", controller.__name__)
104
+ return re.sub(r"(?<=[a-z0-9])(?=[A-Z])", " ", name).strip()
105
+
106
+
70
107
  def service_wildcard(service: str) -> str:
71
108
  return f"{service}:*"
72
109
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aws-python-helper
3
- Version: 1.6.0
3
+ Version: 1.7.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.6.0"
7
+ version = "1.7.0"
8
8
  description = "AWS Python Helper Framework"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"