howler-api 2.13.0.dev329__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.
Potentially problematic release.
This version of howler-api might be problematic. Click here for more details.
- howler/__init__.py +0 -0
- howler/actions/__init__.py +167 -0
- howler/actions/add_label.py +111 -0
- howler/actions/add_to_bundle.py +159 -0
- howler/actions/change_field.py +76 -0
- howler/actions/demote.py +160 -0
- howler/actions/example_plugin.py +104 -0
- howler/actions/prioritization.py +93 -0
- howler/actions/promote.py +147 -0
- howler/actions/remove_from_bundle.py +133 -0
- howler/actions/remove_label.py +111 -0
- howler/actions/transition.py +200 -0
- howler/api/__init__.py +249 -0
- howler/api/base.py +88 -0
- howler/api/socket.py +114 -0
- howler/api/v1/__init__.py +97 -0
- howler/api/v1/action.py +372 -0
- howler/api/v1/analytic.py +748 -0
- howler/api/v1/auth.py +382 -0
- howler/api/v1/borealis.py +101 -0
- howler/api/v1/configs.py +55 -0
- howler/api/v1/dossier.py +222 -0
- howler/api/v1/help.py +28 -0
- howler/api/v1/hit.py +1181 -0
- howler/api/v1/notebook.py +82 -0
- howler/api/v1/overview.py +191 -0
- howler/api/v1/search.py +715 -0
- howler/api/v1/template.py +206 -0
- howler/api/v1/tool.py +183 -0
- howler/api/v1/user.py +414 -0
- howler/api/v1/utils/__init__.py +0 -0
- howler/api/v1/utils/etag.py +84 -0
- howler/api/v1/view.py +288 -0
- howler/app.py +235 -0
- howler/common/README.md +144 -0
- howler/common/__init__.py +0 -0
- howler/common/classification.py +979 -0
- howler/common/classification.yml +107 -0
- howler/common/exceptions.py +167 -0
- howler/common/hexdump.py +48 -0
- howler/common/iprange.py +171 -0
- howler/common/loader.py +154 -0
- howler/common/logging/__init__.py +241 -0
- howler/common/logging/audit.py +138 -0
- howler/common/logging/format.py +38 -0
- howler/common/net.py +79 -0
- howler/common/net_static.py +1494 -0
- howler/common/random_user.py +316 -0
- howler/common/swagger.py +117 -0
- howler/config.py +64 -0
- howler/cronjobs/__init__.py +29 -0
- howler/cronjobs/retention.py +61 -0
- howler/cronjobs/rules.py +274 -0
- howler/cronjobs/view_cleanup.py +88 -0
- howler/datastore/README.md +112 -0
- howler/datastore/__init__.py +0 -0
- howler/datastore/bulk.py +72 -0
- howler/datastore/collection.py +2327 -0
- howler/datastore/constants.py +117 -0
- howler/datastore/exceptions.py +41 -0
- howler/datastore/howler_store.py +105 -0
- howler/datastore/migrations/fix_process.py +41 -0
- howler/datastore/operations.py +130 -0
- howler/datastore/schemas.py +90 -0
- howler/datastore/store.py +231 -0
- howler/datastore/support/__init__.py +0 -0
- howler/datastore/support/build.py +214 -0
- howler/datastore/support/schemas.py +90 -0
- howler/datastore/types.py +22 -0
- howler/error.py +91 -0
- howler/external/__init__.py +0 -0
- howler/external/generate_mitre.py +96 -0
- howler/external/generate_sigma_rules.py +31 -0
- howler/external/generate_tlds.py +47 -0
- howler/external/reindex_data.py +46 -0
- howler/external/wipe_databases.py +58 -0
- howler/gunicorn_config.py +25 -0
- howler/healthz.py +47 -0
- howler/helper/__init__.py +0 -0
- howler/helper/azure.py +50 -0
- howler/helper/discover.py +59 -0
- howler/helper/hit.py +236 -0
- howler/helper/oauth.py +247 -0
- howler/helper/search.py +92 -0
- howler/helper/workflow.py +110 -0
- howler/helper/ws.py +378 -0
- howler/odm/README.md +102 -0
- howler/odm/__init__.py +1 -0
- howler/odm/base.py +1504 -0
- howler/odm/charter.txt +146 -0
- howler/odm/helper.py +416 -0
- howler/odm/howler_enum.py +25 -0
- howler/odm/models/__init__.py +0 -0
- howler/odm/models/action.py +33 -0
- howler/odm/models/analytic.py +90 -0
- howler/odm/models/assemblyline.py +48 -0
- howler/odm/models/aws.py +23 -0
- howler/odm/models/azure.py +16 -0
- howler/odm/models/cbs.py +44 -0
- howler/odm/models/config.py +558 -0
- howler/odm/models/dossier.py +33 -0
- howler/odm/models/ecs/__init__.py +0 -0
- howler/odm/models/ecs/agent.py +17 -0
- howler/odm/models/ecs/autonomous_system.py +16 -0
- howler/odm/models/ecs/client.py +149 -0
- howler/odm/models/ecs/cloud.py +141 -0
- howler/odm/models/ecs/code_signature.py +27 -0
- howler/odm/models/ecs/container.py +32 -0
- howler/odm/models/ecs/dns.py +62 -0
- howler/odm/models/ecs/egress.py +10 -0
- howler/odm/models/ecs/elf.py +74 -0
- howler/odm/models/ecs/email.py +122 -0
- howler/odm/models/ecs/error.py +14 -0
- howler/odm/models/ecs/event.py +140 -0
- howler/odm/models/ecs/faas.py +24 -0
- howler/odm/models/ecs/file.py +84 -0
- howler/odm/models/ecs/geo.py +30 -0
- howler/odm/models/ecs/group.py +18 -0
- howler/odm/models/ecs/hash.py +16 -0
- howler/odm/models/ecs/host.py +17 -0
- howler/odm/models/ecs/http.py +37 -0
- howler/odm/models/ecs/ingress.py +12 -0
- howler/odm/models/ecs/interface.py +21 -0
- howler/odm/models/ecs/network.py +30 -0
- howler/odm/models/ecs/observer.py +45 -0
- howler/odm/models/ecs/organization.py +12 -0
- howler/odm/models/ecs/os.py +21 -0
- howler/odm/models/ecs/pe.py +17 -0
- howler/odm/models/ecs/process.py +216 -0
- howler/odm/models/ecs/registry.py +26 -0
- howler/odm/models/ecs/related.py +45 -0
- howler/odm/models/ecs/rule.py +51 -0
- howler/odm/models/ecs/server.py +24 -0
- howler/odm/models/ecs/threat.py +247 -0
- howler/odm/models/ecs/tls.py +58 -0
- howler/odm/models/ecs/url.py +51 -0
- howler/odm/models/ecs/user.py +57 -0
- howler/odm/models/ecs/user_agent.py +20 -0
- howler/odm/models/ecs/vulnerability.py +41 -0
- howler/odm/models/gcp.py +16 -0
- howler/odm/models/hit.py +356 -0
- howler/odm/models/howler_data.py +328 -0
- howler/odm/models/lead.py +33 -0
- howler/odm/models/localized_label.py +13 -0
- howler/odm/models/overview.py +16 -0
- howler/odm/models/pivot.py +40 -0
- howler/odm/models/template.py +24 -0
- howler/odm/models/user.py +83 -0
- howler/odm/models/view.py +34 -0
- howler/odm/random_data.py +888 -0
- howler/odm/randomizer.py +606 -0
- howler/patched.py +5 -0
- howler/plugins/__init__.py +25 -0
- howler/plugins/config.py +123 -0
- howler/remote/__init__.py +0 -0
- howler/remote/datatypes/README.md +355 -0
- howler/remote/datatypes/__init__.py +98 -0
- howler/remote/datatypes/counters.py +63 -0
- howler/remote/datatypes/events.py +66 -0
- howler/remote/datatypes/hash.py +206 -0
- howler/remote/datatypes/lock.py +42 -0
- howler/remote/datatypes/queues/__init__.py +0 -0
- howler/remote/datatypes/queues/comms.py +59 -0
- howler/remote/datatypes/queues/multi.py +32 -0
- howler/remote/datatypes/queues/named.py +93 -0
- howler/remote/datatypes/queues/priority.py +215 -0
- howler/remote/datatypes/set.py +118 -0
- howler/remote/datatypes/user_quota_tracker.py +54 -0
- howler/security/__init__.py +253 -0
- howler/security/socket.py +108 -0
- howler/security/utils.py +185 -0
- howler/services/__init__.py +0 -0
- howler/services/action_service.py +111 -0
- howler/services/analytic_service.py +128 -0
- howler/services/auth_service.py +323 -0
- howler/services/config_service.py +128 -0
- howler/services/dossier_service.py +252 -0
- howler/services/event_service.py +93 -0
- howler/services/hit_service.py +893 -0
- howler/services/jwt_service.py +158 -0
- howler/services/lucene_service.py +286 -0
- howler/services/notebook_service.py +119 -0
- howler/services/overview_service.py +44 -0
- howler/services/template_service.py +45 -0
- howler/services/user_service.py +330 -0
- howler/utils/__init__.py +0 -0
- howler/utils/annotations.py +28 -0
- howler/utils/chunk.py +38 -0
- howler/utils/dict_utils.py +200 -0
- howler/utils/isotime.py +17 -0
- howler/utils/list_utils.py +11 -0
- howler/utils/lucene.py +77 -0
- howler/utils/path.py +27 -0
- howler/utils/socket_utils.py +61 -0
- howler/utils/str_utils.py +256 -0
- howler/utils/uid.py +47 -0
- howler_api-2.13.0.dev329.dist-info/METADATA +71 -0
- howler_api-2.13.0.dev329.dist-info/RECORD +200 -0
- howler_api-2.13.0.dev329.dist-info/WHEEL +4 -0
- howler_api-2.13.0.dev329.dist-info/entry_points.txt +8 -0
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
# Adjective list and noun list from: https://github.com/williexu/random_username
|
|
2
|
+
# Modified to allow the list to be always in memory in a predefined format
|
|
3
|
+
|
|
4
|
+
import random
|
|
5
|
+
|
|
6
|
+
ADJECTIVES = [
|
|
7
|
+
"abject",
|
|
8
|
+
"accomplished",
|
|
9
|
+
"adept",
|
|
10
|
+
"adoring",
|
|
11
|
+
"adorable",
|
|
12
|
+
"adventurous",
|
|
13
|
+
"affectionate",
|
|
14
|
+
"agreeable",
|
|
15
|
+
"alert",
|
|
16
|
+
"amazed",
|
|
17
|
+
"amazing",
|
|
18
|
+
"ambitious",
|
|
19
|
+
"amiable",
|
|
20
|
+
"amused",
|
|
21
|
+
"approachable",
|
|
22
|
+
"ardent",
|
|
23
|
+
"articulate",
|
|
24
|
+
"artistic",
|
|
25
|
+
"attractive",
|
|
26
|
+
"awed",
|
|
27
|
+
"awesome",
|
|
28
|
+
"beautiful",
|
|
29
|
+
"blissful",
|
|
30
|
+
"brainy",
|
|
31
|
+
"brave",
|
|
32
|
+
"bright",
|
|
33
|
+
"brilliant",
|
|
34
|
+
"bubbly",
|
|
35
|
+
"capable",
|
|
36
|
+
"charismatic",
|
|
37
|
+
"charming",
|
|
38
|
+
"cheerful",
|
|
39
|
+
"chic",
|
|
40
|
+
"clean",
|
|
41
|
+
"colourful",
|
|
42
|
+
"compassionate",
|
|
43
|
+
"considerate",
|
|
44
|
+
"content",
|
|
45
|
+
"courageous",
|
|
46
|
+
"courteous",
|
|
47
|
+
"creative",
|
|
48
|
+
"cultured",
|
|
49
|
+
"curious",
|
|
50
|
+
"cute",
|
|
51
|
+
"dazzling",
|
|
52
|
+
"dear",
|
|
53
|
+
"debonair",
|
|
54
|
+
"determined",
|
|
55
|
+
"diligent",
|
|
56
|
+
"diplomatic",
|
|
57
|
+
"dynamic",
|
|
58
|
+
"eager",
|
|
59
|
+
"ecstatic",
|
|
60
|
+
"educated",
|
|
61
|
+
"efficient",
|
|
62
|
+
"elegant",
|
|
63
|
+
"empathic",
|
|
64
|
+
"energetic",
|
|
65
|
+
"engaging",
|
|
66
|
+
"enthusiastic",
|
|
67
|
+
"euphoric",
|
|
68
|
+
"excellent",
|
|
69
|
+
"excited",
|
|
70
|
+
"expert",
|
|
71
|
+
"fabulous",
|
|
72
|
+
"faithful",
|
|
73
|
+
"fantastic",
|
|
74
|
+
"favourable",
|
|
75
|
+
"fearless",
|
|
76
|
+
"fervent",
|
|
77
|
+
"fit",
|
|
78
|
+
"focused",
|
|
79
|
+
"fond",
|
|
80
|
+
"friendly",
|
|
81
|
+
"funny",
|
|
82
|
+
"generous",
|
|
83
|
+
"gleeful",
|
|
84
|
+
"gorgeous",
|
|
85
|
+
"happy",
|
|
86
|
+
"helpful",
|
|
87
|
+
"holistic",
|
|
88
|
+
"honest",
|
|
89
|
+
"humorous",
|
|
90
|
+
"imaginative",
|
|
91
|
+
"incredible",
|
|
92
|
+
"innocent",
|
|
93
|
+
"insightful",
|
|
94
|
+
"intelligent",
|
|
95
|
+
"inventive",
|
|
96
|
+
"joyful",
|
|
97
|
+
"jubilant",
|
|
98
|
+
"kind",
|
|
99
|
+
"knowledgeable",
|
|
100
|
+
"likable",
|
|
101
|
+
"loving",
|
|
102
|
+
"loyal",
|
|
103
|
+
"magnificent",
|
|
104
|
+
"marvelous",
|
|
105
|
+
"mellow",
|
|
106
|
+
"merciful",
|
|
107
|
+
"nice",
|
|
108
|
+
"optimistic",
|
|
109
|
+
"organized",
|
|
110
|
+
"passionate",
|
|
111
|
+
"patient",
|
|
112
|
+
"peaceful",
|
|
113
|
+
"perfect",
|
|
114
|
+
"persistent",
|
|
115
|
+
"pleased",
|
|
116
|
+
"plucky",
|
|
117
|
+
"polite",
|
|
118
|
+
"powerful",
|
|
119
|
+
"prideful",
|
|
120
|
+
"productive",
|
|
121
|
+
"proficient",
|
|
122
|
+
"qualified",
|
|
123
|
+
"reliable",
|
|
124
|
+
"relieved",
|
|
125
|
+
"remarkable",
|
|
126
|
+
"resolved",
|
|
127
|
+
"resourceful",
|
|
128
|
+
"responsible",
|
|
129
|
+
"sensible",
|
|
130
|
+
"sincere",
|
|
131
|
+
"sleek",
|
|
132
|
+
"smart",
|
|
133
|
+
"solid",
|
|
134
|
+
"spectacular",
|
|
135
|
+
"splendid",
|
|
136
|
+
"spirited",
|
|
137
|
+
"splendid",
|
|
138
|
+
"stellar",
|
|
139
|
+
"stunning",
|
|
140
|
+
"stupendous",
|
|
141
|
+
"sugary",
|
|
142
|
+
"super",
|
|
143
|
+
"superior",
|
|
144
|
+
"thoughtful",
|
|
145
|
+
"thrifty",
|
|
146
|
+
"thrilled",
|
|
147
|
+
"trusting",
|
|
148
|
+
"truthful",
|
|
149
|
+
"trustworthy",
|
|
150
|
+
"upbeat",
|
|
151
|
+
"warmhearted",
|
|
152
|
+
"winged",
|
|
153
|
+
"witty",
|
|
154
|
+
"wonderful",
|
|
155
|
+
"wondrous",
|
|
156
|
+
"worldly",
|
|
157
|
+
"zesty",
|
|
158
|
+
]
|
|
159
|
+
|
|
160
|
+
NOUNS = [
|
|
161
|
+
"abalone",
|
|
162
|
+
"antelope",
|
|
163
|
+
"apples",
|
|
164
|
+
"apricots",
|
|
165
|
+
"baboon",
|
|
166
|
+
"bagels",
|
|
167
|
+
"basmati",
|
|
168
|
+
"bass",
|
|
169
|
+
"bittern",
|
|
170
|
+
"boa",
|
|
171
|
+
"boars",
|
|
172
|
+
"bobolink",
|
|
173
|
+
"buck",
|
|
174
|
+
"burritos",
|
|
175
|
+
"buzzard",
|
|
176
|
+
"cake",
|
|
177
|
+
"camel",
|
|
178
|
+
"cardinal",
|
|
179
|
+
"caribou",
|
|
180
|
+
"caviar",
|
|
181
|
+
"chamois",
|
|
182
|
+
"cheese",
|
|
183
|
+
"cheetah",
|
|
184
|
+
"chile",
|
|
185
|
+
"chough",
|
|
186
|
+
"chowder",
|
|
187
|
+
"clam",
|
|
188
|
+
"coati",
|
|
189
|
+
"cockatoo",
|
|
190
|
+
"coconut",
|
|
191
|
+
"cod",
|
|
192
|
+
"cordial",
|
|
193
|
+
"cow",
|
|
194
|
+
"crackers",
|
|
195
|
+
"crane",
|
|
196
|
+
"cur",
|
|
197
|
+
"curlew",
|
|
198
|
+
"dingo",
|
|
199
|
+
"dinosaur",
|
|
200
|
+
"dotterel",
|
|
201
|
+
"doughnut",
|
|
202
|
+
"dove",
|
|
203
|
+
"doves",
|
|
204
|
+
"dunbird",
|
|
205
|
+
"eagle",
|
|
206
|
+
"eggs",
|
|
207
|
+
"eland",
|
|
208
|
+
"falcon",
|
|
209
|
+
"ferret",
|
|
210
|
+
"fish",
|
|
211
|
+
"flamingo",
|
|
212
|
+
"garlic",
|
|
213
|
+
"gelding",
|
|
214
|
+
"gnu",
|
|
215
|
+
"granola",
|
|
216
|
+
"hare",
|
|
217
|
+
"hawk",
|
|
218
|
+
"heron",
|
|
219
|
+
"hoopoe",
|
|
220
|
+
"hyena",
|
|
221
|
+
"icecream",
|
|
222
|
+
"iguana",
|
|
223
|
+
"jaguar",
|
|
224
|
+
"kitten",
|
|
225
|
+
"lapwing",
|
|
226
|
+
"lemur",
|
|
227
|
+
"leopard",
|
|
228
|
+
"lion",
|
|
229
|
+
"lizard",
|
|
230
|
+
"llama",
|
|
231
|
+
"locust",
|
|
232
|
+
"lollies",
|
|
233
|
+
"macaw",
|
|
234
|
+
"mackerel",
|
|
235
|
+
"magpie",
|
|
236
|
+
"mallard",
|
|
237
|
+
"mandrill",
|
|
238
|
+
"mare",
|
|
239
|
+
"meerkat",
|
|
240
|
+
"moth",
|
|
241
|
+
"muesli",
|
|
242
|
+
"mussel",
|
|
243
|
+
"oatmeal",
|
|
244
|
+
"ocelot",
|
|
245
|
+
"oil",
|
|
246
|
+
"orange",
|
|
247
|
+
"oryx",
|
|
248
|
+
"otter",
|
|
249
|
+
"owl",
|
|
250
|
+
"paella",
|
|
251
|
+
"pear",
|
|
252
|
+
"pepper",
|
|
253
|
+
"pie",
|
|
254
|
+
"piglet",
|
|
255
|
+
"plover",
|
|
256
|
+
"polenta",
|
|
257
|
+
"ponie",
|
|
258
|
+
"porpoise",
|
|
259
|
+
"poultry",
|
|
260
|
+
"pretzels",
|
|
261
|
+
"pudding",
|
|
262
|
+
"pup",
|
|
263
|
+
"quiche",
|
|
264
|
+
"raisins",
|
|
265
|
+
"rat",
|
|
266
|
+
"relish",
|
|
267
|
+
"rhino",
|
|
268
|
+
"rice",
|
|
269
|
+
"ruffs",
|
|
270
|
+
"salami",
|
|
271
|
+
"salt",
|
|
272
|
+
"sardines",
|
|
273
|
+
"sausage",
|
|
274
|
+
"seafowl",
|
|
275
|
+
"seagull",
|
|
276
|
+
"seahorse",
|
|
277
|
+
"shads",
|
|
278
|
+
"sheep",
|
|
279
|
+
"smelt",
|
|
280
|
+
"snail",
|
|
281
|
+
"snipe",
|
|
282
|
+
"stork",
|
|
283
|
+
"swift",
|
|
284
|
+
"syrup",
|
|
285
|
+
"tacos",
|
|
286
|
+
"teal",
|
|
287
|
+
"termite",
|
|
288
|
+
"thrush",
|
|
289
|
+
"thrushe",
|
|
290
|
+
"tomatoe",
|
|
291
|
+
"tortoise",
|
|
292
|
+
"toucan",
|
|
293
|
+
"truffle",
|
|
294
|
+
"tuna",
|
|
295
|
+
"unicorn",
|
|
296
|
+
"venison",
|
|
297
|
+
"viper",
|
|
298
|
+
"wasp",
|
|
299
|
+
"weaver",
|
|
300
|
+
"whiting",
|
|
301
|
+
"widgeon",
|
|
302
|
+
"wigeon",
|
|
303
|
+
"wildfowl",
|
|
304
|
+
"zebra",
|
|
305
|
+
]
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def random_user(digits=2, delimiter="-"):
|
|
309
|
+
"Generate a random user"
|
|
310
|
+
adjective = random.choice(ADJECTIVES)
|
|
311
|
+
noun = random.choice(NOUNS)
|
|
312
|
+
if digits > 0:
|
|
313
|
+
num = str(random.randrange(10**digits))
|
|
314
|
+
return f"{adjective}{delimiter}{noun}{delimiter}{num}"
|
|
315
|
+
else:
|
|
316
|
+
return f"{adjective}{delimiter}{noun}"
|
howler/common/swagger.py
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
import re
|
|
3
|
+
from functools import wraps
|
|
4
|
+
from typing import Any, Callable, Optional, cast
|
|
5
|
+
|
|
6
|
+
from flasgger import utils
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def monkey_patched_parse(obj, process_doc, endpoint=None, verb=None):
|
|
10
|
+
"""We monkey patch flasgger's built-in parse-docstring to work better with our format"""
|
|
11
|
+
short_desc: Optional[str] = None
|
|
12
|
+
long_desc: Optional[str] = None
|
|
13
|
+
|
|
14
|
+
doc = inspect.getdoc(obj)
|
|
15
|
+
|
|
16
|
+
if doc:
|
|
17
|
+
short_desc = doc.splitlines()[0]
|
|
18
|
+
long_desc = f"```\n{doc}\n```"
|
|
19
|
+
|
|
20
|
+
return short_desc, long_desc, None
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
utils.parse_docstring = monkey_patched_parse
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
RESPONSES = {
|
|
27
|
+
status_code: {
|
|
28
|
+
"description": "Something went wrong with your request",
|
|
29
|
+
"schema": {
|
|
30
|
+
"type": "object",
|
|
31
|
+
"properties": {
|
|
32
|
+
"api_response": {"type": "string"},
|
|
33
|
+
"api_error_message": {"type": "string"},
|
|
34
|
+
"api_warning": {"type": "string"},
|
|
35
|
+
"api_server_version": {"type": "string"},
|
|
36
|
+
"api_status_code": {"type": "integer"},
|
|
37
|
+
},
|
|
38
|
+
"example": {
|
|
39
|
+
"api_response": "Example response",
|
|
40
|
+
"api_error_message": "Example error",
|
|
41
|
+
"api_warning": "Example warning",
|
|
42
|
+
"api_server_version": "1.0",
|
|
43
|
+
"api_status_code": status_code,
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
}
|
|
47
|
+
for status_code in [400, 401, 403, 404]
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def generate_swagger_docs(responses: dict[int, str] = {}): # noqa: C901
|
|
52
|
+
"Generate swagger documentation for a given endpoint"
|
|
53
|
+
|
|
54
|
+
def decorator(function: Callable): # noqa: ANN202
|
|
55
|
+
"Decorator function for generating the swagger docs"
|
|
56
|
+
func_signature = inspect.signature(function)
|
|
57
|
+
func_doc = inspect.getdoc(function)
|
|
58
|
+
if module := inspect.getmodule(function):
|
|
59
|
+
module_name = module.__name__
|
|
60
|
+
func_path = f"{module_name}.{function.__name__}" if module_name else function.__name__
|
|
61
|
+
|
|
62
|
+
path_params = [
|
|
63
|
+
{
|
|
64
|
+
"name": param,
|
|
65
|
+
"in": "path",
|
|
66
|
+
"type": "string",
|
|
67
|
+
}
|
|
68
|
+
for param in func_signature.parameters
|
|
69
|
+
if param not in ["kwargs", "_"] and not param.startswith("_")
|
|
70
|
+
]
|
|
71
|
+
|
|
72
|
+
query_params: list[dict[str, Any]] = []
|
|
73
|
+
if func_doc:
|
|
74
|
+
for section in func_doc.split("\n\n"):
|
|
75
|
+
lines = section.splitlines()
|
|
76
|
+
if not lines[0].lower().endswith("arguments:"):
|
|
77
|
+
continue
|
|
78
|
+
|
|
79
|
+
lines = [re.sub(r" =>.+", "", line).strip() for line in lines[1:]]
|
|
80
|
+
|
|
81
|
+
for line in lines:
|
|
82
|
+
if line.lower() == "none" or "=>" not in line:
|
|
83
|
+
continue
|
|
84
|
+
|
|
85
|
+
if ": " in line:
|
|
86
|
+
name, type = line.split(": ")
|
|
87
|
+
else:
|
|
88
|
+
name = line
|
|
89
|
+
type = None
|
|
90
|
+
|
|
91
|
+
query_params.append({"name": name, "in": "query", "type": type})
|
|
92
|
+
|
|
93
|
+
tags: list[str] = []
|
|
94
|
+
if module := inspect.getmodule(function):
|
|
95
|
+
tags.append(module.__name__.split(".")[-1].capitalize())
|
|
96
|
+
|
|
97
|
+
cast(Any, function).specs_dict = {
|
|
98
|
+
"parameters": [*path_params, *query_params],
|
|
99
|
+
"responses": {
|
|
100
|
+
"200": {
|
|
101
|
+
"description": responses.get(200, "Request succeeded"),
|
|
102
|
+
"schema": (None),
|
|
103
|
+
},
|
|
104
|
+
**RESPONSES,
|
|
105
|
+
},
|
|
106
|
+
"summary": "test",
|
|
107
|
+
"tags": tags,
|
|
108
|
+
"operationId": func_path,
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
@wraps(function)
|
|
112
|
+
def wrapper(*args, **kwargs):
|
|
113
|
+
return function(*args, **kwargs)
|
|
114
|
+
|
|
115
|
+
return wrapper
|
|
116
|
+
|
|
117
|
+
return decorator
|
howler/config.py
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from flask_caching import Cache
|
|
4
|
+
|
|
5
|
+
from howler.common import loader
|
|
6
|
+
from howler.odm.models.config import config
|
|
7
|
+
from howler.remote.datatypes import get_client
|
|
8
|
+
from howler.remote.datatypes.user_quota_tracker import UserQuotaTracker
|
|
9
|
+
|
|
10
|
+
#################################################################
|
|
11
|
+
# Configuration
|
|
12
|
+
|
|
13
|
+
CLASSIFICATION = loader.get_classification()
|
|
14
|
+
|
|
15
|
+
AUDIT = config.ui.audit
|
|
16
|
+
|
|
17
|
+
SECRET_KEY = config.ui.secret_key
|
|
18
|
+
DEBUG = config.ui.debug
|
|
19
|
+
MAX_CLASSIFICATION = CLASSIFICATION.UNRESTRICTED
|
|
20
|
+
|
|
21
|
+
HWL_UNSECURED_UI = os.environ.get("HWL_UNSECURED_UI", "false").lower() == "true"
|
|
22
|
+
HWL_USE_REST_API = os.environ.get("HWL_USE_REST_API", "true").lower() == "true"
|
|
23
|
+
HWL_USE_WEBSOCKET_API = os.environ.get("HWL_USE_WEBSOCKET_API", "false").lower() == "true"
|
|
24
|
+
HWL_USE_JOB_SYSTEM = os.environ.get("HWL_USE_JOB_SYSTEM", "false").lower() == "true"
|
|
25
|
+
HWL_ENABLE_RULES = os.environ.get("HWL_ENABLE_RULES", "false").lower() == "true"
|
|
26
|
+
HWL_ENABLE_COVERAGE = os.environ.get("HWL_ENABLE_COVERAGE", "false").lower() == "true"
|
|
27
|
+
HWL_PLUGIN_DIRECTORY = os.environ.get("HWL_PLUGIN_DIRECTORY", "/etc/howler/plugins")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def get_version() -> str:
|
|
31
|
+
"""The version of the HOWLER API
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
str: The howler version
|
|
35
|
+
"""
|
|
36
|
+
return os.environ.get("HOWLER_VERSION", "this is not the version you are looking for")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def get_commit() -> str:
|
|
40
|
+
"""The commit of the currently deployed Howler API
|
|
41
|
+
|
|
42
|
+
Returns:
|
|
43
|
+
str: The commit of the currently deployed image
|
|
44
|
+
"""
|
|
45
|
+
return os.environ.get("COMMIT_HASH", "this is not the commit you are looking for")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def get_branch() -> str:
|
|
49
|
+
"""The branch of the current Howler Image
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
str: The current branch
|
|
53
|
+
"""
|
|
54
|
+
return os.environ.get("BRANCH", "this is not the branch you are looking for")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
redis_persistent = get_client(config.core.redis.persistent.host, config.core.redis.persistent.port, False)
|
|
58
|
+
redis = get_client(config.core.redis.nonpersistent.host, config.core.redis.nonpersistent.port, False)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
cache = Cache(config={"CACHE_TYPE": "SimpleCache"})
|
|
62
|
+
|
|
63
|
+
# TRACKERS
|
|
64
|
+
QUOTA_TRACKER = UserQuotaTracker("quota", timeout=60 * 2, redis=redis) # 2 Minutes timeout
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
import os
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from apscheduler.schedulers.background import BackgroundScheduler
|
|
6
|
+
from pytz import timezone
|
|
7
|
+
|
|
8
|
+
from howler.common.logging import get_logger
|
|
9
|
+
|
|
10
|
+
logger = get_logger(__file__)
|
|
11
|
+
|
|
12
|
+
scheduler = BackgroundScheduler(timezone=timezone(os.getenv("SCHEDULER_TZ", "America/Toronto")))
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def setup_jobs():
|
|
16
|
+
"Dynamically import and initialize all cronjobs in this folder"
|
|
17
|
+
module_path = Path(__file__).parent
|
|
18
|
+
modules_to_import = [_file for _file in os.listdir(module_path) if _file.endswith(".py") and _file != "__init__.py"]
|
|
19
|
+
|
|
20
|
+
for module in modules_to_import:
|
|
21
|
+
try:
|
|
22
|
+
job = importlib.import_module(f"howler.cronjobs.{module.replace('.py', '')}")
|
|
23
|
+
|
|
24
|
+
job.setup_job(scheduler)
|
|
25
|
+
except Exception as e:
|
|
26
|
+
logger.critical("Error when initializing %s - %s", module, e)
|
|
27
|
+
|
|
28
|
+
if scheduler.state != 1:
|
|
29
|
+
scheduler.start()
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from datetime import datetime, timedelta
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from apscheduler.schedulers.base import BaseScheduler
|
|
6
|
+
from apscheduler.triggers.cron import CronTrigger
|
|
7
|
+
from pytz import timezone
|
|
8
|
+
|
|
9
|
+
from howler.common.logging import get_logger
|
|
10
|
+
from howler.config import DEBUG, config
|
|
11
|
+
|
|
12
|
+
logger = get_logger(__file__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def execute():
|
|
16
|
+
"""Delete any hits older than the configured time"""
|
|
17
|
+
from howler.common.loader import datastore
|
|
18
|
+
|
|
19
|
+
delta_kwargs = {str(config.system.retention.limit_unit): config.system.retention.limit_amount}
|
|
20
|
+
|
|
21
|
+
cutoff = (datetime.now() - timedelta(**delta_kwargs)).strftime("%Y-%m-%d")
|
|
22
|
+
|
|
23
|
+
logger.debug("Removing hits older than %s", cutoff)
|
|
24
|
+
|
|
25
|
+
ds = datastore()
|
|
26
|
+
|
|
27
|
+
ds.hit.delete_by_query(f"event.created:{{* TO {cutoff}}} OR howler.expiry:{{* TO now}}")
|
|
28
|
+
|
|
29
|
+
ds.hit.commit()
|
|
30
|
+
|
|
31
|
+
logger.debug("Deletion complete")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def setup_job(sched: BaseScheduler):
|
|
35
|
+
"""Initialize the retention job"""
|
|
36
|
+
if not config.system.retention.enabled:
|
|
37
|
+
if not DEBUG or config.system.type == "production":
|
|
38
|
+
logger.warning("Retention cronjob disabled! This is not recommended for a production settings.")
|
|
39
|
+
|
|
40
|
+
return
|
|
41
|
+
|
|
42
|
+
logger.debug(f"Initializing retention cronjob with cron {config.system.retention.crontab}")
|
|
43
|
+
|
|
44
|
+
if DEBUG:
|
|
45
|
+
_kwargs: dict[str, Any] = {"next_run_time": datetime.now()}
|
|
46
|
+
else:
|
|
47
|
+
_kwargs = {}
|
|
48
|
+
|
|
49
|
+
if sched.get_job("retention"):
|
|
50
|
+
logger.debug("Retention job already running!")
|
|
51
|
+
return
|
|
52
|
+
|
|
53
|
+
sched.add_job(
|
|
54
|
+
id="retention",
|
|
55
|
+
func=execute,
|
|
56
|
+
trigger=CronTrigger.from_crontab(
|
|
57
|
+
config.system.retention.crontab, timezone=timezone(os.getenv("SCHEDULER_TZ", "America/Toronto"))
|
|
58
|
+
),
|
|
59
|
+
**_kwargs,
|
|
60
|
+
)
|
|
61
|
+
logger.debug("Initialization complete")
|