django-nativemojo 0.1.15__py3-none-any.whl → 0.1.17__py3-none-any.whl

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 (221) hide show
  1. {django_nativemojo-0.1.15.dist-info → django_nativemojo-0.1.17.dist-info}/METADATA +3 -2
  2. django_nativemojo-0.1.17.dist-info/RECORD +302 -0
  3. mojo/__init__.py +1 -1
  4. mojo/apps/account/management/commands/serializer_admin.py +121 -1
  5. mojo/apps/account/migrations/0006_add_device_tracking_models.py +72 -0
  6. mojo/apps/account/migrations/0007_delete_userdevicelocation.py +16 -0
  7. mojo/apps/account/migrations/0008_userdevicelocation.py +33 -0
  8. mojo/apps/account/migrations/0009_geolocatedip_subnet.py +18 -0
  9. mojo/apps/account/migrations/0010_group_avatar.py +20 -0
  10. mojo/apps/account/migrations/0011_user_org_registereddevice_pushconfig_and_more.py +118 -0
  11. mojo/apps/account/migrations/0012_remove_pushconfig_apns_key_file_and_more.py +21 -0
  12. mojo/apps/account/migrations/0013_pushconfig_test_mode_alter_pushconfig_apns_enabled_and_more.py +28 -0
  13. mojo/apps/account/migrations/0014_notificationdelivery_data_payload_and_more.py +48 -0
  14. mojo/apps/account/models/__init__.py +2 -0
  15. mojo/apps/account/models/device.py +279 -0
  16. mojo/apps/account/models/group.py +294 -8
  17. mojo/apps/account/models/member.py +14 -1
  18. mojo/apps/account/models/push/__init__.py +4 -0
  19. mojo/apps/account/models/push/config.py +112 -0
  20. mojo/apps/account/models/push/delivery.py +93 -0
  21. mojo/apps/account/models/push/device.py +66 -0
  22. mojo/apps/account/models/push/template.py +99 -0
  23. mojo/apps/account/models/user.py +190 -17
  24. mojo/apps/account/rest/__init__.py +2 -0
  25. mojo/apps/account/rest/device.py +39 -0
  26. mojo/apps/account/rest/group.py +8 -0
  27. mojo/apps/account/rest/push.py +187 -0
  28. mojo/apps/account/rest/user.py +95 -5
  29. mojo/apps/account/services/__init__.py +1 -0
  30. mojo/apps/account/services/push.py +363 -0
  31. mojo/apps/aws/migrations/0001_initial.py +206 -0
  32. mojo/apps/aws/migrations/0002_emaildomain_can_recv_emaildomain_can_send_and_more.py +28 -0
  33. mojo/apps/aws/migrations/0003_mailbox_is_domain_default_mailbox_is_system_default_and_more.py +31 -0
  34. mojo/apps/aws/migrations/0004_s3bucket.py +39 -0
  35. mojo/apps/aws/migrations/0005_alter_emaildomain_region_delete_s3bucket.py +21 -0
  36. mojo/apps/aws/models/__init__.py +19 -0
  37. mojo/apps/aws/models/email_attachment.py +99 -0
  38. mojo/apps/aws/models/email_domain.py +218 -0
  39. mojo/apps/aws/models/email_template.py +132 -0
  40. mojo/apps/aws/models/incoming_email.py +197 -0
  41. mojo/apps/aws/models/mailbox.py +288 -0
  42. mojo/apps/aws/models/sent_message.py +175 -0
  43. mojo/apps/aws/rest/__init__.py +6 -0
  44. mojo/apps/aws/rest/email.py +33 -0
  45. mojo/apps/aws/rest/email_ops.py +183 -0
  46. mojo/apps/aws/rest/messages.py +32 -0
  47. mojo/apps/aws/rest/send.py +101 -0
  48. mojo/apps/aws/rest/sns.py +403 -0
  49. mojo/apps/aws/rest/templates.py +19 -0
  50. mojo/apps/aws/services/__init__.py +32 -0
  51. mojo/apps/aws/services/email.py +390 -0
  52. mojo/apps/aws/services/email_ops.py +548 -0
  53. mojo/apps/docit/__init__.py +6 -0
  54. mojo/apps/docit/markdown_plugins/syntax_highlight.py +25 -0
  55. mojo/apps/docit/markdown_plugins/toc.py +12 -0
  56. mojo/apps/docit/migrations/0001_initial.py +113 -0
  57. mojo/apps/docit/migrations/0002_alter_book_modified_by_alter_page_modified_by.py +26 -0
  58. mojo/apps/docit/migrations/0003_alter_book_group.py +20 -0
  59. mojo/apps/docit/models/__init__.py +17 -0
  60. mojo/apps/docit/models/asset.py +231 -0
  61. mojo/apps/docit/models/book.py +227 -0
  62. mojo/apps/docit/models/page.py +319 -0
  63. mojo/apps/docit/models/page_revision.py +203 -0
  64. mojo/apps/docit/rest/__init__.py +10 -0
  65. mojo/apps/docit/rest/asset.py +17 -0
  66. mojo/apps/docit/rest/book.py +22 -0
  67. mojo/apps/docit/rest/page.py +22 -0
  68. mojo/apps/docit/rest/page_revision.py +17 -0
  69. mojo/apps/docit/services/__init__.py +11 -0
  70. mojo/apps/docit/services/docit.py +315 -0
  71. mojo/apps/docit/services/markdown.py +44 -0
  72. mojo/apps/fileman/backends/s3.py +209 -0
  73. mojo/apps/fileman/models/file.py +45 -9
  74. mojo/apps/fileman/models/manager.py +269 -3
  75. mojo/apps/incident/migrations/0007_event_uid.py +18 -0
  76. mojo/apps/incident/migrations/0008_ticket_ticketnote.py +55 -0
  77. mojo/apps/incident/migrations/0009_incident_status.py +18 -0
  78. mojo/apps/incident/migrations/0010_event_country_code.py +18 -0
  79. mojo/apps/incident/migrations/0011_incident_country_code.py +18 -0
  80. mojo/apps/incident/migrations/0012_alter_incident_status.py +18 -0
  81. mojo/apps/incident/models/__init__.py +1 -0
  82. mojo/apps/incident/models/event.py +35 -0
  83. mojo/apps/incident/models/incident.py +2 -0
  84. mojo/apps/incident/models/ticket.py +62 -0
  85. mojo/apps/incident/reporter.py +21 -3
  86. mojo/apps/incident/rest/__init__.py +1 -0
  87. mojo/apps/incident/rest/ticket.py +43 -0
  88. mojo/apps/jobs/__init__.py +489 -0
  89. mojo/apps/jobs/adapters.py +24 -0
  90. mojo/apps/jobs/cli.py +616 -0
  91. mojo/apps/jobs/daemon.py +370 -0
  92. mojo/apps/jobs/examples/sample_jobs.py +376 -0
  93. mojo/apps/jobs/examples/webhook_examples.py +203 -0
  94. mojo/apps/jobs/handlers/__init__.py +5 -0
  95. mojo/apps/jobs/handlers/webhook.py +317 -0
  96. mojo/apps/jobs/job_engine.py +734 -0
  97. mojo/apps/jobs/keys.py +203 -0
  98. mojo/apps/jobs/local_queue.py +363 -0
  99. mojo/apps/jobs/management/__init__.py +3 -0
  100. mojo/apps/jobs/management/commands/__init__.py +3 -0
  101. mojo/apps/jobs/manager.py +1327 -0
  102. mojo/apps/jobs/migrations/0001_initial.py +97 -0
  103. mojo/apps/jobs/migrations/0002_alter_job_max_retries_joblog.py +39 -0
  104. mojo/apps/jobs/models/__init__.py +6 -0
  105. mojo/apps/jobs/models/job.py +441 -0
  106. mojo/apps/jobs/rest/__init__.py +2 -0
  107. mojo/apps/jobs/rest/control.py +466 -0
  108. mojo/apps/jobs/rest/jobs.py +421 -0
  109. mojo/apps/jobs/scheduler.py +571 -0
  110. mojo/apps/jobs/services/__init__.py +6 -0
  111. mojo/apps/jobs/services/job_actions.py +465 -0
  112. mojo/apps/jobs/settings.py +209 -0
  113. mojo/apps/logit/models/log.py +3 -0
  114. mojo/apps/metrics/__init__.py +8 -1
  115. mojo/apps/metrics/redis_metrics.py +198 -0
  116. mojo/apps/metrics/rest/__init__.py +3 -0
  117. mojo/apps/metrics/rest/categories.py +266 -0
  118. mojo/apps/metrics/rest/helpers.py +48 -0
  119. mojo/apps/metrics/rest/permissions.py +99 -0
  120. mojo/apps/metrics/rest/values.py +277 -0
  121. mojo/apps/metrics/utils.py +17 -0
  122. mojo/decorators/http.py +40 -1
  123. mojo/helpers/aws/__init__.py +11 -7
  124. mojo/helpers/aws/inbound_email.py +309 -0
  125. mojo/helpers/aws/kms.py +413 -0
  126. mojo/helpers/aws/ses_domain.py +959 -0
  127. mojo/helpers/crypto/__init__.py +1 -1
  128. mojo/helpers/crypto/utils.py +15 -0
  129. mojo/helpers/location/__init__.py +2 -0
  130. mojo/helpers/location/countries.py +262 -0
  131. mojo/helpers/location/geolocation.py +196 -0
  132. mojo/helpers/logit.py +37 -0
  133. mojo/helpers/redis/__init__.py +2 -0
  134. mojo/helpers/redis/adapter.py +606 -0
  135. mojo/helpers/redis/client.py +48 -0
  136. mojo/helpers/redis/pool.py +225 -0
  137. mojo/helpers/request.py +8 -0
  138. mojo/helpers/response.py +8 -0
  139. mojo/middleware/auth.py +1 -1
  140. mojo/middleware/cors.py +40 -0
  141. mojo/middleware/logging.py +131 -12
  142. mojo/middleware/mojo.py +5 -0
  143. mojo/models/rest.py +271 -57
  144. mojo/models/secrets.py +86 -0
  145. mojo/serializers/__init__.py +16 -10
  146. mojo/serializers/core/__init__.py +90 -0
  147. mojo/serializers/core/cache/__init__.py +121 -0
  148. mojo/serializers/core/cache/backends.py +518 -0
  149. mojo/serializers/core/cache/base.py +102 -0
  150. mojo/serializers/core/cache/disabled.py +181 -0
  151. mojo/serializers/core/cache/memory.py +287 -0
  152. mojo/serializers/core/cache/redis.py +533 -0
  153. mojo/serializers/core/cache/utils.py +454 -0
  154. mojo/serializers/{manager.py → core/manager.py} +53 -4
  155. mojo/serializers/core/serializer.py +475 -0
  156. mojo/serializers/{advanced/formats → formats}/csv.py +116 -139
  157. mojo/serializers/suggested_improvements.md +388 -0
  158. testit/client.py +1 -1
  159. testit/helpers.py +14 -0
  160. testit/runner.py +23 -6
  161. django_nativemojo-0.1.15.dist-info/RECORD +0 -234
  162. mojo/apps/notify/README.md +0 -91
  163. mojo/apps/notify/README_NOTIFICATIONS.md +0 -566
  164. mojo/apps/notify/admin.py +0 -52
  165. mojo/apps/notify/handlers/example_handlers.py +0 -516
  166. mojo/apps/notify/handlers/ses/__init__.py +0 -25
  167. mojo/apps/notify/handlers/ses/complaint.py +0 -25
  168. mojo/apps/notify/handlers/ses/message.py +0 -86
  169. mojo/apps/notify/management/commands/__init__.py +0 -1
  170. mojo/apps/notify/management/commands/process_notifications.py +0 -370
  171. mojo/apps/notify/mod +0 -0
  172. mojo/apps/notify/models/__init__.py +0 -12
  173. mojo/apps/notify/models/account.py +0 -128
  174. mojo/apps/notify/models/attachment.py +0 -24
  175. mojo/apps/notify/models/bounce.py +0 -68
  176. mojo/apps/notify/models/complaint.py +0 -40
  177. mojo/apps/notify/models/inbox.py +0 -113
  178. mojo/apps/notify/models/inbox_message.py +0 -173
  179. mojo/apps/notify/models/outbox.py +0 -129
  180. mojo/apps/notify/models/outbox_message.py +0 -288
  181. mojo/apps/notify/models/template.py +0 -30
  182. mojo/apps/notify/providers/aws.py +0 -73
  183. mojo/apps/notify/rest/ses.py +0 -0
  184. mojo/apps/notify/utils/__init__.py +0 -2
  185. mojo/apps/notify/utils/notifications.py +0 -404
  186. mojo/apps/notify/utils/parsing.py +0 -202
  187. mojo/apps/notify/utils/render.py +0 -144
  188. mojo/apps/tasks/README.md +0 -118
  189. mojo/apps/tasks/__init__.py +0 -44
  190. mojo/apps/tasks/manager.py +0 -644
  191. mojo/apps/tasks/rest/__init__.py +0 -2
  192. mojo/apps/tasks/rest/hooks.py +0 -0
  193. mojo/apps/tasks/rest/tasks.py +0 -76
  194. mojo/apps/tasks/runner.py +0 -439
  195. mojo/apps/tasks/task.py +0 -99
  196. mojo/apps/tasks/tq_handlers.py +0 -132
  197. mojo/helpers/crypto/__pycache__/hash.cpython-310.pyc +0 -0
  198. mojo/helpers/crypto/__pycache__/sign.cpython-310.pyc +0 -0
  199. mojo/helpers/crypto/__pycache__/utils.cpython-310.pyc +0 -0
  200. mojo/helpers/redis.py +0 -10
  201. mojo/models/meta.py +0 -262
  202. mojo/serializers/advanced/README.md +0 -363
  203. mojo/serializers/advanced/__init__.py +0 -247
  204. mojo/serializers/advanced/formats/__init__.py +0 -28
  205. mojo/serializers/advanced/formats/excel.py +0 -516
  206. mojo/serializers/advanced/formats/json.py +0 -239
  207. mojo/serializers/advanced/formats/response.py +0 -485
  208. mojo/serializers/advanced/serializer.py +0 -568
  209. mojo/serializers/optimized.py +0 -618
  210. {django_nativemojo-0.1.15.dist-info → django_nativemojo-0.1.17.dist-info}/LICENSE +0 -0
  211. {django_nativemojo-0.1.15.dist-info → django_nativemojo-0.1.17.dist-info}/NOTICE +0 -0
  212. {django_nativemojo-0.1.15.dist-info → django_nativemojo-0.1.17.dist-info}/WHEEL +0 -0
  213. /mojo/apps/{notify → aws/migrations}/__init__.py +0 -0
  214. /mojo/apps/{notify/handlers → docit/markdown_plugins}/__init__.py +0 -0
  215. /mojo/apps/{notify/management → docit/migrations}/__init__.py +0 -0
  216. /mojo/apps/{notify/providers → jobs/examples}/__init__.py +0 -0
  217. /mojo/apps/{notify/rest → jobs/migrations}/__init__.py +0 -0
  218. /mojo/{serializers → rest}/openapi.py +0 -0
  219. /mojo/serializers/{settings_example.py → examples/settings.py} +0 -0
  220. /mojo/{apps/notify/handlers/ses/bounce.py → serializers/formats/__init__.py} +0 -0
  221. /mojo/serializers/{advanced/formats → formats}/localizers.py +0 -0
@@ -0,0 +1,277 @@
1
+ from mojo import decorators as md
2
+ from mojo.apps import metrics
3
+ from mojo.helpers.response import JsonResponse
4
+ import mojo.errors
5
+ import datetime
6
+ from .helpers import check_view_permissions, check_write_permissions
7
+
8
+ # Documentation for time-series metrics endpoints
9
+ SERIES_GET_DOCS = {
10
+ "summary": "Get time-series values at a point in time",
11
+ "description": "Retrieves time-series metric values for multiple slugs at a specific datetime and granularity.",
12
+ "parameters": [
13
+ {
14
+ "name": "slugs",
15
+ "in": "query",
16
+ "required": True,
17
+ "schema": {"type": "string"},
18
+ "description": "Comma-separated list of slugs to fetch values for (e.g., 'user_activity_day,page_views')."
19
+ },
20
+ {
21
+ "name": "when",
22
+ "in": "query",
23
+ "required": True,
24
+ "schema": {"type": "string", "format": "date-time"},
25
+ "description": "The specific date/datetime to fetch values for."
26
+ },
27
+ {
28
+ "name": "granularity",
29
+ "in": "query",
30
+ "schema": {"type": "string", "default": "hours"},
31
+ "description": "Granularity of the data (e.g., 'hours', 'days', 'months', 'years')."
32
+ },
33
+ {
34
+ "name": "account",
35
+ "in": "query",
36
+ "schema": {"type": "string", "default": "public"},
37
+ "description": "Account identifier (e.g., 'public', 'global', or 'group_<id>')."
38
+ }
39
+ ],
40
+ "responses": {
41
+ "200": {
42
+ "description": "Successful response with metric values.",
43
+ "content": {
44
+ "application/json": {
45
+ "example": {
46
+ "response": {
47
+ "data": {
48
+ "user_activity_day": 22,
49
+ "page_views": 156
50
+ },
51
+ "slugs": ["user_activity_day", "page_views"],
52
+ "when": "2025-08-01T00:00:00",
53
+ "granularity": "days",
54
+ "account": "public",
55
+ "status": True
56
+ },
57
+ "status_code": 200
58
+ }
59
+ }
60
+ }
61
+ }
62
+ }
63
+ }
64
+
65
+ SERIES_POST_DOCS = {
66
+ "summary": "Get time-series values at a point in time (POST)",
67
+ "description": "Retrieves time-series metric values for multiple slugs at a specific datetime and granularity using POST method.",
68
+ "parameters": [
69
+ {
70
+ "name": "slugs",
71
+ "in": "body",
72
+ "required": True,
73
+ "schema": {"type": "string"},
74
+ "description": "Comma-separated list of slugs to fetch values for (e.g., 'user_activity_day,page_views')."
75
+ },
76
+ {
77
+ "name": "when",
78
+ "in": "body",
79
+ "required": True,
80
+ "schema": {"type": "string", "format": "date-time"},
81
+ "description": "The specific date/datetime to fetch values for."
82
+ },
83
+ {
84
+ "name": "granularity",
85
+ "in": "body",
86
+ "schema": {"type": "string", "default": "hours"},
87
+ "description": "Granularity of the data (e.g., 'hours', 'days', 'months', 'years')."
88
+ },
89
+ {
90
+ "name": "account",
91
+ "in": "body",
92
+ "schema": {"type": "string", "default": "public"},
93
+ "description": "Account identifier (e.g., 'public', 'global', or 'group_<id>')."
94
+ }
95
+ ]
96
+ }
97
+
98
+ # Documentation for simple value storage endpoints
99
+ SET_VALUE_DOCS = {
100
+ "summary": "Set simple global values",
101
+ "description": "Sets simple key-value pairs for global storage (not time-series).",
102
+ "parameters": [
103
+ {
104
+ "name": "slug",
105
+ "in": "body",
106
+ "required": True,
107
+ "schema": {"type": "string"},
108
+ "description": "The key identifier for the value to set."
109
+ },
110
+ {
111
+ "name": "value",
112
+ "in": "body",
113
+ "required": True,
114
+ "schema": {"type": "string"},
115
+ "description": "The value to store."
116
+ },
117
+ {
118
+ "name": "account",
119
+ "in": "body",
120
+ "schema": {"type": "string", "default": "public"},
121
+ "description": "Account identifier (e.g., 'public', 'global', or 'group_<id>')."
122
+ }
123
+ ],
124
+ "responses": {
125
+ "200": {
126
+ "description": "Successful value storage.",
127
+ "content": {
128
+ "application/json": {
129
+ "example": {
130
+ "response": {
131
+ "slug": "max_users",
132
+ "value": "1000",
133
+ "account": "public",
134
+ "status": True
135
+ },
136
+ "status_code": 200
137
+ }
138
+ }
139
+ }
140
+ }
141
+ }
142
+ }
143
+
144
+ VALUE_GET_DOCS = {
145
+ "summary": "Get simple global values",
146
+ "description": "Retrieves simple key-value pairs from global storage (not time-series). Supports multiple slugs.",
147
+ "parameters": [
148
+ {
149
+ "name": "slugs",
150
+ "in": "query",
151
+ "required": True,
152
+ "schema": {"type": "string"},
153
+ "description": "Comma-separated list of slugs to fetch values for (e.g., 'max_users,maintenance_mode')."
154
+ },
155
+ {
156
+ "name": "account",
157
+ "in": "query",
158
+ "schema": {"type": "string", "default": "public"},
159
+ "description": "Account identifier (e.g., 'public', 'global', or 'group_<id>')."
160
+ },
161
+ {
162
+ "name": "default",
163
+ "in": "query",
164
+ "schema": {"type": "string"},
165
+ "description": "Default value to return for missing keys."
166
+ }
167
+ ],
168
+ "responses": {
169
+ "200": {
170
+ "description": "Successful response with values.",
171
+ "content": {
172
+ "application/json": {
173
+ "example": {
174
+ "response": {
175
+ "data": {
176
+ "max_users": "1000",
177
+ "maintenance_mode": "false"
178
+ },
179
+ "slugs": ["max_users", "maintenance_mode"],
180
+ "account": "public",
181
+ "status": True
182
+ },
183
+ "status_code": 200
184
+ }
185
+ }
186
+ }
187
+ }
188
+ }
189
+ }
190
+
191
+
192
+ # Time-series metrics endpoints
193
+ @md.GET('series', docs=SERIES_GET_DOCS)
194
+ @md.requires_params("slugs")
195
+ def on_metrics_series(request):
196
+ """
197
+ Get time-series values for multiple slugs at a single point in time.
198
+ """
199
+ when = request.DATA.get_typed("when")
200
+ account = request.DATA.get("account", "public")
201
+ granularity = request.DATA.get("granularity", "hours")
202
+ slugs = request.DATA.get("slugs")
203
+
204
+ check_view_permissions(request, account)
205
+
206
+ # Fetch the values using our new method
207
+ result = metrics.fetch_values(slugs, when, granularity, account=account)
208
+ result['status'] = True
209
+
210
+ return JsonResponse(result)
211
+
212
+
213
+ @md.POST('series', docs=SERIES_POST_DOCS)
214
+ @md.requires_params("slugs")
215
+ def on_metrics_series_post(request):
216
+ """
217
+ POST version of the series endpoint - same functionality as GET.
218
+ """
219
+ return on_metrics_series(request)
220
+
221
+
222
+ # Simple value storage endpoints
223
+ @md.POST('value/set', docs=SET_VALUE_DOCS)
224
+ @md.requires_params("slug", "value")
225
+ def on_set_value(request):
226
+ """
227
+ Set a simple global value (not time-series).
228
+ """
229
+ slug = request.DATA.get("slug")
230
+ value = request.DATA.get("value")
231
+ account = request.DATA.get("account", "public")
232
+
233
+ check_write_permissions(request, account)
234
+
235
+ metrics.set_value(slug, value, account=account)
236
+
237
+ return JsonResponse({
238
+ "slug": slug,
239
+ "value": value,
240
+ "account": account,
241
+ "status": True
242
+ })
243
+
244
+
245
+ @md.GET('value/get', docs=VALUE_GET_DOCS)
246
+ @md.requires_params("slugs")
247
+ def on_get_value(request):
248
+ """
249
+ Get simple global values for multiple slugs (not time-series).
250
+ """
251
+ slugs = request.DATA.get("slugs")
252
+ account = request.DATA.get("account", "public")
253
+ default = request.DATA.get("default")
254
+
255
+ check_view_permissions(request, account)
256
+
257
+ # Handle comma-separated string input
258
+ if isinstance(slugs, str):
259
+ if ',' in slugs:
260
+ slugs_list = [s.strip() for s in slugs.split(',')]
261
+ else:
262
+ slugs_list = [slugs]
263
+ else:
264
+ slugs_list = slugs
265
+
266
+ # Fetch values for all slugs
267
+ data = {}
268
+ for slug in slugs_list:
269
+ value = metrics.get_value(slug, account=account, default=default)
270
+ data[slug] = value
271
+
272
+ return JsonResponse({
273
+ "data": data,
274
+ "slugs": slugs_list,
275
+ "account": account,
276
+ "status": True
277
+ })
@@ -179,6 +179,23 @@ def generate_perm_write_key(account):
179
179
  def generate_perm_view_key(account):
180
180
  return f"mets:{account}:perm:v"
181
181
 
182
+ def generate_accounts_key():
183
+ return f"mets:_accounts_"
184
+
185
+ def generate_value_key(slug, account):
186
+ """
187
+ Generate a Redis key for storing simple key-value pairs.
188
+
189
+ Args:
190
+ slug (str): The slug identifier for the value.
191
+ account (str): The account under which the value is stored.
192
+
193
+ Returns:
194
+ str: The Redis key for the value.
195
+ """
196
+ normalized_slug = normalize_slug(slug)
197
+ return f"mets:{account}:val:{normalized_slug}"
198
+
182
199
 
183
200
  def normalize_slug(slug):
184
201
  return slug.replace(':', '|')
mojo/decorators/http.py CHANGED
@@ -10,6 +10,7 @@ from mojo.helpers.response import JsonResponse
10
10
  from functools import wraps
11
11
  from mojo.helpers import modules
12
12
  from mojo.models import rest
13
+ from django.http import HttpResponse
13
14
  from mojo.apps import metrics
14
15
 
15
16
  logger = logit.get_logger("error", "error.log")
@@ -23,6 +24,7 @@ MOJO_APPEND_SLASH = settings.get("MOJO_APPEND_SLASH", False)
23
24
 
24
25
  API_METRICS = settings.get("API_METRICS", False)
25
26
  API_METRICS_GRANULARITY = settings.get("API_METRICS_GRANULARITY", "days")
27
+ EVENTS_ON_ERRORS = settings.get("EVENTS_ON_ERRORS", True)
26
28
 
27
29
 
28
30
  def dispatcher(request, *args, **kwargs):
@@ -37,6 +39,13 @@ def dispatcher(request, *args, **kwargs):
37
39
  if request.group is not None:
38
40
  request.group.touch()
39
41
  except ValueError:
42
+ if EVENTS_ON_ERRORS:
43
+ rest.MojoModel.class_report_incident(
44
+ details=f"Permission denied: Invalid group ID -> '{request.DATA.group}'",
45
+ event_type="rest_error",
46
+ request=request,
47
+ request_path=getattr(request, "path", None),
48
+ )
40
49
  return JsonResponse({"error": "Invalid group ID", "code": 400}, status=400)
41
50
  method_key = f"{key}__{request.method}"
42
51
  if method_key not in URLPATTERN_METHODS:
@@ -56,21 +65,51 @@ def dispatch_error_handler(func):
56
65
  try:
57
66
  if API_METRICS:
58
67
  metrics.record("api_calls", category="mojo_api", min_granularity=API_METRICS_GRANULARITY)
59
- return func(request, *args, **kwargs)
68
+ resp = func(request, *args, **kwargs)
69
+ if not isinstance(resp, HttpResponse) and isinstance(resp, dict):
70
+ return JsonResponse(resp)
71
+ return resp
60
72
  except mojo.errors.MojoException as err:
61
73
  if API_METRICS:
62
74
  metrics.record("api_errors", category="mojo_api", min_granularity=API_METRICS_GRANULARITY)
75
+ if EVENTS_ON_ERRORS:
76
+ rest.MojoModel.class_report_incident_for_user(
77
+ details=f"Rest Mojo Error: {err.reason}",
78
+ event_type="rest_error",
79
+ request_data=request.DATA,
80
+ request=request,
81
+ request_path=getattr(request, "path", None),
82
+ stack_trace=traceback.format_exc(),
83
+ )
63
84
  return JsonResponse({"error": err.reason, "code": err.code}, status=err.status)
64
85
  except ValueError as err:
65
86
  if API_METRICS:
66
87
  metrics.record("api_errors", category="mojo_api", min_granularity=API_METRICS_GRANULARITY)
67
88
  logger.exception(f"ValueErrror: {str(err)}, Path: {request.path}, IP: {request.META.get('REMOTE_ADDR')}")
89
+ if EVENTS_ON_ERRORS:
90
+ rest.MojoModel.class_report_incident_for_user(
91
+ details=f"Rest Value Error: {err}",
92
+ event_type="rest_error",
93
+ request_data=request.DATA,
94
+ request=request,
95
+ request_path=getattr(request, "path", None),
96
+ stack_trace=traceback.format_exc()
97
+ )
68
98
  return JsonResponse({"error": str(err), "code": 555 }, status=500)
69
99
  except Exception as err:
70
100
  if API_METRICS:
71
101
  metrics.record("api_errors", category="mojo_api", min_granularity=API_METRICS_GRANULARITY)
72
102
  # logger.exception(f"Unhandled REST Exception: {request.path}")
73
103
  logger.exception(f"Error: {str(err)}, Path: {request.path}, IP: {request.META.get('REMOTE_ADDR')}")
104
+ if EVENTS_ON_ERRORS:
105
+ rest.MojoModel.class_report_incident_for_user(
106
+ details=f"Rest Exception: {err}",
107
+ event_type="rest_error",
108
+ request_data=request.DATA,
109
+ request=request,
110
+ stack_trace=traceback.format_exc(),
111
+ request_path=getattr(request, "path", None),
112
+ )
74
113
  return JsonResponse({"error": str(err), "code": 500 }, status=500)
75
114
 
76
115
  return wrapper
@@ -7,6 +7,7 @@ A simple interface for working with AWS services.
7
7
  # Import service modules - these will be implemented
8
8
  from .s3 import S3Bucket, S3Item
9
9
  from .client import get_session
10
+ from .kms import KMSHelper
10
11
 
11
12
  # These will be implemented in future modules
12
13
  from .iam import IAMRole, IAMPolicy, IAMUser
@@ -17,25 +18,28 @@ from .ec2 import EC2Instance, EC2SecurityGroup
17
18
  __all__ = [
18
19
  # Base
19
20
  'get_session',
20
-
21
+
21
22
  # S3
22
23
  'S3Bucket',
23
24
  'S3Item',
24
-
25
+
26
+ # KMS
27
+ 'KMSHelper',
28
+
25
29
  # IAM
26
30
  'IAMRole',
27
31
  'IAMPolicy',
28
32
  'IAMUser',
29
-
33
+
30
34
  # SES
31
35
  'EmailSender',
32
36
  'EmailTemplate',
33
-
37
+
34
38
  # SNS
35
- 'SNSTopic',
39
+ 'SNSTopic',
36
40
  'SNSSubscription',
37
-
41
+
38
42
  # EC2
39
43
  'EC2Instance',
40
44
  'EC2SecurityGroup',
41
- ]
45
+ ]