howler-api 3.4.0.dev927__tar.gz → 3.4.0.dev933__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 (204) hide show
  1. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/PKG-INFO +1 -1
  2. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/api/v1/hit.py +3 -11
  3. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/api/v1/search.py +29 -0
  4. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/api/v1/tool.py +1 -1
  5. howler_api-3.4.0.dev933/howler/cronjobs/action_queue_worker.py +82 -0
  6. howler_api-3.4.0.dev933/howler/cronjobs/retention.py +129 -0
  7. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/datastore/collection.py +124 -204
  8. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/datastore/howler_store.py +1 -2
  9. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/datastore/store.py +3 -12
  10. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/datastore/types.py +4 -0
  11. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/config.py +15 -45
  12. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/services/action_service.py +95 -0
  13. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/services/hit_service.py +5 -5
  14. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/pyproject.toml +1 -1
  15. howler_api-3.4.0.dev927/howler/cronjobs/retention.py +0 -61
  16. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/README.md +0 -0
  17. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/__init__.py +0 -0
  18. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/actions/__init__.py +0 -0
  19. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/actions/add_label.py +0 -0
  20. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/actions/add_to_bundle.py +0 -0
  21. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/actions/change_field.py +0 -0
  22. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/actions/demote.py +0 -0
  23. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/actions/example_plugin.py +0 -0
  24. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/actions/prioritization.py +0 -0
  25. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/actions/promote.py +0 -0
  26. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/actions/remove_from_bundle.py +0 -0
  27. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/actions/remove_label.py +0 -0
  28. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/actions/transition.py +0 -0
  29. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/api/__init__.py +0 -0
  30. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/api/base.py +0 -0
  31. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/api/socket.py +0 -0
  32. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/api/v1/__init__.py +0 -0
  33. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/api/v1/action.py +0 -0
  34. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/api/v1/analytic.py +0 -0
  35. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/api/v1/auth.py +0 -0
  36. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/api/v1/clue.py +0 -0
  37. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/api/v1/configs.py +0 -0
  38. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/api/v1/dossier.py +0 -0
  39. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/api/v1/help.py +0 -0
  40. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/api/v1/notebook.py +0 -0
  41. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/api/v1/overview.py +0 -0
  42. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/api/v1/template.py +0 -0
  43. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/api/v1/user.py +0 -0
  44. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/api/v1/utils/__init__.py +0 -0
  45. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/api/v1/utils/etag.py +0 -0
  46. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/api/v1/view.py +0 -0
  47. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/app.py +0 -0
  48. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/common/README.md +0 -0
  49. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/common/__init__.py +0 -0
  50. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/common/classification.py +0 -0
  51. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/common/classification.yml +0 -0
  52. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/common/exceptions.py +0 -0
  53. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/common/loader.py +0 -0
  54. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/common/logging/__init__.py +0 -0
  55. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/common/logging/audit.py +0 -0
  56. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/common/logging/format.py +0 -0
  57. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/common/net.py +0 -0
  58. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/common/net_static.py +0 -0
  59. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/common/random_user.py +0 -0
  60. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/common/swagger.py +0 -0
  61. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/config.py +0 -0
  62. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/cronjobs/__init__.py +0 -0
  63. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/cronjobs/rules.py +0 -0
  64. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/cronjobs/view_cleanup.py +0 -0
  65. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/datastore/README.md +0 -0
  66. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/datastore/__init__.py +0 -0
  67. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/datastore/bulk.py +0 -0
  68. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/datastore/constants.py +0 -0
  69. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/datastore/exceptions.py +0 -0
  70. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/datastore/migrations/fix_process.py +0 -0
  71. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/datastore/operations.py +0 -0
  72. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/datastore/schemas.py +0 -0
  73. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/datastore/support/__init__.py +0 -0
  74. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/datastore/support/build.py +0 -0
  75. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/datastore/support/schemas.py +0 -0
  76. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/error.py +0 -0
  77. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/external/README.md +0 -0
  78. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/external/__init__.py +0 -0
  79. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/external/generate_mitre.py +0 -0
  80. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/external/generate_sigma_rules.py +0 -0
  81. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/external/generate_tlds.py +0 -0
  82. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/external/reindex_data.py +0 -0
  83. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/external/wipe_databases.py +0 -0
  84. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/gunicorn_config.py +0 -0
  85. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/healthz.py +0 -0
  86. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/helper/__init__.py +0 -0
  87. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/helper/azure.py +0 -0
  88. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/helper/discover.py +0 -0
  89. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/helper/hit.py +0 -0
  90. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/helper/oauth.py +0 -0
  91. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/helper/search.py +0 -0
  92. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/helper/workflow.py +0 -0
  93. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/helper/ws.py +0 -0
  94. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/README.md +0 -0
  95. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/__init__.py +0 -0
  96. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/base.py +0 -0
  97. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/charter.txt +0 -0
  98. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/helper.py +0 -0
  99. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/howler_enum.py +0 -0
  100. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/__init__.py +0 -0
  101. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/action.py +0 -0
  102. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/analytic.py +0 -0
  103. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/assemblyline.py +0 -0
  104. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/aws.py +0 -0
  105. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/azure.py +0 -0
  106. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/cbs.py +0 -0
  107. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/clue.py +0 -0
  108. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/dossier.py +0 -0
  109. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/__init__.py +0 -0
  110. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/agent.py +0 -0
  111. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/autonomous_system.py +0 -0
  112. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/client.py +0 -0
  113. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/cloud.py +0 -0
  114. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/code_signature.py +0 -0
  115. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/container.py +0 -0
  116. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/dns.py +0 -0
  117. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/egress.py +0 -0
  118. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/elf.py +0 -0
  119. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/email.py +0 -0
  120. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/error.py +0 -0
  121. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/event.py +0 -0
  122. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/faas.py +0 -0
  123. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/file.py +0 -0
  124. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/geo.py +0 -0
  125. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/group.py +0 -0
  126. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/hash.py +0 -0
  127. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/host.py +0 -0
  128. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/http.py +0 -0
  129. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/ingress.py +0 -0
  130. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/interface.py +0 -0
  131. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/network.py +0 -0
  132. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/observer.py +0 -0
  133. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/organization.py +0 -0
  134. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/os.py +0 -0
  135. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/pe.py +0 -0
  136. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/process.py +0 -0
  137. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/registry.py +0 -0
  138. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/related.py +0 -0
  139. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/rule.py +0 -0
  140. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/server.py +0 -0
  141. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/threat.py +0 -0
  142. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/tls.py +0 -0
  143. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/url.py +0 -0
  144. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/user.py +0 -0
  145. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/user_agent.py +0 -0
  146. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/vulnerability.py +0 -0
  147. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/gcp.py +0 -0
  148. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/hit.py +0 -0
  149. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/howler_data.py +0 -0
  150. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/lead.py +0 -0
  151. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/localized_label.py +0 -0
  152. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/overview.py +0 -0
  153. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/pivot.py +0 -0
  154. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/template.py +0 -0
  155. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/user.py +0 -0
  156. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/view.py +0 -0
  157. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/random_data.py +0 -0
  158. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/randomizer.py +0 -0
  159. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/patched.py +0 -0
  160. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/plugins/__init__.py +0 -0
  161. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/plugins/config.py +0 -0
  162. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/remote/__init__.py +0 -0
  163. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/remote/datatypes/README.md +0 -0
  164. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/remote/datatypes/__init__.py +0 -0
  165. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/remote/datatypes/counters.py +0 -0
  166. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/remote/datatypes/events.py +0 -0
  167. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/remote/datatypes/hash.py +0 -0
  168. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/remote/datatypes/lock.py +0 -0
  169. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/remote/datatypes/queues/__init__.py +0 -0
  170. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/remote/datatypes/queues/comms.py +0 -0
  171. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/remote/datatypes/queues/multi.py +0 -0
  172. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/remote/datatypes/queues/named.py +0 -0
  173. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/remote/datatypes/queues/priority.py +0 -0
  174. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/remote/datatypes/set.py +0 -0
  175. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/remote/datatypes/user_quota_tracker.py +0 -0
  176. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/security/__init__.py +0 -0
  177. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/security/socket.py +0 -0
  178. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/security/utils.py +0 -0
  179. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/services/__init__.py +0 -0
  180. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/services/analytic_service.py +0 -0
  181. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/services/auth_service.py +0 -0
  182. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/services/config_service.py +0 -0
  183. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/services/dossier_service.py +0 -0
  184. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/services/event_service.py +0 -0
  185. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/services/jwt_service.py +0 -0
  186. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/services/lucene_service.py +0 -0
  187. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/services/notebook_service.py +0 -0
  188. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/services/overview_service.py +0 -0
  189. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/services/template_service.py +0 -0
  190. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/services/user_service.py +0 -0
  191. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/telemetry.py +0 -0
  192. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/utils/__init__.py +0 -0
  193. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/utils/annotations.py +0 -0
  194. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/utils/chunk.py +0 -0
  195. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/utils/compat.py +0 -0
  196. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/utils/constants.py +0 -0
  197. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/utils/dict_utils.py +0 -0
  198. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/utils/isotime.py +0 -0
  199. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/utils/list_utils.py +0 -0
  200. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/utils/lucene.py +0 -0
  201. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/utils/path.py +0 -0
  202. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/utils/socket_utils.py +0 -0
  203. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/utils/str_utils.py +0 -0
  204. {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/utils/uid.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: howler-api
3
- Version: 3.4.0.dev927
3
+ Version: 3.4.0.dev933
4
4
  Summary: Howler - API server
5
5
  License: MIT
6
6
  Keywords: howler,alerting,gc,canada,cse-cst,cse,cst,cyber,cccs
@@ -123,7 +123,7 @@ def create_hits(user: User, **kwargs):
123
123
 
124
124
  datastore().hit.commit()
125
125
 
126
- action_service.bulk_execute_on_query(f"howler.id:({' OR '.join(odm.howler.id for odm in odms)})", user=user)
126
+ action_service.enqueue_action_execution([odm.howler.id for odm in odms], trigger="create", user=user)
127
127
 
128
128
  response_body["warnings"] = warnings
129
129
 
@@ -551,11 +551,7 @@ def add_label(id, label_set, user, **kwargs):
551
551
 
552
552
  datastore().hit.commit()
553
553
 
554
- action_service.bulk_execute_on_query(
555
- f"howler.id:{id}",
556
- trigger="add_label",
557
- user=user,
558
- )
554
+ action_service.enqueue_action_execution([id], trigger="add_label", user=user)
559
555
 
560
556
  hit, version = hit_service.get_hit(id, as_odm=False, version=True)
561
557
 
@@ -609,11 +605,7 @@ def remove_labels(id, label_set, user, **kwargs):
609
605
 
610
606
  datastore().hit.commit()
611
607
 
612
- action_service.bulk_execute_on_query(
613
- f"howler.id:{id}",
614
- trigger="remove_label",
615
- user=user,
616
- )
608
+ action_service.enqueue_action_execution([id], trigger="remove_label", user=user)
617
609
 
618
610
  hit, version = hit_service.get_hit(id, as_odm=False, version=True)
619
611
 
@@ -78,6 +78,7 @@ def search(index, **kwargs):
78
78
  sort => How to sort the results (not available in deep paging)
79
79
  fl => List of fields to return
80
80
  timeout => Maximum execution time (ms)
81
+ use_archive => Allow access to the datastore achive (Default: False)
81
82
  track_total_hits => Track the total number of query matches, instead of stopping at 10000 (Default: False)
82
83
  metadata => A list of additional features to be added to the result alongside the raw results
83
84
 
@@ -117,9 +118,18 @@ def search(index, **kwargs):
117
118
  "track_total_hits",
118
119
  ]
119
120
  multi_fields = ["filters", "metadata"]
121
+ boolean_fields = ["use_archive"]
120
122
 
121
123
  params, req_data = generate_params(request, fields, multi_fields)
122
124
 
125
+ params.update(
126
+ {
127
+ k: str(req_data.get(k, "false")).lower() in ["true", ""]
128
+ for k in boolean_fields
129
+ if req_data.get(k, None) is not None
130
+ }
131
+ )
132
+
123
133
  if has_access_control(index):
124
134
  params.update({"access_control": user["access_control"]})
125
135
 
@@ -340,9 +350,18 @@ def sigma_search(index, **kwargs):
340
350
  "track_total_hits",
341
351
  ]
342
352
  multi_fields = ["filters"]
353
+ boolean_fields = ["use_archive"]
343
354
 
344
355
  params, req_data = generate_params(request, fields, multi_fields)
345
356
 
357
+ params.update(
358
+ {
359
+ k: str(req_data.get(k, "false")).lower() in ["true", ""]
360
+ for k in boolean_fields
361
+ if req_data.get(k, None) is not None
362
+ }
363
+ )
364
+
346
365
  if has_access_control(index):
347
366
  params.update({"access_control": user["access_control"]})
348
367
 
@@ -501,6 +520,7 @@ def count(index, **kwargs):
501
520
  Optional Arguments:
502
521
  filters => List of additional filter queries limit the data
503
522
  timeout => Maximum execution time (ms)
523
+ use_archive => Allow access to the datastore achive (Default: False)
504
524
 
505
525
  Data Block:
506
526
  # Note that the data block is for POST requests only!
@@ -523,6 +543,15 @@ def count(index, **kwargs):
523
543
 
524
544
  params, req_data = generate_params(request, [], [])
525
545
 
546
+ boolean_fields = ["use_archive"]
547
+ params.update(
548
+ {
549
+ k: str(req_data.get(k, "false")).lower() in ["true", ""]
550
+ for k in boolean_fields
551
+ if req_data.get(k, None) is not None
552
+ }
553
+ )
554
+
526
555
  if has_access_control(index):
527
556
  params.update({"access_control": user["access_control"]})
528
557
 
@@ -178,6 +178,6 @@ def create_one_or_many_hits(tool_name: str, user: User, **kwargs): # noqa: C901
178
178
 
179
179
  datastore().hit.commit()
180
180
 
181
- action_service.bulk_execute_on_query(f"howler.id:({' OR '.join(entry['id'] for entry in out)})", user=user)
181
+ action_service.enqueue_action_execution([entry["id"] for entry in out], trigger="create", user=user)
182
182
 
183
183
  return created(out, warnings=warnings)
@@ -0,0 +1,82 @@
1
+ """Action queue worker cronjob — starts per-trigger action queue consumer threads.
2
+
3
+ Auto-discovered by ``howler.cronjobs.setup_jobs`` when ``HWL_USE_JOB_SYSTEM``
4
+ is enabled. Instead of scheduling a periodic APScheduler job, this module
5
+ starts a long-running daemon thread per trigger type that drains its
6
+ dedicated action queue.
7
+ """
8
+
9
+ import threading
10
+
11
+ from apscheduler.schedulers.base import BaseScheduler
12
+
13
+ from howler.common.logging import get_logger
14
+ from howler.config import config
15
+ from howler.odm.models.action import VALID_TRIGGERS
16
+ from howler.services.action_service import _get_action_queue, process_action_batch
17
+
18
+ logger = get_logger(__file__)
19
+
20
+ _threads: dict[str, threading.Thread] = {}
21
+
22
+ BATCH_SIZE: int = config.system.action_queue.batch_size
23
+ BATCH_TIMEOUT: int = config.system.action_queue.batch_timeout
24
+
25
+
26
+ def run_worker(trigger: str) -> None: # pragma: no cover – long-running loop, tested via process_action_batch
27
+ """Block on the action queue for *trigger* and process batches.
28
+
29
+ Accumulates up to ``BATCH_SIZE`` items or flushes after ``BATCH_TIMEOUT``
30
+ seconds, whichever comes first.
31
+ """
32
+ if not config.system.action_queue.enabled:
33
+ logger.info("Action queue worker disabled by configuration, not starting for trigger=%s", trigger)
34
+ return
35
+
36
+ queue = _get_action_queue(trigger)
37
+ logger.info(
38
+ "Action queue worker started for trigger=%s (batch_size=%d, timeout=%ds)",
39
+ trigger,
40
+ BATCH_SIZE,
41
+ BATCH_TIMEOUT,
42
+ )
43
+
44
+ batch: list[dict] = []
45
+
46
+ while True:
47
+ try:
48
+ item = queue.pop(blocking=True, timeout=BATCH_TIMEOUT)
49
+
50
+ if item is not None:
51
+ batch.append(item)
52
+
53
+ if len(batch) >= BATCH_SIZE or (item is None and batch):
54
+ logger.debug("Processing action batch of %d item(s) for trigger=%s", len(batch), trigger)
55
+ try:
56
+ process_action_batch(trigger, batch)
57
+ logger.info("Action batch complete: %d item(s) processed for trigger=%s", len(batch), trigger)
58
+ except Exception:
59
+ logger.exception("Error processing action batch for trigger=%s", trigger)
60
+ finally:
61
+ batch = []
62
+ except Exception:
63
+ logger.exception("Unexpected error in action queue worker loop for trigger=%s", trigger)
64
+
65
+
66
+ def setup_job(sched: BaseScheduler):
67
+ """Start an action queue worker thread per trigger type if the action queue is enabled."""
68
+ global _threads
69
+
70
+ if not config.system.action_queue.enabled:
71
+ logger.info("Action queue worker disabled by configuration")
72
+ return
73
+
74
+ for trigger in VALID_TRIGGERS:
75
+ if trigger in _threads and _threads[trigger].is_alive():
76
+ logger.debug("Action queue worker thread for trigger=%s already running", trigger)
77
+ continue
78
+
79
+ thread = threading.Thread(target=run_worker, args=(trigger,), name=f"action-queue-{trigger}", daemon=True)
80
+ thread.start()
81
+ _threads[trigger] = thread
82
+ logger.info("Action queue worker thread started for trigger=%s", trigger)
@@ -0,0 +1,129 @@
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
+ from howler.datastore.howler_store import HowlerDatastore
12
+
13
+ logger = get_logger(__file__)
14
+
15
+
16
+ def execute():
17
+ """Delete any hits older than the configured time"""
18
+ from howler.common.loader import datastore
19
+
20
+ delta_kwargs = {str(config.system.retention.limit_unit): config.system.retention.limit_amount}
21
+
22
+ cutoff = (datetime.now() - timedelta(**delta_kwargs)).strftime("%Y-%m-%d")
23
+
24
+ logger.debug("Removing hits older than %s", cutoff)
25
+
26
+ ds = datastore()
27
+
28
+ ds.hit.delete_by_query(f"event.created:{{* TO {cutoff}}} OR howler.expiry:{{* TO now}}")
29
+
30
+ ds.hit.commit()
31
+
32
+ logger.debug("Deletion complete")
33
+
34
+ logger.debug("Cleaning analytics with no matching hits")
35
+ _remove_analytics_without_hits(ds)
36
+
37
+
38
+ def _remove_analytics_without_hits(ds: HowlerDatastore):
39
+
40
+ matched_analytics = _find_analytics_with_hits(ds)
41
+
42
+ if matched_analytics is not None:
43
+ ds.analytic.delete_by_search_object(
44
+ {
45
+ "bool": {
46
+ "filter": [
47
+ {
48
+ "bool": {
49
+ "must_not": [
50
+ {"exists": {"field": "rule"}},
51
+ {"exists": {"field": "rule_type"}},
52
+ ]
53
+ }
54
+ }
55
+ ],
56
+ "must_not": [{"terms": {"name": matched_analytics}}],
57
+ }
58
+ }
59
+ )
60
+ ds.analytic.commit()
61
+ else:
62
+ logger.warning(
63
+ "Aggregation search for matched analytics did not run or returned no results. "
64
+ "There is likely an issue with the query. Skipping cleanup."
65
+ )
66
+
67
+
68
+ def _find_analytics_with_hits(ds: HowlerDatastore) -> list[str] | None:
69
+
70
+ total_analytics = ds.analytic.count("id:*", filters=None)["count"]
71
+
72
+ if total_analytics:
73
+ matched_analytics = ds.hit.search(
74
+ "howler.id:*",
75
+ aggregations=[
76
+ (
77
+ "matched_analytics",
78
+ {
79
+ "terms": {
80
+ "field": "howler.analytic",
81
+ "size": total_analytics,
82
+ }
83
+ },
84
+ )
85
+ ],
86
+ rows=0,
87
+ )
88
+
89
+ if "matched_analytics" in matched_analytics["aggregations"]:
90
+ matched_analytic_names = [
91
+ bucket["key"] for bucket in matched_analytics["aggregations"]["matched_analytics"]["buckets"]
92
+ ]
93
+ else:
94
+ return None
95
+
96
+ else:
97
+ matched_analytic_names = []
98
+
99
+ return matched_analytic_names
100
+
101
+
102
+ def setup_job(sched: BaseScheduler):
103
+ """Initialize the retention job"""
104
+ if not config.system.retention.enabled:
105
+ if not DEBUG or config.system.type == "production":
106
+ logger.warning("Retention cronjob disabled! This is not recommended for a production settings.")
107
+
108
+ return
109
+
110
+ logger.debug("Initializing retention cronjob with cron %s", config.system.retention.crontab)
111
+
112
+ if DEBUG:
113
+ _kwargs: dict[str, Any] = {"next_run_time": datetime.now()}
114
+ else:
115
+ _kwargs = {}
116
+
117
+ if sched.get_job("retention"):
118
+ logger.debug("Retention job already running!")
119
+ return
120
+
121
+ sched.add_job(
122
+ id="retention",
123
+ func=execute,
124
+ trigger=CronTrigger.from_crontab(
125
+ config.system.retention.crontab, timezone=timezone(os.getenv("SCHEDULER_TZ", "America/Toronto"))
126
+ ),
127
+ **_kwargs,
128
+ )
129
+ logger.debug("Initialization complete")