howler-api 2.12.0.dev285__py3-none-any.whl → 2.12.0.dev294__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.
@@ -0,0 +1,88 @@
1
+ import os
2
+ from datetime import datetime
3
+ from typing import Any, List
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 pinned views that no longer exist"""
17
+ from howler.common.loader import datastore
18
+
19
+ # Initialize datastore
20
+ ds = datastore()
21
+ # fetch the first result from user ds (needed to initialize total)
22
+ result = ds.user.search("*:*", rows=250, fl="*")
23
+ total_user_count = result["total"]
24
+ user_list: List[Any] = result["items"]
25
+ # Do the same thing for the views
26
+ result = ds.view.search("*:*", rows=250)
27
+ total_view_count = result["total"]
28
+ view_list: List[Any] = result["items"]
29
+ view_ids: List[str] = []
30
+
31
+ # Collect all views
32
+ while len(view_list) < total_view_count:
33
+ view_list.extend(ds.view.search("*:*", rows=250, offset=len(user_list)))
34
+
35
+ # Collect all users
36
+ while len(user_list) < total_user_count:
37
+ user_list.extend(ds.user.search("*:*", rows=250, offset=len(user_list)))
38
+
39
+ for view in view_list:
40
+ view_ids.append(view["view_id"])
41
+
42
+ # Iterate over each user to see if the dashboard contains invalid entries (deleted views)
43
+ for user in user_list:
44
+ valid_entries = []
45
+ # No views/analytics saved to the dashboard? Skip it
46
+ if user["dashboard"] == []:
47
+ continue
48
+ for dashboard_entry in user["dashboard"]:
49
+ if dashboard_entry["type"] != "view" or (
50
+ dashboard_entry["type"] == "view" and dashboard_entry["entry_id"] in view_ids
51
+ ):
52
+ valid_entries.append(dashboard_entry)
53
+ # If the length of valid entries is less than the current dashboard, one or more pins are invalid
54
+ if len(valid_entries) < len(user["dashboard"]):
55
+ # set the user dashboard to valid entries
56
+ user["dashboard"] = valid_entries
57
+ # update the user
58
+ ds.user.save(user["uname"], user)
59
+
60
+
61
+ def setup_job(sched: BaseScheduler):
62
+ """Initialize the view cleanup job"""
63
+ if not config.system.view_cleanup.enabled:
64
+ if not DEBUG or config.system.type == "production":
65
+ logger.warning("view cleanup cronjob disabled! This is not recommended for a production settings.")
66
+
67
+ return
68
+
69
+ logger.debug(f"Initializing view cleanup cronjob with cron {config.system.view_cleanup.crontab}")
70
+
71
+ if DEBUG:
72
+ _kwargs: dict[str, Any] = {"next_run_time": datetime.now()}
73
+ else:
74
+ _kwargs = {}
75
+
76
+ if sched.get_job("view_cleanup"):
77
+ logger.debug("view cleanup job already running!")
78
+ return
79
+
80
+ sched.add_job(
81
+ id="view_cleanup",
82
+ func=execute,
83
+ trigger=CronTrigger.from_crontab(
84
+ config.system.view_cleanup.crontab, timezone=timezone(os.getenv("SCHEDULER_TZ", "America/Toronto"))
85
+ ),
86
+ **_kwargs,
87
+ )
88
+ logger.debug("Initialization complete")
@@ -250,10 +250,26 @@ class Retention(BaseModel):
250
250
  )
251
251
 
252
252
 
253
+ class ViewCleanup(BaseModel):
254
+ enabled: bool = Field(
255
+ default=True,
256
+ description=(
257
+ "Whether to enable the view cleanup. If enabled, views pinned "
258
+ "to the dashboard that no longer exist in the backend will be cleared."
259
+ ),
260
+ )
261
+ crontab: str = Field(
262
+ default="0 0 * * *",
263
+ description="The crontab that denotes how often to run the view_cleanup job",
264
+ )
265
+
266
+
253
267
  class System(BaseModel):
254
268
  type: Literal["production", "staging", "development"] = Field(default="development", description="Type of system")
255
269
  retention: Retention = Retention()
256
270
  "Retention Configuration"
271
+ view_cleanup: ViewCleanup = ViewCleanup()
272
+ "View Cleanup Configuration"
257
273
 
258
274
 
259
275
  class UI(BaseModel):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: howler-api
3
- Version: 2.12.0.dev285
3
+ Version: 2.12.0.dev294
4
4
  Summary: Howler - API server
5
5
  License: MIT
6
6
  Keywords: howler,alerting,gc,canada,cse-cst,cse,cst,cyber,cccs
@@ -51,6 +51,7 @@ howler/config.py,sha256=amwf4FXJ1ZKeooHqPlC_F691tg6Yaj4osKn_ExiUSS0,2128
51
51
  howler/cronjobs/__init__.py,sha256=GEhNsxPGATumlroMa-g6z5Dt9yy0QT93s3uuNC-GwIM,909
52
52
  howler/cronjobs/retention.py,sha256=I-vXXFo2W5eHkJ-xQuywU2-KfM9xqgii9Y99Qd9E-Vk,1777
53
53
  howler/cronjobs/rules.py,sha256=ZK3FmLsEF34-3vQzXkc4J6Y0P5-JSawMSnAkH9o-PeE,10406
54
+ howler/cronjobs/view_cleanup.py,sha256=ULWLR1uFcRemRgkEDrMqmBamHiE0dgDvu49VK0qDkuw,3076
54
55
  howler/datastore/README.md,sha256=ekWl1YJSrHlZpU5PgBkPEzPWjdbTdav6Rd2P0ccIesw,4758
55
56
  howler/datastore/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
56
57
  howler/datastore/bulk.py,sha256=VfolZfiaBD4ZTK3j6IVVVq4GMjVXb5elrsGwwM_nONE,2829
@@ -96,7 +97,7 @@ howler/odm/models/assemblyline.py,sha256=_wcTiX3A6bhA2SGlK9tDF0v-uwLpIabXE8j2Fw9
96
97
  howler/odm/models/aws.py,sha256=pJVadJqubdgT27riCfp7bEKVP4XsMZB0ZUnKAbmCMd0,895
97
98
  howler/odm/models/azure.py,sha256=o7MZMMo9jh1SB8xXCajl_YSKP2nnnWsjx_DPT6LnQKg,710
98
99
  howler/odm/models/cbs.py,sha256=onUiJOGUxK3iy_-4XkGGwHxFiFq9Td_p59Kum4XaR-w,1366
99
- howler/odm/models/config.py,sha256=jviwXPvBLkOxpDzOYeVQ72-rl4jhlamcmvBrqtAJM-Y,16123
100
+ howler/odm/models/config.py,sha256=WBjMuqWY7XYt4Gx7uWFpEJz0760yhr91Iw8bQZTBas4,16629
100
101
  howler/odm/models/dossier.py,sha256=Ob2qROrG2-DYzmVo2XVe4NJ8HjWGCoRAu2gPo6p9XGU,1244
101
102
  howler/odm/models/ecs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
102
103
  howler/odm/models/ecs/agent.py,sha256=idSooyFCLuQAB7_RyEWTYW4-x9w5a3wpy2ct_-EDRQs,713
@@ -193,7 +194,7 @@ howler/utils/path.py,sha256=DfOU4i4zSs4wchHoE8iE7aWVLkTxiC_JRGepF2hBYBk,690
193
194
  howler/utils/socket_utils.py,sha256=nz1SklC9xBHUSfHyTJjpq3mbozX1GDf01WzdGxfaUII,2212
194
195
  howler/utils/str_utils.py,sha256=HE8Hqh2HlOLaj16w0H9zKOyDJLp-f1LQ50y_WeGZaEk,8389
195
196
  howler/utils/uid.py,sha256=p9dsqyvZ-lpiAuzZWCPCeEM99kdk0Ly9czf04HNdSuw,1341
196
- howler_api-2.12.0.dev285.dist-info/METADATA,sha256=8HJ5Mbd2tySG-zjH-z6_5UF1XyOABST62_OTju_kMyM,2815
197
- howler_api-2.12.0.dev285.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
198
- howler_api-2.12.0.dev285.dist-info/entry_points.txt,sha256=Lu9SBGvwe0wczJHmc-RudC24lmQk7tv3ZBXon9RIihg,259
199
- howler_api-2.12.0.dev285.dist-info/RECORD,,
197
+ howler_api-2.12.0.dev294.dist-info/METADATA,sha256=IyUU6T7TekQltonOSedkzSQ1mpcRu8uOkasVlXMWFuA,2815
198
+ howler_api-2.12.0.dev294.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
199
+ howler_api-2.12.0.dev294.dist-info/entry_points.txt,sha256=Lu9SBGvwe0wczJHmc-RudC24lmQk7tv3ZBXon9RIihg,259
200
+ howler_api-2.12.0.dev294.dist-info/RECORD,,