howler-api 2.12.0.dev307__py3-none-any.whl → 2.12.0.dev316__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.
- howler/api/v1/view.py +22 -20
- howler/odm/models/config.py +145 -1
- howler/odm/random_data.py +2 -2
- {howler_api-2.12.0.dev307.dist-info → howler_api-2.12.0.dev316.dist-info}/METADATA +1 -1
- {howler_api-2.12.0.dev307.dist-info → howler_api-2.12.0.dev316.dist-info}/RECORD +7 -7
- {howler_api-2.12.0.dev307.dist-info → howler_api-2.12.0.dev316.dist-info}/WHEEL +0 -0
- {howler_api-2.12.0.dev307.dist-info → howler_api-2.12.0.dev316.dist-info}/entry_points.txt +0 -0
howler/api/v1/view.py
CHANGED
|
@@ -108,13 +108,13 @@ def create_view(**kwargs):
|
|
|
108
108
|
|
|
109
109
|
|
|
110
110
|
@generate_swagger_docs()
|
|
111
|
-
@view_api.route("/<
|
|
111
|
+
@view_api.route("/<view_id>", methods=["DELETE"])
|
|
112
112
|
@api_login(required_priv=["W"])
|
|
113
|
-
def delete_view(
|
|
113
|
+
def delete_view(view_id: str, user: User, **kwargs):
|
|
114
114
|
"""Delete a view
|
|
115
115
|
|
|
116
116
|
Variables:
|
|
117
|
-
|
|
117
|
+
view_id => The id of the view to delete
|
|
118
118
|
|
|
119
119
|
Optional Arguments:
|
|
120
120
|
None
|
|
@@ -129,7 +129,7 @@ def delete_view(id: str, user: User, **kwargs):
|
|
|
129
129
|
"""
|
|
130
130
|
storage = datastore()
|
|
131
131
|
|
|
132
|
-
existing_view: View = storage.view.get_if_exists(
|
|
132
|
+
existing_view: View = storage.view.get_if_exists(view_id)
|
|
133
133
|
if not existing_view:
|
|
134
134
|
return not_found(err="This view does not exist")
|
|
135
135
|
|
|
@@ -139,7 +139,7 @@ def delete_view(id: str, user: User, **kwargs):
|
|
|
139
139
|
if existing_view.type == "readonly":
|
|
140
140
|
return forbidden(err="You cannot delete built-in views.")
|
|
141
141
|
|
|
142
|
-
success = storage.view.delete(
|
|
142
|
+
success = storage.view.delete(view_id)
|
|
143
143
|
|
|
144
144
|
storage.view.commit()
|
|
145
145
|
|
|
@@ -147,13 +147,13 @@ def delete_view(id: str, user: User, **kwargs):
|
|
|
147
147
|
|
|
148
148
|
|
|
149
149
|
@generate_swagger_docs()
|
|
150
|
-
@view_api.route("/<
|
|
150
|
+
@view_api.route("/<view_id>", methods=["PUT"])
|
|
151
151
|
@api_login(required_priv=["R", "W"])
|
|
152
|
-
def update_view(
|
|
152
|
+
def update_view(view_id: str, user: User, **kwargs):
|
|
153
153
|
"""Update a view
|
|
154
154
|
|
|
155
155
|
Variables:
|
|
156
|
-
|
|
156
|
+
view_id => The view_id of the view to modify
|
|
157
157
|
|
|
158
158
|
Optional Arguments:
|
|
159
159
|
None
|
|
@@ -178,7 +178,7 @@ def update_view(id: str, user: User, **kwargs):
|
|
|
178
178
|
if set(new_data.keys()) & {"view_id", "owner"}:
|
|
179
179
|
return bad_request(err="You cannot change the owner or id of a view.")
|
|
180
180
|
|
|
181
|
-
existing_view: View = storage.view.get_if_exists(
|
|
181
|
+
existing_view: View = storage.view.get_if_exists(view_id)
|
|
182
182
|
if not existing_view:
|
|
183
183
|
return not_found(err="This view does not exist")
|
|
184
184
|
|
|
@@ -210,13 +210,13 @@ def update_view(id: str, user: User, **kwargs):
|
|
|
210
210
|
|
|
211
211
|
|
|
212
212
|
@generate_swagger_docs()
|
|
213
|
-
@view_api.route("/<
|
|
213
|
+
@view_api.route("/<view_id>/favourite", methods=["POST"])
|
|
214
214
|
@api_login(required_priv=["R", "W"])
|
|
215
|
-
def set_as_favourite(
|
|
215
|
+
def set_as_favourite(view_id: str, **kwargs):
|
|
216
216
|
"""Add a view to a list of the user's favourites
|
|
217
217
|
|
|
218
218
|
Variables:
|
|
219
|
-
|
|
219
|
+
view_id => The id of the view to add as a favourite
|
|
220
220
|
|
|
221
221
|
Optional Arguments:
|
|
222
222
|
None
|
|
@@ -231,7 +231,7 @@ def set_as_favourite(id: str, **kwargs):
|
|
|
231
231
|
"""
|
|
232
232
|
storage = datastore()
|
|
233
233
|
|
|
234
|
-
existing_view: View = storage.view.get_if_exists(
|
|
234
|
+
existing_view: View = storage.view.get_if_exists(view_id)
|
|
235
235
|
if not existing_view:
|
|
236
236
|
return not_found(err="This view does not exist")
|
|
237
237
|
|
|
@@ -243,7 +243,7 @@ def set_as_favourite(id: str, **kwargs):
|
|
|
243
243
|
try:
|
|
244
244
|
current_user = storage.user.get_if_exists(kwargs["user"]["uname"])
|
|
245
245
|
|
|
246
|
-
current_user["favourite_views"] = list(set(current_user.get("favourite_views", []) + [
|
|
246
|
+
current_user["favourite_views"] = list(set(current_user.get("favourite_views", []) + [view_id]))
|
|
247
247
|
|
|
248
248
|
storage.user.save(current_user["uname"], current_user)
|
|
249
249
|
|
|
@@ -253,9 +253,9 @@ def set_as_favourite(id: str, **kwargs):
|
|
|
253
253
|
|
|
254
254
|
|
|
255
255
|
@generate_swagger_docs()
|
|
256
|
-
@view_api.route("/<
|
|
256
|
+
@view_api.route("/<view_id>/favourite", methods=["DELETE"])
|
|
257
257
|
@api_login(required_priv=["R", "W"])
|
|
258
|
-
def remove_as_favourite(
|
|
258
|
+
def remove_as_favourite(view_id: str, **kwargs):
|
|
259
259
|
"""Remove a view from a list of the user's favourites
|
|
260
260
|
|
|
261
261
|
Variables:
|
|
@@ -271,13 +271,15 @@ def remove_as_favourite(id, **kwargs):
|
|
|
271
271
|
"""
|
|
272
272
|
storage = datastore()
|
|
273
273
|
|
|
274
|
-
if not storage.view.exists(id):
|
|
275
|
-
return not_found(err="This view does not exist")
|
|
276
|
-
|
|
277
274
|
try:
|
|
278
275
|
current_user = storage.user.get_if_exists(kwargs["user"]["uname"])
|
|
279
276
|
|
|
280
|
-
|
|
277
|
+
current_favourites: list[str] = current_user.get("favourite_views", [])
|
|
278
|
+
|
|
279
|
+
if view_id not in current_favourites:
|
|
280
|
+
return not_found(err="View is not favourited.")
|
|
281
|
+
|
|
282
|
+
current_user["favourite_views"] = [favourite for favourite in current_favourites if favourite != view_id]
|
|
281
283
|
|
|
282
284
|
storage.user.save(current_user["uname"], current_user)
|
|
283
285
|
|
howler/odm/models/config.py
CHANGED
|
@@ -20,11 +20,24 @@ logger.addHandler(console)
|
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
class RedisServer(BaseModel):
|
|
23
|
+
"""Configuration for a single Redis server instance.
|
|
24
|
+
|
|
25
|
+
Defines the connection parameters for a Redis server, including
|
|
26
|
+
the hostname and port number.
|
|
27
|
+
"""
|
|
28
|
+
|
|
23
29
|
host: str = Field(description="Hostname of Redis instance")
|
|
24
30
|
port: int = Field(description="Port of Redis instance")
|
|
25
31
|
|
|
26
32
|
|
|
27
33
|
class Redis(BaseModel):
|
|
34
|
+
"""Redis configuration for Howler.
|
|
35
|
+
|
|
36
|
+
Defines connections to both persistent and non-persistent Redis instances.
|
|
37
|
+
The non-persistent instance is used for volatile data like caches, while
|
|
38
|
+
the persistent instance is used for data that needs to survive restarts.
|
|
39
|
+
"""
|
|
40
|
+
|
|
28
41
|
nonpersistent: RedisServer = Field(
|
|
29
42
|
default=RedisServer(host="127.0.0.1", port=6379), description="A volatile Redis instance"
|
|
30
43
|
)
|
|
@@ -35,6 +48,14 @@ class Redis(BaseModel):
|
|
|
35
48
|
|
|
36
49
|
|
|
37
50
|
class Host(BaseModel):
|
|
51
|
+
"""Configuration for a remote host connection.
|
|
52
|
+
|
|
53
|
+
Defines connection parameters for external services, including authentication
|
|
54
|
+
credentials (username/password or API key) and connection details.
|
|
55
|
+
Environment variables can override username and password using the pattern
|
|
56
|
+
{NAME}_HOST_USERNAME and {NAME}_HOST_PASSWORD.
|
|
57
|
+
"""
|
|
58
|
+
|
|
38
59
|
name: str = Field(description="Name of the host")
|
|
39
60
|
username: Optional[str] = Field(description="Username to login with", default=None)
|
|
40
61
|
password: Optional[str] = Field(description="Password to login with", default=None)
|
|
@@ -64,6 +85,12 @@ class Host(BaseModel):
|
|
|
64
85
|
|
|
65
86
|
|
|
66
87
|
class Datastore(BaseModel):
|
|
88
|
+
"""Datastore configuration for Howler.
|
|
89
|
+
|
|
90
|
+
Defines the backend datastore used by Howler for storing hits and metadata.
|
|
91
|
+
Currently supports Elasticsearch as the datastore type.
|
|
92
|
+
"""
|
|
93
|
+
|
|
67
94
|
hosts: list[Host] = Field(
|
|
68
95
|
default=[Host(name="elastic", username="elastic", password="devpass", scheme="http", host="localhost:9200")], # noqa: S106
|
|
69
96
|
description="List of hosts used for the datastore",
|
|
@@ -74,6 +101,13 @@ class Datastore(BaseModel):
|
|
|
74
101
|
|
|
75
102
|
|
|
76
103
|
class Logging(BaseModel):
|
|
104
|
+
"""Logging configuration for Howler.
|
|
105
|
+
|
|
106
|
+
Defines how and where Howler logs should be output, including console,
|
|
107
|
+
file, and syslog destinations. Also controls log level, format, and
|
|
108
|
+
metric export intervals.
|
|
109
|
+
"""
|
|
110
|
+
|
|
77
111
|
log_level: Literal["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL", "DISABLED"] = Field(
|
|
78
112
|
default="INFO",
|
|
79
113
|
description="What level of logging should we have?",
|
|
@@ -94,6 +128,12 @@ class Logging(BaseModel):
|
|
|
94
128
|
|
|
95
129
|
|
|
96
130
|
class PasswordRequirement(BaseModel):
|
|
131
|
+
"""Password complexity requirements for internal authentication.
|
|
132
|
+
|
|
133
|
+
Defines the rules for password creation and validation, including
|
|
134
|
+
character type requirements and minimum length.
|
|
135
|
+
"""
|
|
136
|
+
|
|
97
137
|
lower: bool = Field(default=False, description="Password must contain lowercase letters")
|
|
98
138
|
number: bool = Field(default=False, description="Password must contain numbers")
|
|
99
139
|
special: bool = Field(default=False, description="Password must contain special characters")
|
|
@@ -102,6 +142,13 @@ class PasswordRequirement(BaseModel):
|
|
|
102
142
|
|
|
103
143
|
|
|
104
144
|
class Internal(BaseModel):
|
|
145
|
+
"""Internal authentication configuration.
|
|
146
|
+
|
|
147
|
+
Defines settings for Howler's built-in username/password authentication,
|
|
148
|
+
including password requirements and brute-force protection via login
|
|
149
|
+
failure tracking.
|
|
150
|
+
"""
|
|
151
|
+
|
|
105
152
|
enabled: bool = Field(default=True, description="Internal authentication allowed?")
|
|
106
153
|
failure_ttl: int = Field(
|
|
107
154
|
default=60, description="How long to wait after `max_failures` before re-attempting login?"
|
|
@@ -111,6 +158,13 @@ class Internal(BaseModel):
|
|
|
111
158
|
|
|
112
159
|
|
|
113
160
|
class OAuthAutoProperty(BaseModel):
|
|
161
|
+
"""Automatic property assignment based on OAuth attributes.
|
|
162
|
+
|
|
163
|
+
Defines rules for automatically assigning user properties (roles,
|
|
164
|
+
classifications, or access levels) based on pattern matching against
|
|
165
|
+
OAuth provider data.
|
|
166
|
+
"""
|
|
167
|
+
|
|
114
168
|
field: str = Field(description="Field to apply `pattern` to")
|
|
115
169
|
pattern: str = Field(description="Regex pattern for auto-prop assignment")
|
|
116
170
|
type: Literal["access", "classification", "role"] = Field(
|
|
@@ -120,6 +174,13 @@ class OAuthAutoProperty(BaseModel):
|
|
|
120
174
|
|
|
121
175
|
|
|
122
176
|
class OAuthProvider(BaseModel):
|
|
177
|
+
"""OAuth provider configuration.
|
|
178
|
+
|
|
179
|
+
Defines the connection and authentication settings for an OAuth 2.0 provider.
|
|
180
|
+
Includes user auto-creation, group mapping, JWT validation, and various
|
|
181
|
+
OAuth endpoints required for the authentication flow.
|
|
182
|
+
"""
|
|
183
|
+
|
|
123
184
|
auto_create: bool = Field(default=True, description="Auto-create users if they are missing")
|
|
124
185
|
auto_sync: bool = Field(default=False, description="Should we automatically sync with OAuth provider?")
|
|
125
186
|
auto_properties: list[OAuthAutoProperty] = Field(
|
|
@@ -191,6 +252,13 @@ class OAuthProvider(BaseModel):
|
|
|
191
252
|
|
|
192
253
|
|
|
193
254
|
class OAuth(BaseModel):
|
|
255
|
+
"""OAuth authentication configuration.
|
|
256
|
+
|
|
257
|
+
Top-level OAuth settings including enabling/disabling OAuth authentication,
|
|
258
|
+
Gravatar integration, and a dictionary of configured OAuth providers.
|
|
259
|
+
Also controls API key lifetime restrictions for OAuth-authenticated users.
|
|
260
|
+
"""
|
|
261
|
+
|
|
194
262
|
enabled: bool = Field(default=False, description="Enable use of OAuth?")
|
|
195
263
|
gravatar_enabled: bool = Field(default=True, description="Enable gravatar?")
|
|
196
264
|
providers: dict[str, OAuthProvider] = Field(
|
|
@@ -204,6 +272,13 @@ class OAuth(BaseModel):
|
|
|
204
272
|
|
|
205
273
|
|
|
206
274
|
class Auth(BaseModel):
|
|
275
|
+
"""Authentication configuration for Howler.
|
|
276
|
+
|
|
277
|
+
Configures all authentication methods supported by Howler, including
|
|
278
|
+
internal username/password authentication and OAuth providers. Also
|
|
279
|
+
controls API key settings and restrictions.
|
|
280
|
+
"""
|
|
281
|
+
|
|
207
282
|
allow_apikeys: bool = Field(default=True, description="Allow API keys?")
|
|
208
283
|
allow_extended_apikeys: bool = Field(default=True, description="Allow extended API keys?")
|
|
209
284
|
max_apikey_duration_amount: Optional[int] = Field(
|
|
@@ -218,17 +293,34 @@ class Auth(BaseModel):
|
|
|
218
293
|
|
|
219
294
|
|
|
220
295
|
class APMServer(BaseModel):
|
|
221
|
-
"APM server configuration
|
|
296
|
+
"""Application Performance Monitoring (APM) server configuration.
|
|
297
|
+
|
|
298
|
+
Defines the connection details for an external APM server used to
|
|
299
|
+
collect and analyze application performance metrics.
|
|
300
|
+
"""
|
|
222
301
|
|
|
223
302
|
server_url: Optional[str] = Field(default=None, description="URL to API server")
|
|
224
303
|
token: Optional[str] = Field(default=None, description="Authentication token for server")
|
|
225
304
|
|
|
226
305
|
|
|
227
306
|
class Metrics(BaseModel):
|
|
307
|
+
"""Metrics collection configuration.
|
|
308
|
+
|
|
309
|
+
Configures how Howler collects and exports application metrics,
|
|
310
|
+
including integration with external APM servers.
|
|
311
|
+
"""
|
|
312
|
+
|
|
228
313
|
apm_server: APMServer = APMServer()
|
|
229
314
|
|
|
230
315
|
|
|
231
316
|
class Retention(BaseModel):
|
|
317
|
+
"""Hit retention policy configuration.
|
|
318
|
+
|
|
319
|
+
Defines the automatic data retention policy for hits, including
|
|
320
|
+
the maximum age of hits before they are purged and the schedule
|
|
321
|
+
for running the retention cleanup job.
|
|
322
|
+
"""
|
|
323
|
+
|
|
232
324
|
enabled: bool = Field(
|
|
233
325
|
default=True,
|
|
234
326
|
description=(
|
|
@@ -251,6 +343,12 @@ class Retention(BaseModel):
|
|
|
251
343
|
|
|
252
344
|
|
|
253
345
|
class ViewCleanup(BaseModel):
|
|
346
|
+
"""View cleanup job configuration.
|
|
347
|
+
|
|
348
|
+
Defines the schedule and behavior for cleaning up stale dashboard views
|
|
349
|
+
that reference non-existent backend data.
|
|
350
|
+
"""
|
|
351
|
+
|
|
254
352
|
enabled: bool = Field(
|
|
255
353
|
default=True,
|
|
256
354
|
description=(
|
|
@@ -265,6 +363,13 @@ class ViewCleanup(BaseModel):
|
|
|
265
363
|
|
|
266
364
|
|
|
267
365
|
class System(BaseModel):
|
|
366
|
+
"""System-level configuration for Howler.
|
|
367
|
+
|
|
368
|
+
Defines global system settings including deployment type (production,
|
|
369
|
+
staging, or development) and configuration for automated maintenance
|
|
370
|
+
jobs like data retention and view cleanup.
|
|
371
|
+
"""
|
|
372
|
+
|
|
268
373
|
type: Literal["production", "staging", "development"] = Field(default="development", description="Type of system")
|
|
269
374
|
retention: Retention = Retention()
|
|
270
375
|
"Retention Configuration"
|
|
@@ -273,6 +378,13 @@ class System(BaseModel):
|
|
|
273
378
|
|
|
274
379
|
|
|
275
380
|
class UI(BaseModel):
|
|
381
|
+
"""User interface and web server configuration.
|
|
382
|
+
|
|
383
|
+
Defines settings for the Howler web UI including Flask configuration,
|
|
384
|
+
session validation, API auditing, static file locations, and WebSocket
|
|
385
|
+
integration for real-time updates.
|
|
386
|
+
"""
|
|
387
|
+
|
|
276
388
|
audit: bool = Field(description="Should API calls be audited and saved to a separate log file?", default=True)
|
|
277
389
|
debug: bool = Field(default=False, description="Enable debugging?")
|
|
278
390
|
static_folder: Optional[str] = Field(
|
|
@@ -298,6 +410,13 @@ class UI(BaseModel):
|
|
|
298
410
|
|
|
299
411
|
|
|
300
412
|
class Borealis(BaseModel):
|
|
413
|
+
"""Borealis enrichment service integration configuration.
|
|
414
|
+
|
|
415
|
+
Defines settings for integrating with Borealis, an external enrichment
|
|
416
|
+
service that can provide additional context and status information for
|
|
417
|
+
hits displayed in the Howler UI.
|
|
418
|
+
"""
|
|
419
|
+
|
|
301
420
|
enabled: bool = Field(default=False, description="Should borealis integration be enabled?")
|
|
302
421
|
|
|
303
422
|
url: str = Field(
|
|
@@ -312,6 +431,13 @@ class Borealis(BaseModel):
|
|
|
312
431
|
|
|
313
432
|
|
|
314
433
|
class Notebook(BaseModel):
|
|
434
|
+
"""Jupyter notebook integration configuration.
|
|
435
|
+
|
|
436
|
+
Defines settings for integrating with nbgallery, a collaborative
|
|
437
|
+
Jupyter notebook platform, allowing users to access and share
|
|
438
|
+
notebooks related to their Howler analysis work.
|
|
439
|
+
"""
|
|
440
|
+
|
|
315
441
|
enabled: bool = Field(default=False, description="Should nbgallery notebook integration be enabled?")
|
|
316
442
|
|
|
317
443
|
scope: Optional[str] = Field(default=None, description="The scope expected by nbgallery for JWTs")
|
|
@@ -322,6 +448,13 @@ class Notebook(BaseModel):
|
|
|
322
448
|
|
|
323
449
|
|
|
324
450
|
class Core(BaseModel):
|
|
451
|
+
"""Core application configuration for Howler.
|
|
452
|
+
|
|
453
|
+
Aggregates all core service configurations including Redis, metrics,
|
|
454
|
+
and external integrations like Borealis and nbgallery notebooks.
|
|
455
|
+
Also manages the loading of external plugins.
|
|
456
|
+
"""
|
|
457
|
+
|
|
325
458
|
plugins: set[str] = Field(description="A list of external plugins to load", default=set())
|
|
326
459
|
|
|
327
460
|
metrics: Metrics = Metrics()
|
|
@@ -379,6 +512,17 @@ logger.info("Fetching configuration files from %s", ":".join(str(c) for c in con
|
|
|
379
512
|
|
|
380
513
|
|
|
381
514
|
class Config(BaseSettings):
|
|
515
|
+
"""Main Howler configuration model.
|
|
516
|
+
|
|
517
|
+
The root configuration object that aggregates all configuration sections
|
|
518
|
+
including authentication, datastore, logging, system settings, UI, and core
|
|
519
|
+
services. Configuration can be loaded from YAML files or environment variables
|
|
520
|
+
with the HWL_ prefix.
|
|
521
|
+
|
|
522
|
+
Environment variables use double underscores (__) for nested properties.
|
|
523
|
+
For example: HWL_DATASTORE__TYPE=elasticsearch
|
|
524
|
+
"""
|
|
525
|
+
|
|
382
526
|
auth: Auth = Auth()
|
|
383
527
|
core: Core = Core()
|
|
384
528
|
datastore: Datastore = Datastore()
|
howler/odm/random_data.py
CHANGED
|
@@ -821,14 +821,14 @@ def wipe_dossiers(ds: HowlerDatastore):
|
|
|
821
821
|
def setup_hits(ds):
|
|
822
822
|
"Set up hits index"
|
|
823
823
|
os.environ["ELASTIC_HIT_SHARDS"] = "1"
|
|
824
|
-
os.environ["ELASTIC_HIT_REPLICAS"] = "
|
|
824
|
+
os.environ["ELASTIC_HIT_REPLICAS"] = "1"
|
|
825
825
|
ds.hit.fix_shards()
|
|
826
826
|
ds.hit.fix_replicas()
|
|
827
827
|
|
|
828
828
|
|
|
829
829
|
def setup_users(ds):
|
|
830
830
|
"Set up users index"
|
|
831
|
-
os.environ["ELASTIC_USER_REPLICAS"] = "
|
|
831
|
+
os.environ["ELASTIC_USER_REPLICAS"] = "1"
|
|
832
832
|
os.environ["ELASTIC_USER_AVATAR_REPLICAS"] = "1"
|
|
833
833
|
ds.user.fix_replicas()
|
|
834
834
|
ds.user_avatar.fix_replicas()
|
|
@@ -30,7 +30,7 @@ howler/api/v1/tool.py,sha256=6CN5LIXgxaAvxEHE6s4IFtAEhQo_zBLtl6ghbM8whjE,6737
|
|
|
30
30
|
howler/api/v1/user.py,sha256=YQh6eCAi4bsC_w9eGX0ZgnLoFL4z4efEc1knJsZhj4k,13842
|
|
31
31
|
howler/api/v1/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
32
32
|
howler/api/v1/utils/etag.py,sha256=IGB4WUkecHbBt0KKLQFCxCn0mN3T_aSPG0y45l_zT9I,3431
|
|
33
|
-
howler/api/v1/view.py,sha256=
|
|
33
|
+
howler/api/v1/view.py,sha256=VyizfGehsuoPyn7iswGPKCLcbSQoO1ipRsbr91nSDKk,8228
|
|
34
34
|
howler/app.py,sha256=AJJ8TGMSi9hJGc7IR7xKsZuVwg-ih85-t0PRO0-ntHw,7063
|
|
35
35
|
howler/common/README.md,sha256=lgnrAdgnOADmmfRplhbfYD7jU627nr3zO-fJ6N4Nbcs,6577
|
|
36
36
|
howler/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -97,7 +97,7 @@ howler/odm/models/assemblyline.py,sha256=_wcTiX3A6bhA2SGlK9tDF0v-uwLpIabXE8j2Fw9
|
|
|
97
97
|
howler/odm/models/aws.py,sha256=pJVadJqubdgT27riCfp7bEKVP4XsMZB0ZUnKAbmCMd0,895
|
|
98
98
|
howler/odm/models/azure.py,sha256=o7MZMMo9jh1SB8xXCajl_YSKP2nnnWsjx_DPT6LnQKg,710
|
|
99
99
|
howler/odm/models/cbs.py,sha256=onUiJOGUxK3iy_-4XkGGwHxFiFq9Td_p59Kum4XaR-w,1366
|
|
100
|
-
howler/odm/models/config.py,sha256=
|
|
100
|
+
howler/odm/models/config.py,sha256=Hp37cfobHG824AUePE19_mCKFw_sc0ZSYtjDP3bxvGI,21770
|
|
101
101
|
howler/odm/models/dossier.py,sha256=Ob2qROrG2-DYzmVo2XVe4NJ8HjWGCoRAu2gPo6p9XGU,1244
|
|
102
102
|
howler/odm/models/ecs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
103
103
|
howler/odm/models/ecs/agent.py,sha256=idSooyFCLuQAB7_RyEWTYW4-x9w5a3wpy2ct_-EDRQs,713
|
|
@@ -147,7 +147,7 @@ howler/odm/models/pivot.py,sha256=VLZl5DHnLfmCM2ZU5sAye0qu18toCaFcYLYraXct2GM,13
|
|
|
147
147
|
howler/odm/models/template.py,sha256=-Tqq_36qD_3nQ4jv13OPeH_EyyERKhc55wT03CU-Kbk,979
|
|
148
148
|
howler/odm/models/user.py,sha256=3V7cLxxHJwWfTsEdZ7-QZT_-PQL7H_RJ3buQ8AraGzQ,3052
|
|
149
149
|
howler/odm/models/view.py,sha256=kmaJOXhR4prki5o0gBirs1dqGcQK3b9ATysL_kNoku0,1308
|
|
150
|
-
howler/odm/random_data.py,sha256=
|
|
150
|
+
howler/odm/random_data.py,sha256=I_Yfp75XM2pzHKMxXXNdiU1Mb7jNXeVF8aLc_zz1HKY,28493
|
|
151
151
|
howler/odm/randomizer.py,sha256=-AG-C-FZEsZQp1bQGWLNIpx6NN6r1DcWtk6nq2Ktfwg,23595
|
|
152
152
|
howler/patched.py,sha256=Br4BGU5raaqjSMDLD7ogb5A8Yn0dzecouh6uWVV2jlQ,77
|
|
153
153
|
howler/plugins/__init__.py,sha256=P5P-t4KgIInOzp4NmIturNIhUbb3jPO81n55Q_b_gM0,841
|
|
@@ -194,7 +194,7 @@ howler/utils/path.py,sha256=DfOU4i4zSs4wchHoE8iE7aWVLkTxiC_JRGepF2hBYBk,690
|
|
|
194
194
|
howler/utils/socket_utils.py,sha256=nz1SklC9xBHUSfHyTJjpq3mbozX1GDf01WzdGxfaUII,2212
|
|
195
195
|
howler/utils/str_utils.py,sha256=HE8Hqh2HlOLaj16w0H9zKOyDJLp-f1LQ50y_WeGZaEk,8389
|
|
196
196
|
howler/utils/uid.py,sha256=p9dsqyvZ-lpiAuzZWCPCeEM99kdk0Ly9czf04HNdSuw,1341
|
|
197
|
-
howler_api-2.12.0.
|
|
198
|
-
howler_api-2.12.0.
|
|
199
|
-
howler_api-2.12.0.
|
|
200
|
-
howler_api-2.12.0.
|
|
197
|
+
howler_api-2.12.0.dev316.dist-info/METADATA,sha256=QaWRVrzS2LNqHob-DDq7hIv3pxAbMBsbEuA77yba4lo,2815
|
|
198
|
+
howler_api-2.12.0.dev316.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
199
|
+
howler_api-2.12.0.dev316.dist-info/entry_points.txt,sha256=Lu9SBGvwe0wczJHmc-RudC24lmQk7tv3ZBXon9RIihg,259
|
|
200
|
+
howler_api-2.12.0.dev316.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|