howler-api 4.0.0.dev1084__tar.gz → 4.0.0.dev1087__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 (227) hide show
  1. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/PKG-INFO +3 -3
  2. howler_api-4.0.0.dev1087/howler/actions/add_to_bundle.py +171 -0
  3. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/actions/demote.py +2 -2
  4. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/actions/remove_from_bundle.py +46 -64
  5. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/actions/transition.py +7 -5
  6. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/api/__init__.py +1 -2
  7. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/api/socket.py +14 -44
  8. howler_api-4.0.0.dev1084/howler/services/docs_service.py → howler_api-4.0.0.dev1087/howler/api/v1/__init__.py +45 -37
  9. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/api/v1/action.py +2 -1
  10. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/api/v1/analytic.py +106 -17
  11. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/api/v1/hit.py +128 -52
  12. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/api/v1/tool.py +22 -47
  13. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/api/v1/user.py +1 -1
  14. howler_api-4.0.0.dev1087/howler/api/v1/utils/etag.py +84 -0
  15. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/app.py +0 -15
  16. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/common/loader.py +5 -4
  17. howler_api-4.0.0.dev1087/howler/cronjobs/rules.py +279 -0
  18. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/datastore/collection.py +25 -40
  19. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/datastore/howler_store.py +14 -29
  20. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/datastore/types.py +6 -1
  21. howler_api-4.0.0.dev1087/howler/healthz.py +91 -0
  22. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/helper/discover.py +8 -12
  23. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/helper/hit.py +4 -4
  24. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/helper/search.py +3 -13
  25. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/helper.py +7 -113
  26. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/config.py +1 -14
  27. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/ecs/event.py +1 -1
  28. howler_api-4.0.0.dev1084/howler/odm/models/record.py → howler_api-4.0.0.dev1087/howler/odm/models/hit.py +28 -15
  29. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/howler_data.py +35 -3
  30. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/view.py +0 -8
  31. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/random_data.py +50 -368
  32. howler_api-4.0.0.dev1087/howler/remote/datatypes/__init__.py +250 -0
  33. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/remote/datatypes/counters.py +2 -2
  34. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/remote/datatypes/hash.py +1 -1
  35. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/remote/datatypes/queues/comms.py +1 -2
  36. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/remote/datatypes/user_quota_tracker.py +3 -3
  37. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/services/config_service.py +3 -3
  38. howler_api-4.0.0.dev1087/howler/services/event_service.py +96 -0
  39. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/services/hit_service.py +176 -89
  40. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/utils/socket_utils.py +26 -5
  41. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/pyproject.toml +7 -8
  42. howler_api-4.0.0.dev1084/howler/actions/add_to_bundle.py +0 -148
  43. howler_api-4.0.0.dev1084/howler/actions/add_to_case.py +0 -136
  44. howler_api-4.0.0.dev1084/howler/api/v1/__init__.py +0 -44
  45. howler_api-4.0.0.dev1084/howler/api/v1/utils/etag.py +0 -106
  46. howler_api-4.0.0.dev1084/howler/api/v2/__init__.py +0 -44
  47. howler_api-4.0.0.dev1084/howler/api/v2/case.py +0 -437
  48. howler_api-4.0.0.dev1084/howler/api/v2/ingest.py +0 -369
  49. howler_api-4.0.0.dev1084/howler/api/v2/search.py +0 -340
  50. howler_api-4.0.0.dev1084/howler/cronjobs/correlation.py +0 -36
  51. howler_api-4.0.0.dev1084/howler/healthz.py +0 -47
  52. howler_api-4.0.0.dev1084/howler/odm/constants.py +0 -20
  53. howler_api-4.0.0.dev1084/howler/odm/mixins.py +0 -97
  54. howler_api-4.0.0.dev1084/howler/odm/models/case.py +0 -218
  55. howler_api-4.0.0.dev1084/howler/odm/models/event.py +0 -124
  56. howler_api-4.0.0.dev1084/howler/odm/models/hit.py +0 -31
  57. howler_api-4.0.0.dev1084/howler/remote/datatypes/__init__.py +0 -158
  58. howler_api-4.0.0.dev1084/howler/services/bundle_compat_service.py +0 -281
  59. howler_api-4.0.0.dev1084/howler/services/case_service.py +0 -944
  60. howler_api-4.0.0.dev1084/howler/services/comms_service.py +0 -134
  61. howler_api-4.0.0.dev1084/howler/services/correlation_service.py +0 -168
  62. howler_api-4.0.0.dev1084/howler/services/event_service.py +0 -140
  63. howler_api-4.0.0.dev1084/howler/services/search_service.py +0 -229
  64. howler_api-4.0.0.dev1084/howler/services/viewer_service.py +0 -43
  65. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/README.md +0 -0
  66. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/__init__.py +0 -0
  67. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/actions/__init__.py +0 -0
  68. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/actions/add_label.py +0 -0
  69. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/actions/change_field.py +0 -0
  70. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/actions/example_plugin.py +0 -0
  71. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/actions/prioritization.py +0 -0
  72. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/actions/promote.py +0 -0
  73. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/actions/remove_label.py +0 -0
  74. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/api/base.py +0 -0
  75. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/api/v1/auth.py +0 -0
  76. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/api/v1/clue.py +0 -0
  77. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/api/v1/configs.py +0 -0
  78. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/api/v1/dossier.py +0 -0
  79. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/api/v1/help.py +0 -0
  80. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/api/v1/notebook.py +0 -0
  81. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/api/v1/overview.py +0 -0
  82. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/api/v1/search.py +0 -0
  83. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/api/v1/template.py +0 -0
  84. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/api/v1/utils/__init__.py +0 -0
  85. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/api/v1/utils/params.py +0 -0
  86. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/api/v1/view.py +0 -0
  87. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/common/README.md +0 -0
  88. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/common/__init__.py +0 -0
  89. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/common/classification.py +0 -0
  90. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/common/classification.yml +0 -0
  91. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/common/exceptions.py +0 -0
  92. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/common/logging/__init__.py +0 -0
  93. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/common/logging/audit.py +0 -0
  94. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/common/logging/format.py +0 -0
  95. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/common/net.py +0 -0
  96. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/common/net_static.py +0 -0
  97. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/common/random_user.py +0 -0
  98. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/common/swagger.py +0 -0
  99. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/config.py +0 -0
  100. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/cronjobs/__init__.py +0 -0
  101. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/cronjobs/action_queue_worker.py +0 -0
  102. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/cronjobs/retention.py +0 -0
  103. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/cronjobs/view_cleanup.py +0 -0
  104. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/datastore/README.md +0 -0
  105. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/datastore/__init__.py +0 -0
  106. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/datastore/bulk.py +0 -0
  107. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/datastore/constants.py +0 -0
  108. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/datastore/exceptions.py +0 -0
  109. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/datastore/migrations/fix_process.py +0 -0
  110. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/datastore/operations.py +0 -0
  111. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/datastore/schemas.py +0 -0
  112. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/datastore/store.py +0 -0
  113. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/datastore/support/__init__.py +0 -0
  114. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/datastore/support/build.py +0 -0
  115. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/datastore/support/schemas.py +0 -0
  116. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/error.py +0 -0
  117. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/external/README.md +0 -0
  118. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/external/__init__.py +0 -0
  119. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/external/generate_mitre.py +0 -0
  120. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/external/generate_sigma_rules.py +0 -0
  121. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/external/generate_tlds.py +0 -0
  122. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/external/reindex_data.py +0 -0
  123. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/external/wipe_databases.py +0 -0
  124. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/gunicorn_config.py +0 -0
  125. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/helper/__init__.py +0 -0
  126. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/helper/azure.py +0 -0
  127. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/helper/oauth.py +0 -0
  128. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/helper/workflow.py +0 -0
  129. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/helper/ws.py +0 -0
  130. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/README.md +0 -0
  131. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/__init__.py +0 -0
  132. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/base.py +0 -0
  133. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/charter.txt +0 -0
  134. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/howler_enum.py +0 -0
  135. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/__init__.py +0 -0
  136. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/action.py +0 -0
  137. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/analytic.py +0 -0
  138. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/assemblyline.py +0 -0
  139. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/aws.py +0 -0
  140. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/azure.py +0 -0
  141. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/cbs.py +0 -0
  142. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/clue.py +0 -0
  143. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/dossier.py +0 -0
  144. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/ecs/__init__.py +0 -0
  145. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/ecs/agent.py +0 -0
  146. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/ecs/autonomous_system.py +0 -0
  147. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/ecs/client.py +0 -0
  148. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/ecs/cloud.py +0 -0
  149. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/ecs/code_signature.py +0 -0
  150. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/ecs/container.py +0 -0
  151. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/ecs/dns.py +0 -0
  152. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/ecs/egress.py +0 -0
  153. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/ecs/elf.py +0 -0
  154. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/ecs/email.py +0 -0
  155. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/ecs/error.py +0 -0
  156. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/ecs/faas.py +0 -0
  157. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/ecs/file.py +0 -0
  158. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/ecs/geo.py +0 -0
  159. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/ecs/group.py +0 -0
  160. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/ecs/hash.py +0 -0
  161. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/ecs/host.py +0 -0
  162. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/ecs/http.py +0 -0
  163. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/ecs/ingress.py +0 -0
  164. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/ecs/interface.py +0 -0
  165. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/ecs/network.py +0 -0
  166. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/ecs/observer.py +0 -0
  167. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/ecs/organization.py +0 -0
  168. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/ecs/os.py +0 -0
  169. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/ecs/pe.py +0 -0
  170. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/ecs/process.py +0 -0
  171. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/ecs/registry.py +0 -0
  172. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/ecs/related.py +0 -0
  173. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/ecs/rule.py +0 -0
  174. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/ecs/server.py +0 -0
  175. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/ecs/threat.py +0 -0
  176. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/ecs/tls.py +0 -0
  177. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/ecs/url.py +0 -0
  178. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/ecs/user.py +0 -0
  179. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/ecs/user_agent.py +0 -0
  180. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/ecs/vulnerability.py +0 -0
  181. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/gcp.py +0 -0
  182. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/lead.py +0 -0
  183. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/localized_label.py +0 -0
  184. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/overview.py +0 -0
  185. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/pivot.py +0 -0
  186. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/template.py +0 -0
  187. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/models/user.py +0 -0
  188. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/odm/randomizer.py +0 -0
  189. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/patched.py +0 -0
  190. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/plugins/__init__.py +0 -0
  191. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/plugins/config.py +0 -0
  192. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/remote/__init__.py +0 -0
  193. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/remote/datatypes/README.md +0 -0
  194. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/remote/datatypes/events.py +0 -0
  195. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/remote/datatypes/lock.py +0 -0
  196. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/remote/datatypes/queues/__init__.py +0 -0
  197. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/remote/datatypes/queues/multi.py +0 -0
  198. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/remote/datatypes/queues/named.py +0 -0
  199. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/remote/datatypes/queues/priority.py +0 -0
  200. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/remote/datatypes/set.py +0 -0
  201. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/security/__init__.py +0 -0
  202. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/security/socket.py +0 -0
  203. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/security/utils.py +0 -0
  204. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/services/__init__.py +0 -0
  205. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/services/action_service.py +0 -0
  206. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/services/analytic_service.py +0 -0
  207. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/services/auth_service.py +0 -0
  208. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/services/dossier_service.py +0 -0
  209. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/services/jwt_service.py +0 -0
  210. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/services/lucene_service.py +0 -0
  211. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/services/notebook_service.py +0 -0
  212. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/services/overview_service.py +0 -0
  213. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/services/template_service.py +0 -0
  214. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/services/user_service.py +0 -0
  215. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/telemetry.py +0 -0
  216. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/utils/__init__.py +0 -0
  217. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/utils/annotations.py +0 -0
  218. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/utils/chunk.py +0 -0
  219. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/utils/compat.py +0 -0
  220. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/utils/constants.py +0 -0
  221. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/utils/dict_utils.py +0 -0
  222. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/utils/isotime.py +0 -0
  223. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/utils/list_utils.py +0 -0
  224. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/utils/lucene.py +0 -0
  225. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/utils/path.py +0 -0
  226. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/howler/utils/str_utils.py +0 -0
  227. {howler_api-4.0.0.dev1084 → howler_api-4.0.0.dev1087}/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.dev1084
3
+ Version: 4.0.0.dev1087
4
4
  Summary: Howler - API server
5
5
  License: MIT
6
6
  Keywords: howler,alerting,gc,canada,cse-cst,cse,cst,cyber,cccs
@@ -29,7 +29,7 @@ Requires-Dist: elasticsearch (==8.19.3)
29
29
  Requires-Dist: flasgger (>=0.9.7.1,<0.10.0.0)
30
30
  Requires-Dist: flask (==3.1.3)
31
31
  Requires-Dist: flask-caching (==2.4.0)
32
- Requires-Dist: gevent (>=25.9.1,<26.0.0)
32
+ Requires-Dist: gevent (==23.9.1)
33
33
  Requires-Dist: gunicorn (==23.0.0)
34
34
  Requires-Dist: luqum (>=1.0.0,<2.0.0)
35
35
  Requires-Dist: mergedeep (>=1.3.4,<2.0.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,171 @@
1
+ from typing import Optional
2
+
3
+ from howler.actions import check_hit_limit
4
+ from howler.common.loader import datastore
5
+ from howler.datastore.operations import OdmHelper
6
+ from howler.odm.models.action import VALID_TRIGGERS
7
+ from howler.odm.models.hit import Hit
8
+ from howler.odm.models.user import User
9
+ from howler.services import hit_service
10
+ from howler.utils.str_utils import sanitize_lucene_query
11
+
12
+ hit_helper = OdmHelper(Hit)
13
+
14
+ OPERATION_ID = "add_to_bundle"
15
+ MAX_HITS_BASIC = 10
16
+ MAX_HITS_ADVANCED = 1000
17
+ SKIP_CENTRAL_LIMIT = True # This operation transforms the query, handles limit check locally
18
+
19
+
20
+ def execute(query: str, bundle_id: Optional[str] = None, user: Optional[User] = None, **kwargs):
21
+ """Add a set of hits matching the query to the specified bundle.
22
+
23
+ Args:
24
+ query (str): The query containing the matching hits
25
+ bundle_id (str): The `howler.id` of the bundle to add the hits to.
26
+ """
27
+ report = []
28
+
29
+ if not bundle_id:
30
+ return [
31
+ {
32
+ "query": query,
33
+ "outcome": "error",
34
+ "title": "Invalid Bundle ID",
35
+ "message": "Bundle ID cannot be empty.",
36
+ }
37
+ ]
38
+
39
+ try:
40
+ bundle_hit = hit_service.get_hit(bundle_id, as_odm=True)
41
+ if not bundle_hit or not bundle_hit.howler.is_bundle:
42
+ report.append(
43
+ {
44
+ "query": query,
45
+ "outcome": "error",
46
+ "title": "Invalid Bundle",
47
+ "message": f"Either a hit with ID {bundle_id} does not exist, or it is not a bundle.",
48
+ }
49
+ )
50
+ return report
51
+
52
+ ds = datastore()
53
+
54
+ skipped_hits_bundles = ds.hit.search(
55
+ f"({query}) AND howler.is_bundle:true",
56
+ fl="howler.id",
57
+ )["items"]
58
+
59
+ if len(skipped_hits_bundles) > 0:
60
+ report.append(
61
+ {
62
+ "query": f"({query}) AND howler.is_bundle:true",
63
+ "outcome": "skipped",
64
+ "title": "Skipped Bundles",
65
+ "message": "Bundles cannot be added to a bundle.",
66
+ }
67
+ )
68
+
69
+ skipped_hits_already_added = ds.hit.search(
70
+ f"({query}) AND (howler.bundles:{sanitize_lucene_query(bundle_id)})",
71
+ fl="howler.id",
72
+ )["items"]
73
+
74
+ if len(skipped_hits_already_added) > 0:
75
+ report.append(
76
+ {
77
+ "query": f"({query}) AND (howler.bundles:{sanitize_lucene_query(bundle_id)})",
78
+ "outcome": "skipped",
79
+ "title": "Skipped Hits",
80
+ "message": "These hits have already been added to the specified bundle.",
81
+ }
82
+ )
83
+
84
+ safe_query = f"({query}) AND (-howler.bundles:({sanitize_lucene_query(bundle_id)}) AND howler.is_bundle:false)"
85
+
86
+ # Check hit limit against the effective query (not raw query)
87
+ if user:
88
+ limit_error = check_hit_limit(safe_query, user, MAX_HITS_BASIC, MAX_HITS_ADVANCED)
89
+ if limit_error:
90
+ return [limit_error]
91
+
92
+ matching_hits = ds.hit.search(safe_query, rows=MAX_HITS_ADVANCED, fl="howler.id")["items"]
93
+ if len(matching_hits) < 1:
94
+ report.append(
95
+ {
96
+ "query": safe_query,
97
+ "outcome": "skipped",
98
+ "title": "No Matching Hits",
99
+ "message": "There were no hits matching this query.",
100
+ }
101
+ )
102
+ return report
103
+
104
+ ds.hit.update_by_query(
105
+ safe_query,
106
+ [hit_helper.list_add("howler.bundles", sanitize_lucene_query(bundle_id), if_missing=True)],
107
+ )
108
+
109
+ operations = [
110
+ hit_helper.list_add(
111
+ "howler.hits",
112
+ hit["howler"]["id"],
113
+ if_missing=True,
114
+ )
115
+ for hit in matching_hits
116
+ ]
117
+
118
+ operations.append(hit_helper.update("howler.bundle_size", len(operations)))
119
+ hit_service.update_hit(
120
+ bundle_id,
121
+ operations,
122
+ )
123
+ bundle_hit = hit_service.get_hit(bundle_id, as_odm=True)
124
+ report.append(
125
+ {
126
+ "query": safe_query.replace("-howler.bundles", "howler.bundles"),
127
+ "outcome": "success",
128
+ "title": "Executed Successfully",
129
+ "message": "The specified bundle has had all matching hits added.",
130
+ }
131
+ )
132
+ except Exception as e:
133
+ report.append(
134
+ {
135
+ "query": query,
136
+ "outcome": "error",
137
+ "title": "Failed to Execute",
138
+ "message": f"Unknown exception occurred: {str(e)}",
139
+ }
140
+ )
141
+
142
+ return report
143
+
144
+
145
+ def specification():
146
+ """Specify various properties of the action, such as title, descriptions, permissions and input steps."""
147
+ return {
148
+ "id": OPERATION_ID,
149
+ "title": "Add to Bundle",
150
+ "priority": 6,
151
+ "i18nKey": f"operations.{OPERATION_ID}",
152
+ "description": {
153
+ "short": "Add a set of hits to a bundle",
154
+ "long": execute.__doc__,
155
+ },
156
+ "roles": ["automation_basic", "actionrunner_basic"],
157
+ "steps": [
158
+ {
159
+ "args": {"bundle_id": []},
160
+ "options": {},
161
+ "validation": {
162
+ "warn": {"query": "howler.bundles:($bundle_id) OR howler.is_bundle:true"},
163
+ "error": {
164
+ "query": "howler.id:$bundle_id AND howler.is_bundle:false",
165
+ "message": "The bundle id given must be a bundle.",
166
+ },
167
+ },
168
+ }
169
+ ],
170
+ "triggers": VALID_TRIGGERS,
171
+ }
@@ -9,7 +9,7 @@ from howler.odm.models.howler_data import (
9
9
  Assessment,
10
10
  AssessmentEscalationMap,
11
11
  Escalation,
12
- Status,
12
+ HitStatus,
13
13
  )
14
14
  from howler.odm.models.user import User
15
15
  from howler.utils.str_utils import sanitize_lucene_query
@@ -99,7 +99,7 @@ def execute(
99
99
  "howler.assignment",
100
100
  user.get("uname", "automation") if user else "automation",
101
101
  ),
102
- odm_helper.update("howler.status", Status.RESOLVED),
102
+ odm_helper.update("howler.status", HitStatus.RESOLVED),
103
103
  ],
104
104
  )
105
105
 
@@ -1,27 +1,29 @@
1
- """Deprecated remove_from_bundle action — delegates to case_service for item removal."""
2
-
3
1
  from typing import Optional
4
2
 
5
3
  from howler.actions import check_hit_limit
6
- from howler.common.exceptions import NotFoundException
4
+ from howler.common.exceptions import HowlerException
7
5
  from howler.common.loader import datastore
6
+ from howler.datastore.operations import OdmHelper
8
7
  from howler.odm.models.action import VALID_TRIGGERS
8
+ from howler.odm.models.hit import Hit
9
9
  from howler.odm.models.user import User
10
- from howler.services import bundle_compat_service, case_service
10
+ from howler.services import hit_service
11
11
  from howler.utils.str_utils import sanitize_lucene_query
12
12
 
13
+ hit_helper = OdmHelper(Hit)
14
+
13
15
  OPERATION_ID = "remove_from_bundle"
14
16
  MAX_HITS_BASIC = 10
15
17
  MAX_HITS_ADVANCED = 1000
16
18
  SKIP_CENTRAL_LIMIT = True # This operation transforms the query, handles limit check locally
17
19
 
18
20
 
19
- def execute(query: str, bundle_id: Optional[str] = None, user: Optional[User] = None, **kwargs): # noqa: C901
20
- """Remove a set of hits matching the query from the specified bundle (deprecated — uses cases).
21
+ def execute(query: str, bundle_id: Optional[str] = None, user: Optional[User] = None, **kwargs):
22
+ """Remove a set of hits matching the query from the specified bundle.
21
23
 
22
24
  Args:
23
25
  query (str): The query containing the matching hits
24
- bundle_id (str): The ``howler.id`` of the bundle to remove the hits from.
26
+ bundle_id (str): The `howler.id` of the bundle to remove the hits from.
25
27
  """
26
28
  report = []
27
29
 
@@ -36,78 +38,67 @@ def execute(query: str, bundle_id: Optional[str] = None, user: Optional[User] =
36
38
  ]
37
39
 
38
40
  try:
39
- case_id = bundle_compat_service.find_case_for_bundle(bundle_id)
40
- if case_id is None:
41
+ bundle_hit = hit_service.get_hit(bundle_id, as_odm=True)
42
+ if not bundle_hit or not bundle_hit.howler.is_bundle:
41
43
  report.append(
42
44
  {
43
45
  "query": query,
44
46
  "outcome": "error",
45
47
  "title": "Invalid Bundle",
46
- "message": f"Either a hit with ID {bundle_id} does not exist, or it has no associated case.",
48
+ "message": f"Either a hit with ID {bundle_id} does not exist, or it is not a bundle.",
47
49
  }
48
50
  )
49
51
  return report
50
52
 
51
53
  ds = datastore()
52
54
 
53
- # Check hit limit against the query before searching
54
- if user:
55
- limit_error = check_hit_limit(query, user, MAX_HITS_BASIC, MAX_HITS_ADVANCED)
56
- if limit_error:
57
- return [limit_error]
58
-
59
- matching_hits = ds.hit.search(query, rows=MAX_HITS_ADVANCED)["items"]
55
+ skipped_hits = ds.hit.search(
56
+ f"({query}) AND -howler.bundles:{sanitize_lucene_query(bundle_id)}",
57
+ fl="howler.id",
58
+ )["items"]
60
59
 
61
- if not matching_hits:
60
+ if len(skipped_hits) > 0:
62
61
  report.append(
63
62
  {
64
- "query": query,
63
+ "query": f"howler.id:({' OR '.join(h.howler.id for h in skipped_hits)})",
65
64
  "outcome": "skipped",
66
- "title": "No Matching Hits",
67
- "message": "There were no hits matching this query.",
68
- }
69
- )
70
- return report
71
-
72
- # Get the case to check which hits are actually in it
73
- case = ds.case.get(case_id)
74
- if case is None:
75
- report.append(
76
- {
77
- "query": query,
78
- "outcome": "error",
79
- "title": "Case Not Found",
80
- "message": f"Associated case {case_id} no longer exists.",
65
+ "title": "Skipped Hit not in Bundle",
66
+ "message": "These hits already are not in the bundle.",
81
67
  }
82
68
  )
83
- return report
84
69
 
85
- case_item_values = {item.value for item in case.items}
86
- values_to_remove = [h.howler.id for h in matching_hits if h.howler.id in case_item_values]
87
- skipped_ids = [h.howler.id for h in matching_hits if h.howler.id not in case_item_values]
70
+ safe_query = f"{query} AND (howler.bundles:{sanitize_lucene_query(bundle_id)})"
88
71
 
89
- if skipped_ids:
90
- report.append(
91
- {
92
- "query": f"howler.id:({' OR '.join(sanitize_lucene_query(h) for h in skipped_ids)})",
93
- "outcome": "skipped",
94
- "title": "Skipped Hits Not in Bundle",
95
- "message": "These hits are not in the bundle.",
96
- }
97
- )
72
+ # Check hit limit against the effective query (not raw query)
73
+ if user:
74
+ limit_error = check_hit_limit(safe_query, user, MAX_HITS_BASIC, MAX_HITS_ADVANCED)
75
+ if limit_error:
76
+ return [limit_error]
98
77
 
99
- if not values_to_remove:
78
+ matching_hits = ds.hit.search(safe_query, rows=MAX_HITS_ADVANCED, fl="howler.id")["items"]
79
+ if len(matching_hits) < 1:
100
80
  report.append(
101
81
  {
102
- "query": query,
82
+ "query": safe_query,
103
83
  "outcome": "skipped",
104
84
  "title": "No Matching Hits",
105
- "message": "None of the matching hits were found in the bundle.",
85
+ "message": "There were no hits matching this query.",
106
86
  }
107
87
  )
108
88
  return report
109
89
 
110
- case_service.remove_case_items(case_id, values_to_remove)
90
+ ds.hit.update_by_query(
91
+ safe_query,
92
+ [hit_helper.list_remove("howler.bundles", bundle_id)],
93
+ )
94
+
95
+ hit_service.update_hit(
96
+ bundle_id,
97
+ [hit_helper.list_remove("howler.hits", h["howler"]["id"]) for h in matching_hits],
98
+ )
99
+
100
+ if len(ds.hit.get(bundle_id).howler.hits) < 1:
101
+ hit_service.update_hit(bundle_id, [hit_helper.update("howler.is_bundle", False)])
111
102
 
112
103
  report.append(
113
104
  {
@@ -117,17 +108,7 @@ def execute(query: str, bundle_id: Optional[str] = None, user: Optional[User] =
117
108
  "message": f"Matching hits removed from bundle with id {bundle_id}",
118
109
  }
119
110
  )
120
-
121
- except NotFoundException as e:
122
- report.append(
123
- {
124
- "query": query,
125
- "outcome": "error",
126
- "title": "Failed to Execute",
127
- "message": str(e),
128
- }
129
- )
130
- except Exception as e:
111
+ except HowlerException as e:
131
112
  report.append(
132
113
  {
133
114
  "query": query,
@@ -144,11 +125,11 @@ def specification():
144
125
  """Specify various properties of the action, such as title, descriptions, permissions and input steps."""
145
126
  return {
146
127
  "id": OPERATION_ID,
147
- "title": "Remove from Bundle (Deprecated)",
128
+ "title": "Remove from Bundle",
148
129
  "priority": 5,
149
130
  "i18nKey": f"operations.{OPERATION_ID}",
150
131
  "description": {
151
- "short": "Remove a set of hits from a bundle (deprecated — uses cases)",
132
+ "short": "Remove a set of hits from a bundle",
152
133
  "long": execute.__doc__,
153
134
  },
154
135
  "roles": ["automation_basic", "actionrunner_basic"],
@@ -156,6 +137,7 @@ def specification():
156
137
  {
157
138
  "args": {"bundle_id": []},
158
139
  "options": {},
140
+ "validation": {"error": {"query": "-howler.bundles:$bundle_id"}},
159
141
  }
160
142
  ],
161
143
  "triggers": VALID_TRIGGERS,
@@ -9,12 +9,12 @@ from howler.helper.workflow import Workflow, WorkflowException
9
9
  from howler.odm.models.action import VALID_TRIGGERS
10
10
  from howler.odm.models.howler_data import (
11
11
  Assessment,
12
+ HitStatus,
12
13
  HitStatusTransition,
13
- Status,
14
14
  Vote,
15
15
  )
16
16
  from howler.odm.models.user import User
17
- from howler.services import comms_service, hit_service
17
+ from howler.services import event_service, hit_service
18
18
  from howler.utils.list_utils import flatten_list
19
19
 
20
20
  OPERATION_ID = "transition"
@@ -147,7 +147,7 @@ def execute(
147
147
  if total_processed % 10 == 0:
148
148
  log.debug("Transition executed on %s hits", total_processed)
149
149
  if request_id is not None:
150
- comms_service.emit(
150
+ event_service.emit(
151
151
  "automation",
152
152
  {
153
153
  "request_id": request_id,
@@ -193,13 +193,15 @@ def specification():
193
193
  "steps": [
194
194
  {
195
195
  "args": {"status": []},
196
- "options": {"status": Status.list()},
196
+ "options": {"status": HitStatus.list()},
197
197
  "validation": {"error": {"query": "-howler.status:$status"}},
198
198
  },
199
199
  {
200
200
  "args": {"transition": []},
201
201
  "options": {
202
- "transition": {f"status:{status}": hit_service.get_transitions(status) for status in Status.list()},
202
+ "transition": {
203
+ f"status:{status}": hit_service.get_transitions(status) for status in HitStatus.list()
204
+ },
203
205
  },
204
206
  },
205
207
  {
@@ -25,8 +25,7 @@ logger = get_logger(__file__)
25
25
 
26
26
  def make_subapi_blueprint(name, api_version=1):
27
27
  """Create a flask Blueprint for a subapi in a standard way."""
28
- full_name = f"v{api_version}_{name}"
29
- return Blueprint(full_name, full_name, url_prefix="/".join([API_PREFIX, f"v{api_version}", name]))
28
+ return Blueprint(name, name, url_prefix="/".join([API_PREFIX, f"v{api_version}", name]))
30
29
 
31
30
 
32
31
  def _make_api_response(
@@ -6,12 +6,12 @@ from typing import Any
6
6
  from flask import Blueprint, request
7
7
  from opentelemetry import trace
8
8
 
9
- import howler.services.comms_service as comms_service
10
- import howler.services.viewer_service as viewer_service
9
+ import howler.services.event_service as event_service
11
10
  from howler.api import ok, unauthorized
12
11
  from howler.common.logging import get_logger
12
+ from howler.datastore.operations import OdmHelper
13
13
  from howler.helper.ws import ConnectionClosed, Server
14
- from howler.security import api_login
14
+ from howler.odm.models.hit import Hit
15
15
  from howler.security.socket import websocket_auth, ws_response
16
16
  from howler.utils.socket_utils import check_action
17
17
 
@@ -24,16 +24,13 @@ socket_api._doc = "Endpoints concerning websocket connectivity between the clien
24
24
  logger = get_logger(__file__)
25
25
  tracer = trace.get_tracer(__name__)
26
26
 
27
+ hit_helper = OdmHelper(Hit)
28
+
27
29
 
28
30
  @tracer.start_as_current_span(f"{__name__}.emit")
29
31
  @socket_api.route("/emit/<event>", methods=["POST"])
30
32
  def emit(event: str):
31
- """Emit an event to all listening websockets.
32
-
33
- .. deprecated::
34
- This endpoint is deprecated. Events are now propagated via Redis pubsub
35
- and no longer require a dedicated websocket pod.
36
- """
33
+ """Emit an event to all listening websockets"""
37
34
  if "Authorization" not in request.headers:
38
35
  return unauthorized(err="Missing authorization header")
39
36
 
@@ -47,30 +44,15 @@ def emit(event: str):
47
44
 
48
45
  return unauthorized(err="Invalid auth data")
49
46
 
50
- comms_service.emit(event, request.json)
47
+ event_service.emit(event, request.json)
51
48
 
52
49
  return ok()
53
50
 
54
51
 
55
- @tracer.start_as_current_span(f"{__name__}.get_viewers")
56
- @socket_api.route("/viewers/<entity_id>", methods=["GET"])
57
- @api_login(audit=False, required_priv=["R"])
58
- def get_viewers(entity_id: str, **kwargs):
59
- """Get the list of users currently viewing the specified entity
60
-
61
- Variables:
62
- entity_id => The ID of the entity to get viewers for
63
-
64
- Result Example:
65
- ["user1", "user2"]
66
- """
67
- return ok(viewer_service.get_viewers(entity_id))
68
-
69
-
70
52
  @tracer.start_as_current_span(f"{__name__}.connect")
71
53
  @socket_api.route("/connect", websocket=True) # type: ignore
72
54
  @websocket_auth(required_priv=["R"])
73
- def connect(ws: Server, *args: Any, ws_id: str, **kwargs): # noqa: C901
55
+ def connect(ws: Server, *args: Any, ws_id: str, **kwargs):
74
56
  """Connect to the server to monitor for updates via websocket
75
57
 
76
58
  Variables:
@@ -96,20 +78,10 @@ def connect(ws: Server, *args: Any, ws_id: str, **kwargs): # noqa: C901
96
78
  logger.debug("Sending action: %s", data)
97
79
  ws.send(ws_response("action", data))
98
80
 
99
- def send_case(data: dict[str, Any]):
100
- logger.debug("Sending case update: %s", data.get("case", {}).get("case_id", "unknown"))
101
- ws.send(ws_response("cases", data))
102
-
103
- def send_viewers_update(data: dict[str, Any]):
104
- logger.debug("Sending viewers update: %s", data.get("id", "unknown"))
105
- ws.send(ws_response("viewers_update", data))
106
-
107
81
  try:
108
- comms_service.on("hits", send_hit)
109
- comms_service.on("broadcast", send_broadcast)
110
- comms_service.on("action", send_action)
111
- comms_service.on("cases", send_case)
112
- comms_service.on("viewers_update", send_viewers_update)
82
+ event_service.on("hits", send_hit)
83
+ event_service.on("broadcast", send_broadcast)
84
+ event_service.on("action", send_action)
113
85
  while ws.connected:
114
86
  data = ws.receive(10)
115
87
  if data:
@@ -138,11 +110,9 @@ def connect(ws: Server, *args: Any, ws_id: str, **kwargs): # noqa: C901
138
110
  else:
139
111
  logger.exception("Exception on connect.")
140
112
  finally:
141
- comms_service.off("hits", send_hit)
142
- comms_service.off("broadcast", send_broadcast)
143
- comms_service.off("action", send_action)
144
- comms_service.off("cases", send_case)
145
- comms_service.off("viewers_update", send_viewers_update)
113
+ event_service.off("hits", send_hit)
114
+ event_service.off("broadcast", send_broadcast)
115
+ event_service.off("action", send_action)
146
116
 
147
117
  for id, action, broadcast in outstanding_actions:
148
118
  outstanding_actions = check_action(id, action, broadcast, outstanding_actions=outstanding_actions, **kwargs)