howler-api 4.0.0.dev1141__tar.gz → 4.0.0.dev1147__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 (225) hide show
  1. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/PKG-INFO +3 -3
  2. howler_api-4.0.0.dev1147/howler/healthz.py +91 -0
  3. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/config.py +1 -0
  4. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/view.py +16 -1
  5. howler_api-4.0.0.dev1147/howler/remote/datatypes/__init__.py +257 -0
  6. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/remote/datatypes/counters.py +2 -2
  7. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/remote/datatypes/hash.py +1 -1
  8. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/remote/datatypes/user_quota_tracker.py +3 -3
  9. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/pyproject.toml +4 -5
  10. howler_api-4.0.0.dev1141/howler/healthz.py +0 -47
  11. howler_api-4.0.0.dev1141/howler/remote/datatypes/__init__.py +0 -158
  12. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/README.md +0 -0
  13. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/__init__.py +0 -0
  14. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/actions/__init__.py +0 -0
  15. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/actions/add_label.py +0 -0
  16. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/actions/add_to_bundle.py +0 -0
  17. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/actions/add_to_case.py +0 -0
  18. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/actions/change_field.py +0 -0
  19. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/actions/demote.py +0 -0
  20. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/actions/example_plugin.py +0 -0
  21. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/actions/prioritization.py +0 -0
  22. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/actions/promote.py +0 -0
  23. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/actions/remove_from_bundle.py +0 -0
  24. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/actions/remove_label.py +0 -0
  25. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/actions/transition.py +0 -0
  26. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/api/__init__.py +0 -0
  27. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/api/base.py +0 -0
  28. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/api/socket.py +0 -0
  29. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/api/v1/__init__.py +0 -0
  30. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/api/v1/action.py +0 -0
  31. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/api/v1/analytic.py +0 -0
  32. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/api/v1/auth.py +0 -0
  33. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/api/v1/clue.py +0 -0
  34. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/api/v1/configs.py +0 -0
  35. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/api/v1/dossier.py +0 -0
  36. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/api/v1/help.py +0 -0
  37. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/api/v1/hit.py +0 -0
  38. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/api/v1/notebook.py +0 -0
  39. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/api/v1/overview.py +0 -0
  40. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/api/v1/search.py +0 -0
  41. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/api/v1/template.py +0 -0
  42. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/api/v1/tool.py +0 -0
  43. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/api/v1/user.py +0 -0
  44. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/api/v1/utils/__init__.py +0 -0
  45. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/api/v1/utils/etag.py +0 -0
  46. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/api/v1/utils/params.py +0 -0
  47. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/api/v1/view.py +0 -0
  48. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/api/v2/__init__.py +0 -0
  49. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/api/v2/case.py +0 -0
  50. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/api/v2/fuzzy.py +0 -0
  51. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/api/v2/ingest.py +0 -0
  52. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/api/v2/search.py +0 -0
  53. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/app.py +0 -0
  54. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/common/README.md +0 -0
  55. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/common/__init__.py +0 -0
  56. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/common/classification.py +0 -0
  57. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/common/classification.yml +0 -0
  58. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/common/exceptions.py +0 -0
  59. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/common/loader.py +0 -0
  60. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/common/logging/__init__.py +0 -0
  61. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/common/logging/audit.py +0 -0
  62. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/common/logging/format.py +0 -0
  63. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/common/net.py +0 -0
  64. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/common/net_static.py +0 -0
  65. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/common/random_user.py +0 -0
  66. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/common/swagger.py +0 -0
  67. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/config.py +0 -0
  68. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/cronjobs/__init__.py +0 -0
  69. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/cronjobs/action_queue_worker.py +0 -0
  70. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/cronjobs/correlation.py +0 -0
  71. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/cronjobs/retention.py +0 -0
  72. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/cronjobs/view_cleanup.py +0 -0
  73. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/datastore/README.md +0 -0
  74. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/datastore/__init__.py +0 -0
  75. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/datastore/bulk.py +0 -0
  76. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/datastore/collection.py +0 -0
  77. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/datastore/constants.py +0 -0
  78. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/datastore/exceptions.py +0 -0
  79. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/datastore/howler_store.py +0 -0
  80. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/datastore/migrations/fix_process.py +0 -0
  81. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/datastore/operations.py +0 -0
  82. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/datastore/schemas.py +0 -0
  83. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/datastore/store.py +0 -0
  84. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/datastore/support/__init__.py +0 -0
  85. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/datastore/support/build.py +0 -0
  86. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/datastore/support/schemas.py +0 -0
  87. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/datastore/types.py +0 -0
  88. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/error.py +0 -0
  89. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/external/README.md +0 -0
  90. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/external/__init__.py +0 -0
  91. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/external/generate_mitre.py +0 -0
  92. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/external/generate_sigma_rules.py +0 -0
  93. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/external/generate_tlds.py +0 -0
  94. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/external/reindex_data.py +0 -0
  95. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/external/wipe_databases.py +0 -0
  96. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/gunicorn_config.py +0 -0
  97. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/helper/__init__.py +0 -0
  98. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/helper/azure.py +0 -0
  99. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/helper/discover.py +0 -0
  100. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/helper/hit.py +0 -0
  101. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/helper/oauth.py +0 -0
  102. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/helper/search.py +0 -0
  103. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/helper/workflow.py +0 -0
  104. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/helper/ws.py +0 -0
  105. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/README.md +0 -0
  106. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/__init__.py +0 -0
  107. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/base.py +0 -0
  108. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/charter.txt +0 -0
  109. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/constants.py +0 -0
  110. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/helper.py +0 -0
  111. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/howler_enum.py +0 -0
  112. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/mixins.py +0 -0
  113. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/__init__.py +0 -0
  114. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/action.py +0 -0
  115. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/analytic.py +0 -0
  116. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/assemblyline.py +0 -0
  117. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/aws.py +0 -0
  118. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/azure.py +0 -0
  119. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/case.py +0 -0
  120. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/cbs.py +0 -0
  121. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/clue.py +0 -0
  122. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/dossier.py +0 -0
  123. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/ecs/__init__.py +0 -0
  124. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/ecs/agent.py +0 -0
  125. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/ecs/autonomous_system.py +0 -0
  126. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/ecs/client.py +0 -0
  127. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/ecs/cloud.py +0 -0
  128. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/ecs/code_signature.py +0 -0
  129. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/ecs/container.py +0 -0
  130. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/ecs/dns.py +0 -0
  131. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/ecs/egress.py +0 -0
  132. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/ecs/elf.py +0 -0
  133. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/ecs/email.py +0 -0
  134. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/ecs/error.py +0 -0
  135. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/ecs/event.py +0 -0
  136. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/ecs/faas.py +0 -0
  137. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/ecs/file.py +0 -0
  138. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/ecs/geo.py +0 -0
  139. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/ecs/group.py +0 -0
  140. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/ecs/hash.py +0 -0
  141. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/ecs/host.py +0 -0
  142. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/ecs/http.py +0 -0
  143. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/ecs/ingress.py +0 -0
  144. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/ecs/interface.py +0 -0
  145. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/ecs/network.py +0 -0
  146. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/ecs/observer.py +0 -0
  147. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/ecs/organization.py +0 -0
  148. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/ecs/os.py +0 -0
  149. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/ecs/pe.py +0 -0
  150. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/ecs/process.py +0 -0
  151. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/ecs/registry.py +0 -0
  152. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/ecs/related.py +0 -0
  153. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/ecs/rule.py +0 -0
  154. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/ecs/server.py +0 -0
  155. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/ecs/threat.py +0 -0
  156. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/ecs/tls.py +0 -0
  157. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/ecs/url.py +0 -0
  158. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/ecs/user.py +0 -0
  159. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/ecs/user_agent.py +0 -0
  160. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/ecs/vulnerability.py +0 -0
  161. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/event.py +0 -0
  162. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/gcp.py +0 -0
  163. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/hit.py +0 -0
  164. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/howler_data.py +0 -0
  165. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/lead.py +0 -0
  166. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/localized_label.py +0 -0
  167. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/overview.py +0 -0
  168. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/pivot.py +0 -0
  169. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/record.py +0 -0
  170. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/template.py +0 -0
  171. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/models/user.py +0 -0
  172. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/random_data.py +0 -0
  173. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/odm/randomizer.py +0 -0
  174. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/patched.py +0 -0
  175. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/plugins/__init__.py +0 -0
  176. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/plugins/config.py +0 -0
  177. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/remote/__init__.py +0 -0
  178. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/remote/datatypes/README.md +0 -0
  179. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/remote/datatypes/events.py +0 -0
  180. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/remote/datatypes/lock.py +0 -0
  181. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/remote/datatypes/queues/__init__.py +0 -0
  182. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/remote/datatypes/queues/comms.py +0 -0
  183. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/remote/datatypes/queues/multi.py +0 -0
  184. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/remote/datatypes/queues/named.py +0 -0
  185. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/remote/datatypes/queues/priority.py +0 -0
  186. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/remote/datatypes/set.py +0 -0
  187. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/security/__init__.py +0 -0
  188. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/security/socket.py +0 -0
  189. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/security/utils.py +0 -0
  190. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/services/__init__.py +0 -0
  191. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/services/action_service.py +0 -0
  192. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/services/analytic_service.py +0 -0
  193. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/services/auth_service.py +0 -0
  194. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/services/bundle_compat_service.py +0 -0
  195. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/services/case_service.py +0 -0
  196. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/services/comms_service.py +0 -0
  197. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/services/config_service.py +0 -0
  198. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/services/correlation_service.py +0 -0
  199. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/services/docs_service.py +0 -0
  200. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/services/dossier_service.py +0 -0
  201. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/services/event_service.py +0 -0
  202. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/services/fuzzy_service.py +0 -0
  203. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/services/hit_service.py +0 -0
  204. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/services/jwt_service.py +0 -0
  205. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/services/lucene_service.py +0 -0
  206. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/services/notebook_service.py +0 -0
  207. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/services/overview_service.py +0 -0
  208. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/services/search_service.py +0 -0
  209. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/services/template_service.py +0 -0
  210. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/services/user_service.py +0 -0
  211. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/services/viewer_service.py +0 -0
  212. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/telemetry.py +0 -0
  213. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/utils/__init__.py +0 -0
  214. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/utils/annotations.py +0 -0
  215. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/utils/chunk.py +0 -0
  216. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/utils/compat.py +0 -0
  217. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/utils/constants.py +0 -0
  218. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/utils/dict_utils.py +0 -0
  219. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/utils/isotime.py +0 -0
  220. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/utils/list_utils.py +0 -0
  221. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/utils/lucene.py +0 -0
  222. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/utils/path.py +0 -0
  223. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/utils/socket_utils.py +0 -0
  224. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/utils/str_utils.py +0 -0
  225. {howler_api-4.0.0.dev1141 → howler_api-4.0.0.dev1147}/howler/utils/uid.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: howler-api
3
- Version: 4.0.0.dev1141
3
+ Version: 4.0.0.dev1147
4
4
  Summary: Howler - API server
5
5
  License: MIT
6
6
  Keywords: howler,alerting,gc,canada,cse-cst,cse,cst,cyber,cccs
@@ -19,7 +19,7 @@ Classifier: Programming Language :: Python :: 3.12
19
19
  Classifier: Programming Language :: Python :: 3.13
20
20
  Classifier: Programming Language :: Python :: 3.14
21
21
  Classifier: Topic :: Software Development :: Libraries
22
- Requires-Dist: apscheduler (==3.11.2)
22
+ Requires-Dist: apscheduler (==3.11.3)
23
23
  Requires-Dist: authlib (>=1.6.0,<2.0.0)
24
24
  Requires-Dist: azure-monitor-opentelemetry (>=1.8.7,<2.0.0)
25
25
  Requires-Dist: bcrypt (==4.3.0)
@@ -50,7 +50,7 @@ Requires-Dist: python-datemath (==3.0.3)
50
50
  Requires-Dist: python-dotenv (>=1.1.0,<2.0.0)
51
51
  Requires-Dist: pytz (>=2025.2,<2026.0)
52
52
  Requires-Dist: pyyaml (==6.0.3)
53
- Requires-Dist: redis (==4.6.0)
53
+ Requires-Dist: redis (==8.0.1)
54
54
  Requires-Dist: requests (==2.34.2)
55
55
  Requires-Dist: tzdata (>=2026.1,<2027.0)
56
56
  Requires-Dist: validators (>=0.34,<0.36)
@@ -0,0 +1,91 @@
1
+ from flask import Blueprint, abort, make_response
2
+ from redis import RedisError
3
+
4
+ from howler import config as howler_config
5
+ from howler.common.loader import datastore
6
+ from howler.common.logging import get_logger
7
+
8
+ API_PREFIX = "/api/healthz"
9
+ healthz = Blueprint("healthz", __name__, url_prefix=API_PREFIX)
10
+ logger = get_logger(__file__)
11
+
12
+
13
+ def _is_datastore_healthy() -> bool:
14
+ """Check whether the datastore is reachable."""
15
+ try:
16
+ if datastore().ds.ping():
17
+ return True
18
+ except Exception as error: # noqa: BLE001
19
+ logger.warning("Health check failed: datastore ping raised %s", error)
20
+ return False
21
+
22
+ logger.warning("Health check failed: datastore ping returned false")
23
+ return False
24
+
25
+
26
+ def _is_redis_healthy() -> bool:
27
+ """Check whether configured Redis clients are reachable."""
28
+ redis_config = howler_config.config.core.redis
29
+
30
+ if redis_config.nonpersistent.host:
31
+ try:
32
+ if not howler_config.redis.ping():
33
+ logger.warning("Health check failed: nonpersistent Redis ping returned false")
34
+ return False
35
+ except (ConnectionResetError, OSError, RedisError) as error:
36
+ logger.warning("Health check failed: nonpersistent Redis ping raised %s", error)
37
+ return False
38
+
39
+ if redis_config.persistent.host:
40
+ try:
41
+ if not howler_config.redis_persistent.ping():
42
+ logger.warning("Health check failed: persistent Redis ping returned false")
43
+ return False
44
+ except (ConnectionResetError, OSError, RedisError) as error:
45
+ logger.warning("Health check failed: persistent Redis ping raised %s", error)
46
+ return False
47
+
48
+ return True
49
+
50
+
51
+ @healthz.route("/live")
52
+ def liveness(**_):
53
+ """Check if the API is live
54
+
55
+ Variables:
56
+ None
57
+
58
+ Arguments:
59
+ None
60
+
61
+ Result Example:
62
+ OK or FAIL
63
+ """
64
+ return make_response("OK")
65
+
66
+
67
+ @healthz.route("/ready")
68
+ def readyness(**_):
69
+ """Check if the API is Ready
70
+
71
+ Checks that the datastore and configured Redis connections are alive.
72
+
73
+ Variables:
74
+ None
75
+
76
+ Arguments:
77
+ None
78
+
79
+ Result Example:
80
+ OK or FAIL
81
+ """
82
+ if _is_datastore_healthy() and _is_redis_healthy():
83
+ return make_response("OK")
84
+ else:
85
+ abort(503)
86
+
87
+
88
+ @healthz.errorhandler(503)
89
+ def error(_):
90
+ "Handle errors exposed in healthz routes"
91
+ return "FAIL", 503
@@ -33,6 +33,7 @@ class RedisServer(BaseModel):
33
33
  tls_ca_cert: Optional[str] = Field(
34
34
  description="Path to CA Certificate (PEM) to validate Redis instance certificate when using TLS", default=None
35
35
  )
36
+ tls_disable_check_hostname: bool = Field(default=False, description="Disable TLS hostname check")
36
37
  is_cluster: bool = Field(default=False, description="Is this Redis instance a cluster?")
37
38
 
38
39
 
@@ -1,14 +1,29 @@
1
1
  # mypy: ignore-errors
2
- from typing import Literal, Union
2
+ from typing import Literal, Optional, Union
3
3
 
4
4
  from howler import odm
5
5
 
6
6
 
7
+ @odm.model(index=True, store=True, description="The field and width of a column to display in a grid view.")
8
+ class GridColumn(odm.Model):
9
+ field: str = odm.Keyword(description="The field key for this column.")
10
+ width: Optional[int] = odm.Optional(odm.Integer(description="The width of this column in pixels."))
11
+
12
+
7
13
  @odm.model(index=True, store=True, description="Additional View Settings")
8
14
  class Settings(odm.Model):
9
15
  advance_on_triage: bool = odm.Boolean(
10
16
  description="Should the user advance to the next alert when triage is complete?", default=False
11
17
  )
18
+ display: Optional[Union[Literal["list"], Literal["grid"]]] = odm.Optional(
19
+ odm.Enum(
20
+ values=["list", "grid"],
21
+ description="The layout to use when opening this view",
22
+ )
23
+ )
24
+ columns: Optional[list[GridColumn]] = odm.Optional(
25
+ odm.List(odm.Compound(GridColumn, description="The columns to display in this view."))
26
+ )
12
27
 
13
28
 
14
29
  DEFAULT_INDEXES = ["hit"]
@@ -0,0 +1,257 @@
1
+ #!/usr/bin/env python
2
+
3
+ import json
4
+ import logging
5
+ import os
6
+ import socket
7
+ import time
8
+ from datetime import datetime, timezone
9
+ from pathlib import Path
10
+ from typing import Any
11
+
12
+ import redis
13
+ from packaging.version import parse
14
+ from redis.backoff import ExponentialBackoff
15
+ from redis.retry import Retry
16
+
17
+ from howler.odm.models.config import config
18
+ from howler.utils.uid import get_random_id
19
+
20
+ # Add a version warning if redis python client is < 2.10.0. Older versions
21
+ # have a connection bug that can manifest with the dispatcher.
22
+ if parse(redis.__version__) <= parse("2.10.0"):
23
+ import warnings
24
+
25
+ warnings.warn(
26
+ "%s works best with redis > 2.10.0. You're running"
27
+ " redis %s. You should upgrade." % (__name__, redis.__version__)
28
+ )
29
+
30
+ APP_NAME = os.environ.get("APP_NAME", "howler")
31
+
32
+ log = logging.getLogger(f"{APP_NAME}.queue")
33
+ pool: dict[tuple[str, str, bool], redis.BlockingConnectionPool] = {}
34
+
35
+ # TCP keepalive tuning so a connection to a primary that has gone away (e.g.
36
+ # during an Azure Managed Redis failover) is detected in ~90s instead of the
37
+ # Linux default keepalive idle of ~2 hours. This is the mechanism that lets
38
+ # *blocking* reads (BLPOP/BZPOPMIN with timeout=0) notice a dead peer, since we
39
+ # deliberately do NOT set socket_timeout (see RESILIENCE_CONFIG below). The
40
+ # option constants are platform specific, so each is added only if available.
41
+ _KEEPALIVE_OPTIONS: dict[int, int] = {
42
+ getattr(socket, name): value
43
+ for name, value in (("TCP_KEEPIDLE", 60), ("TCP_KEEPINTVL", 10), ("TCP_KEEPCNT", 3))
44
+ if hasattr(socket, name)
45
+ }
46
+
47
+ # Connection resilience defaults. These ensure that during a managed Redis
48
+ # failover (e.g. Azure Managed Redis) the client quickly detects dead/stale
49
+ # connections and reconnects to the new primary instead of hanging on the OS
50
+ # level TCP timeout (which can be several minutes).
51
+ # - socket_connect_timeout: cap how long establishing a new connection can
52
+ # block so a dead endpoint surfaces as a (retryable) error in seconds.
53
+ # - socket_keepalive / socket_keepalive_options: enable and tune TCP
54
+ # keepalives so half-open connections (including those blocked in a long
55
+ # BLPOP/BZPOPMIN) are detected and recycled.
56
+ # - health_check_interval: proactively PING idle pooled connections before
57
+ # use so stale connections to the old primary are recycled transparently.
58
+ #
59
+ # NOTE: socket_timeout is explicitly set to None. It is a per-read socket
60
+ # timeout that redis-py also applies to blocking commands, so any positive value
61
+ # makes BLPOP/BZPOPMIN/BRPOP with an infinite (timeout=0) wait raise spurious
62
+ # TimeoutErrors (see redis-py #2807). This MUST be set explicitly: redis-py 8.0
63
+ # changed the default socket_timeout from None to 5 seconds, so simply omitting
64
+ # it would silently break the infinite blocking pops the queue consumers in this
65
+ # package rely on. With socket_timeout disabled, keepalives + health checks
66
+ # handle failover detection instead.
67
+ RESILIENCE_CONFIG: dict[str, Any] = {
68
+ "socket_connect_timeout": 5,
69
+ "socket_timeout": None,
70
+ "socket_keepalive": True,
71
+ "health_check_interval": 15,
72
+ }
73
+ if _KEEPALIVE_OPTIONS:
74
+ RESILIENCE_CONFIG["socket_keepalive_options"] = _KEEPALIVE_OPTIONS
75
+
76
+ # Maximum wall-clock time (in seconds) that retry_call will keep retrying after
77
+ # the first observed Redis connection failure before giving up and re-raising the
78
+ # last error. The retry window starts after the first error because blocking
79
+ # Redis reads rely on TCP keepalive to notice a dead peer, and that detection can
80
+ # take longer than this retry budget.
81
+ RETRY_DEADLINE = 60
82
+
83
+
84
+ def now_as_iso():
85
+ s = datetime.now(timezone.utc).replace(tzinfo=None).isoformat()
86
+ return f"{s}Z"
87
+
88
+
89
+ def reply_queue_name(prefix=None, suffix=None):
90
+ if prefix:
91
+ components = [prefix]
92
+ else:
93
+ components = []
94
+
95
+ components.append(get_random_id())
96
+
97
+ if suffix:
98
+ components.append(str(suffix))
99
+
100
+ return "-".join(components)
101
+
102
+
103
+ def retry_call(func, *args, **kw):
104
+ maximum = 2
105
+ exponent = -7
106
+ deadline = None
107
+
108
+ while True:
109
+ try:
110
+ ret_val = func(*args, **kw)
111
+
112
+ if exponent != -7:
113
+ log.info("Reconnected to Redis!")
114
+
115
+ return ret_val
116
+ except (redis.ConnectionError, redis.TimeoutError, OSError) as ce:
117
+ # redis.ConnectionError covers BusyLoadingError (node loading after a
118
+ # failover) and most connect-time socket errors. redis.TimeoutError
119
+ # covers Redis read/connect timeouts. OSError covers the builtin socket
120
+ # errors raised while a managed Redis instance is failing over,
121
+ # including its ConnectionResetError and TimeoutError subclasses.
122
+ #
123
+ # Use a monotonic clock for the retry budget so NTP/system clock
124
+ # adjustments can't make retries give up early or run far too long.
125
+ if deadline is None:
126
+ deadline = time.monotonic() + RETRY_DEADLINE
127
+ elif time.monotonic() >= deadline:
128
+ # Give up after RETRY_DEADLINE so a sustained outage fails the
129
+ # request cleanly instead of blocking the worker indefinitely.
130
+ log.exception("No connection to Redis after %ss, giving up.", RETRY_DEADLINE)
131
+ raise
132
+
133
+ log.warning("No connection to Redis, reconnecting... [%s]", ce)
134
+ time.sleep(2**exponent)
135
+ exponent = exponent + 1 if exponent < maximum else exponent
136
+
137
+
138
+ def get_client(
139
+ host: str | redis.Redis | redis.StrictRedis | redis.RedisCluster | None,
140
+ port: int | None,
141
+ private: bool = False,
142
+ ):
143
+ """
144
+ Get Redis instance.
145
+
146
+ Args:
147
+ host: Redis host, defaults to nonpersistent host
148
+ port: Redis port, defaults to nonpersistent port
149
+ private: If true then use standard connection, otherwise use a Pool
150
+
151
+ Returns:
152
+ Redis instance
153
+ """
154
+ # In case a structure is passed a client as host
155
+ if isinstance(host, (redis.Redis, redis.StrictRedis, redis.RedisCluster)):
156
+ return host
157
+
158
+ if not host or not port:
159
+ host = host or config.core.redis.nonpersistent.host
160
+ port = int(port or config.core.redis.nonpersistent.port)
161
+
162
+ extra_conn_config: dict[str, Any] = {}
163
+ host_config = None
164
+
165
+ # Apply socket connect timeout / keepalive / health-check defaults so the
166
+ # client can detect and recover from managed Redis failovers instead of
167
+ # hanging.
168
+ extra_conn_config.update(RESILIENCE_CONFIG)
169
+
170
+ # Pin RESP2 on the wire. redis-py 8.0 switched the default to RESP3; pinning
171
+ # protocol=2 preserves the exact wire behaviour this codebase was written
172
+ # against so the dependency upgrade is behaviour-neutral. Can be revisited to
173
+ # adopt RESP3 once its response shapes have been validated end to end.
174
+ extra_conn_config["protocol"] = 2
175
+
176
+ # Configure connection-level retries using the modern Retry API (the
177
+ # replacement for the deprecated retry_on_timeout flag). A fresh Retry
178
+ # instance is built here per get_client call. Note that for pooled clients
179
+ # (private=False) the underlying connection pool is cached by host/port/ssl
180
+ # in get_pool, so this Retry is only applied when the pool is first created
181
+ # and is then effectively shared (per-pool) by every later client on that
182
+ # pool. The Retry config itself is immutable, so this sharing is benign.
183
+ extra_conn_config["retry"] = Retry(ExponentialBackoff(cap=10, base=0.5), retries=3)
184
+ extra_conn_config["retry_on_error"] = [redis.ConnectionError, redis.TimeoutError]
185
+
186
+ if host == config.core.redis.nonpersistent.host and port == config.core.redis.nonpersistent.port:
187
+ host_config = config.core.redis.nonpersistent
188
+ else:
189
+ host_config = config.core.redis.persistent
190
+
191
+ if host_config.password:
192
+ extra_conn_config["username"] = "default"
193
+ extra_conn_config["password"] = host_config.password
194
+
195
+ if host_config.tls_enabled:
196
+ extra_conn_config["ssl"] = True
197
+ extra_conn_config["ssl_cert_reqs"] = "required"
198
+
199
+ if host_config.tls_disable_check_hostname:
200
+ extra_conn_config["ssl_check_hostname"] = False
201
+
202
+ if host_config.tls_ca_cert:
203
+ if not Path(host_config.tls_ca_cert).exists():
204
+ raise FileNotFoundError(f"Redis TLS CA cert or path '{host_config.tls_ca_cert}' not found.")
205
+
206
+ if Path(host_config.tls_ca_cert).is_file():
207
+ extra_conn_config["ssl_ca_certs"] = host_config.tls_ca_cert
208
+ else:
209
+ extra_conn_config["ssl_ca_path"] = host_config.tls_ca_cert
210
+
211
+ if host_config.is_cluster is True:
212
+ return redis.RedisCluster(host=host, port=port, **extra_conn_config) # type: ignore
213
+
214
+ if private:
215
+ return redis.StrictRedis(host=host, port=port, **extra_conn_config) # type: ignore
216
+ else:
217
+ return redis.StrictRedis(connection_pool=get_pool(host, port, **extra_conn_config))
218
+
219
+
220
+ def get_pool(host, port, **kwargs):
221
+ """
222
+ Get Redis connection pool
223
+ Args:
224
+ host: Redis host
225
+ port: Redis port
226
+ **kwargs: Extra parameters to pass to pool connection class
227
+
228
+ Returns:
229
+ Redis BlockingConnectionPool
230
+ """
231
+ key = (host, str(port), kwargs.get("ssl", False))
232
+ connection_pool = pool.get(key, None)
233
+
234
+ if not connection_pool:
235
+ if "ssl" in kwargs and kwargs["ssl"]:
236
+ # SSLConnection class doesn't accept 'ssl' parameter as it implicitly uses SSL
237
+ kwargs.pop("ssl")
238
+ connection_pool = redis.BlockingConnectionPool(
239
+ host=host,
240
+ port=port,
241
+ max_connections=200,
242
+ connection_class=redis.SSLConnection,
243
+ **kwargs,
244
+ )
245
+ else:
246
+ connection_pool = redis.BlockingConnectionPool(host=host, port=port, max_connections=200, **kwargs)
247
+ pool[key] = connection_pool
248
+
249
+ return connection_pool
250
+
251
+
252
+ def decode(data):
253
+ try:
254
+ return json.loads(data)
255
+ except ValueError:
256
+ log.warning("Invalid data on queue: %s", str(data))
257
+ return None
@@ -1,4 +1,4 @@
1
- from typing import Optional
1
+ from typing import Any, Optional
2
2
 
3
3
  from redis.exceptions import ConnectionError
4
4
 
@@ -8,7 +8,7 @@ from howler.remote.datatypes.hash import Hash
8
8
 
9
9
  class Counters(object):
10
10
  def __init__(self, prefix="counter", host=None, port=None, track_counters=False):
11
- self.c = get_client(host, port, False)
11
+ self.c: Any = get_client(host, port, False)
12
12
  self.prefix = prefix
13
13
  if track_counters:
14
14
  self.tracker: Optional[Hash] = Hash("c-tracker-%s" % prefix, host=host, port=port)
@@ -73,7 +73,7 @@ class Hash(Generic[T]):
73
73
  def __init__(
74
74
  self,
75
75
  name: str,
76
- host: str | Redis[Any] | RedisCluster[Any] | None = None,
76
+ host: str | Redis | RedisCluster | None = None,
77
77
  port: Optional[int] = None,
78
78
  ):
79
79
  self.c: Any = get_client(host, port, False)
@@ -1,6 +1,6 @@
1
1
  from typing import Any
2
2
 
3
- import redis
3
+ from redis.exceptions import ResponseError
4
4
 
5
5
  from howler.remote.datatypes import get_client, retry_call
6
6
 
@@ -35,7 +35,7 @@ class UserQuotaTracker(object):
35
35
  def begin(self, user, max_quota):
36
36
  try:
37
37
  return retry_call(self.bs, args=[self._queue_name(user), max_quota, self.timeout]) == 1
38
- except redis.exceptions.ResponseError as er:
38
+ except ResponseError as er:
39
39
  # TODO: This is a failsafe for upgrade purposes could be removed in a future version
40
40
  if "WRONGTYPE" in str(er):
41
41
  retry_call(self.c.delete, self._queue_name(user))
@@ -47,7 +47,7 @@ class UserQuotaTracker(object):
47
47
  """When only one item is requested, blocking is is possible."""
48
48
  try:
49
49
  retry_call(self.c.zpopmin, self._queue_name(user))
50
- except redis.exceptions.ResponseError as er:
50
+ except ResponseError as er:
51
51
  # TODO: This is a failsafe for upgrade purposes could be removed in a future version
52
52
  if "WRONGTYPE" in str(er):
53
53
  retry_call(self.c.delete, self._queue_name(user))
@@ -152,7 +152,7 @@ suppress-none-returning = true
152
152
  [tool.poetry]
153
153
  package-mode = true
154
154
  name = "howler-api"
155
- version = "4.0.0.dev1141"
155
+ version = "4.0.0.dev1147"
156
156
  description = "Howler - API server"
157
157
  authors = [
158
158
  "Canadian Centre for Cyber Security <howler@cyber.gc.ca>",
@@ -197,7 +197,7 @@ repository = "https://github.com/CybercentreCanada/howler-api"
197
197
  [tool.poetry.dependencies]
198
198
  python = "^3.10"
199
199
  gunicorn = "23.0.0"
200
- apscheduler = "3.11.2"
200
+ apscheduler = "3.11.3"
201
201
  authlib = "^1.6.0"
202
202
  chardet = "5.2.0"
203
203
  elasticsearch = "8.19.3"
@@ -211,7 +211,7 @@ pyjwt = "2.13.0"
211
211
  python-baseconv = "1.2.2"
212
212
  python-datemath = "3.0.3"
213
213
  pyyaml = "6.0.3"
214
- redis = "4.6.0"
214
+ redis = "8.0.1"
215
215
  requests = "2.34.2"
216
216
  wsproto = "1.3.2"
217
217
  chevron = "0.14.0"
@@ -240,7 +240,7 @@ pyright = { extras = ["nodejs"], version = "^1.1.408" }
240
240
  mypy = "^1.6.1"
241
241
 
242
242
  [tool.poetry.group.test.dependencies]
243
- pytest = "9.0.3"
243
+ pytest = "9.1.1"
244
244
  retrying = "^1.3.4"
245
245
  pyftpdlib = "^1.5.9"
246
246
  pyopenssl = ">=24.1,<27.0"
@@ -255,7 +255,6 @@ typing-extensions = "^4.12.2"
255
255
  types-PyYAML = "6.0.12.20260518"
256
256
  types-paramiko = "3.5.0.20250801"
257
257
  types-pyOpenSSL = "23.3.0.20240106"
258
- types-redis = "4.6.0.20241004"
259
258
  types-six = "1.17.0.20260518"
260
259
  types-requests = "<2.33.0.20260519"
261
260
  websocket-client = "1.9.0"
@@ -1,47 +0,0 @@
1
- from flask import Blueprint, abort, make_response
2
-
3
- from howler.common.loader import datastore
4
-
5
- API_PREFIX = "/api/healthz"
6
- healthz = Blueprint("healthz", __name__, url_prefix=API_PREFIX)
7
-
8
-
9
- @healthz.route("/live")
10
- def liveness(**_):
11
- """Check if the API is live
12
-
13
- Variables:
14
- None
15
-
16
- Arguments:
17
- None
18
-
19
- Result Example:
20
- OK or FAIL
21
- """
22
- return make_response("OK")
23
-
24
-
25
- @healthz.route("/ready")
26
- def readyness(**_):
27
- """Check if the API is Ready
28
-
29
- Variables:
30
- None
31
-
32
- Arguments:
33
- None
34
-
35
- Result Example:
36
- OK or FAIL
37
- """
38
- if datastore().ds.ping():
39
- return make_response("OK")
40
- else:
41
- abort(503)
42
-
43
-
44
- @healthz.errorhandler(503)
45
- def error(_):
46
- "Handle errors exposed in healthz routes"
47
- return "FAIL", 503