howler-api 3.2.0.dev595__tar.gz → 3.2.0.dev599__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 (211) hide show
  1. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/PKG-INFO +2 -2
  2. howler_api-3.2.0.dev599/howler/actions/add_to_bundle.py +159 -0
  3. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/actions/demote.py +2 -2
  4. howler_api-3.2.0.dev599/howler/actions/remove_from_bundle.py +133 -0
  5. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/actions/transition.py +5 -3
  6. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/api/__init__.py +1 -2
  7. howler_api-3.2.0.dev595/howler/services/docs_service.py → howler_api-3.2.0.dev599/howler/api/v1/__init__.py +45 -37
  8. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/api/v1/action.py +2 -1
  9. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/api/v1/analytic.py +102 -17
  10. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/api/v1/hit.py +215 -11
  11. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/api/v1/tool.py +18 -1
  12. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/api/v1/user.py +1 -1
  13. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/app.py +0 -10
  14. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/common/loader.py +5 -4
  15. howler_api-3.2.0.dev599/howler/cronjobs/rules.py +274 -0
  16. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/datastore/collection.py +9 -8
  17. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/datastore/howler_store.py +14 -29
  18. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/datastore/store.py +1 -4
  19. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/datastore/types.py +6 -1
  20. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/helper/discover.py +7 -11
  21. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/helper/hit.py +4 -4
  22. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/helper/search.py +2 -12
  23. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/base.py +12 -40
  24. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/helper.py +5 -115
  25. howler_api-3.2.0.dev595/howler/odm/models/record.py → howler_api-3.2.0.dev599/howler/odm/models/hit.py +21 -1
  26. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/howler_data.py +31 -3
  27. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/random_data.py +45 -347
  28. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/remote/datatypes/__init__.py +2 -3
  29. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/remote/datatypes/queues/comms.py +1 -2
  30. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/services/config_service.py +3 -3
  31. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/services/hit_service.py +168 -90
  32. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/pyproject.toml +3 -3
  33. howler_api-3.2.0.dev595/howler/api/v1/__init__.py +0 -44
  34. howler_api-3.2.0.dev595/howler/api/v2/__init__.py +0 -44
  35. howler_api-3.2.0.dev595/howler/api/v2/case.py +0 -286
  36. howler_api-3.2.0.dev595/howler/api/v2/ingest.py +0 -327
  37. howler_api-3.2.0.dev595/howler/api/v2/search.py +0 -341
  38. howler_api-3.2.0.dev595/howler/odm/constants.py +0 -20
  39. howler_api-3.2.0.dev595/howler/odm/models/case.py +0 -179
  40. howler_api-3.2.0.dev595/howler/odm/models/hit.py +0 -29
  41. howler_api-3.2.0.dev595/howler/odm/models/observable.py +0 -129
  42. howler_api-3.2.0.dev595/howler/services/case_service.py +0 -558
  43. howler_api-3.2.0.dev595/howler/services/observable_service.py +0 -128
  44. howler_api-3.2.0.dev595/howler/services/search_service.py +0 -225
  45. howler_api-3.2.0.dev595/howler/utils/compat.py +0 -21
  46. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/README.md +0 -0
  47. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/__init__.py +0 -0
  48. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/actions/__init__.py +0 -0
  49. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/actions/add_label.py +0 -0
  50. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/actions/change_field.py +0 -0
  51. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/actions/example_plugin.py +0 -0
  52. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/actions/prioritization.py +0 -0
  53. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/actions/promote.py +0 -0
  54. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/actions/remove_label.py +0 -0
  55. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/api/base.py +0 -0
  56. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/api/socket.py +0 -0
  57. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/api/v1/auth.py +0 -0
  58. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/api/v1/clue.py +0 -0
  59. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/api/v1/configs.py +0 -0
  60. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/api/v1/dossier.py +0 -0
  61. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/api/v1/help.py +0 -0
  62. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/api/v1/notebook.py +0 -0
  63. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/api/v1/overview.py +0 -0
  64. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/api/v1/search.py +0 -0
  65. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/api/v1/template.py +0 -0
  66. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/api/v1/utils/__init__.py +0 -0
  67. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/api/v1/utils/etag.py +0 -0
  68. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/api/v1/view.py +0 -0
  69. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/common/README.md +0 -0
  70. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/common/__init__.py +0 -0
  71. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/common/classification.py +0 -0
  72. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/common/classification.yml +0 -0
  73. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/common/exceptions.py +0 -0
  74. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/common/logging/__init__.py +0 -0
  75. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/common/logging/audit.py +0 -0
  76. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/common/logging/format.py +0 -0
  77. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/common/net.py +0 -0
  78. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/common/net_static.py +0 -0
  79. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/common/random_user.py +0 -0
  80. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/common/swagger.py +0 -0
  81. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/config.py +0 -0
  82. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/cronjobs/__init__.py +0 -0
  83. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/cronjobs/retention.py +0 -0
  84. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/cronjobs/view_cleanup.py +0 -0
  85. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/datastore/README.md +0 -0
  86. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/datastore/__init__.py +0 -0
  87. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/datastore/bulk.py +0 -0
  88. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/datastore/constants.py +0 -0
  89. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/datastore/exceptions.py +0 -0
  90. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/datastore/migrations/fix_process.py +0 -0
  91. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/datastore/operations.py +0 -0
  92. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/datastore/schemas.py +0 -0
  93. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/datastore/support/__init__.py +0 -0
  94. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/datastore/support/build.py +0 -0
  95. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/datastore/support/schemas.py +0 -0
  96. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/error.py +0 -0
  97. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/external/__init__.py +0 -0
  98. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/external/generate_mitre.py +0 -0
  99. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/external/generate_sigma_rules.py +0 -0
  100. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/external/generate_tlds.py +0 -0
  101. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/external/reindex_data.py +0 -0
  102. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/external/wipe_databases.py +0 -0
  103. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/gunicorn_config.py +0 -0
  104. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/healthz.py +0 -0
  105. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/helper/__init__.py +0 -0
  106. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/helper/azure.py +0 -0
  107. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/helper/oauth.py +0 -0
  108. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/helper/workflow.py +0 -0
  109. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/helper/ws.py +0 -0
  110. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/README.md +0 -0
  111. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/__init__.py +0 -0
  112. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/charter.txt +0 -0
  113. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/howler_enum.py +0 -0
  114. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/__init__.py +0 -0
  115. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/action.py +0 -0
  116. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/analytic.py +0 -0
  117. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/assemblyline.py +0 -0
  118. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/aws.py +0 -0
  119. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/azure.py +0 -0
  120. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/cbs.py +0 -0
  121. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/clue.py +0 -0
  122. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/config.py +0 -0
  123. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/dossier.py +0 -0
  124. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/ecs/__init__.py +0 -0
  125. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/ecs/agent.py +0 -0
  126. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/ecs/autonomous_system.py +0 -0
  127. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/ecs/client.py +0 -0
  128. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/ecs/cloud.py +0 -0
  129. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/ecs/code_signature.py +0 -0
  130. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/ecs/container.py +0 -0
  131. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/ecs/dns.py +0 -0
  132. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/ecs/egress.py +0 -0
  133. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/ecs/elf.py +0 -0
  134. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/ecs/email.py +0 -0
  135. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/ecs/error.py +0 -0
  136. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/ecs/event.py +0 -0
  137. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/ecs/faas.py +0 -0
  138. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/ecs/file.py +0 -0
  139. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/ecs/geo.py +0 -0
  140. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/ecs/group.py +0 -0
  141. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/ecs/hash.py +0 -0
  142. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/ecs/host.py +0 -0
  143. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/ecs/http.py +0 -0
  144. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/ecs/ingress.py +0 -0
  145. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/ecs/interface.py +0 -0
  146. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/ecs/network.py +0 -0
  147. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/ecs/observer.py +0 -0
  148. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/ecs/organization.py +0 -0
  149. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/ecs/os.py +0 -0
  150. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/ecs/pe.py +0 -0
  151. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/ecs/process.py +0 -0
  152. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/ecs/registry.py +0 -0
  153. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/ecs/related.py +0 -0
  154. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/ecs/rule.py +0 -0
  155. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/ecs/server.py +0 -0
  156. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/ecs/threat.py +0 -0
  157. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/ecs/tls.py +0 -0
  158. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/ecs/url.py +0 -0
  159. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/ecs/user.py +0 -0
  160. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/ecs/user_agent.py +0 -0
  161. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/ecs/vulnerability.py +0 -0
  162. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/gcp.py +0 -0
  163. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/lead.py +0 -0
  164. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/localized_label.py +0 -0
  165. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/overview.py +0 -0
  166. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/pivot.py +0 -0
  167. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/template.py +0 -0
  168. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/user.py +0 -0
  169. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/models/view.py +0 -0
  170. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/odm/randomizer.py +0 -0
  171. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/patched.py +0 -0
  172. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/plugins/__init__.py +0 -0
  173. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/plugins/config.py +0 -0
  174. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/remote/__init__.py +0 -0
  175. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/remote/datatypes/README.md +0 -0
  176. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/remote/datatypes/counters.py +0 -0
  177. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/remote/datatypes/events.py +0 -0
  178. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/remote/datatypes/hash.py +0 -0
  179. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/remote/datatypes/lock.py +0 -0
  180. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/remote/datatypes/queues/__init__.py +0 -0
  181. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/remote/datatypes/queues/multi.py +0 -0
  182. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/remote/datatypes/queues/named.py +0 -0
  183. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/remote/datatypes/queues/priority.py +0 -0
  184. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/remote/datatypes/set.py +0 -0
  185. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/remote/datatypes/user_quota_tracker.py +0 -0
  186. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/security/__init__.py +0 -0
  187. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/security/socket.py +0 -0
  188. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/security/utils.py +0 -0
  189. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/services/__init__.py +0 -0
  190. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/services/action_service.py +0 -0
  191. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/services/analytic_service.py +0 -0
  192. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/services/auth_service.py +0 -0
  193. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/services/dossier_service.py +0 -0
  194. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/services/event_service.py +0 -0
  195. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/services/jwt_service.py +0 -0
  196. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/services/lucene_service.py +0 -0
  197. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/services/notebook_service.py +0 -0
  198. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/services/overview_service.py +0 -0
  199. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/services/template_service.py +0 -0
  200. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/services/user_service.py +0 -0
  201. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/utils/__init__.py +0 -0
  202. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/utils/annotations.py +0 -0
  203. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/utils/chunk.py +0 -0
  204. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/utils/dict_utils.py +0 -0
  205. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/utils/isotime.py +0 -0
  206. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/utils/list_utils.py +0 -0
  207. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/utils/lucene.py +0 -0
  208. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/utils/path.py +0 -0
  209. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/utils/socket_utils.py +0 -0
  210. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/utils/str_utils.py +0 -0
  211. {howler_api-3.2.0.dev595 → howler_api-3.2.0.dev599}/howler/utils/uid.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: howler-api
3
- Version: 3.2.0.dev595
3
+ Version: 3.2.0.dev599
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.3.1)
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)
@@ -0,0 +1,159 @@
1
+ from typing import Optional
2
+
3
+ from howler.common.loader import datastore
4
+ from howler.datastore.operations import OdmHelper
5
+ from howler.odm.models.action import VALID_TRIGGERS
6
+ from howler.odm.models.hit import Hit
7
+ from howler.services import hit_service
8
+ from howler.utils.str_utils import sanitize_lucene_query
9
+
10
+ hit_helper = OdmHelper(Hit)
11
+
12
+ OPERATION_ID = "add_to_bundle"
13
+
14
+
15
+ def execute(query: str, bundle_id: Optional[str] = None, **kwargs):
16
+ """Add a set of hits matching the query to the specified bundle.
17
+
18
+ Args:
19
+ query (str): The query containing the matching hits
20
+ bundle_id (str): The `howler.id` of the bundle to add the hits to.
21
+ """
22
+ report = []
23
+
24
+ if not bundle_id:
25
+ return [
26
+ {
27
+ "query": query,
28
+ "outcome": "error",
29
+ "title": "Invalid Bundle ID",
30
+ "message": "Bundle ID cannot be empty.",
31
+ }
32
+ ]
33
+
34
+ try:
35
+ bundle_hit = hit_service.get_hit(bundle_id, as_odm=True)
36
+ if not bundle_hit or not bundle_hit.howler.is_bundle:
37
+ report.append(
38
+ {
39
+ "query": query,
40
+ "outcome": "error",
41
+ "title": "Invalid Bundle",
42
+ "message": f"Either a hit with ID {bundle_id} does not exist, or it is not a bundle.",
43
+ }
44
+ )
45
+ return report
46
+
47
+ ds = datastore()
48
+
49
+ skipped_hits_bundles = ds.hit.search(
50
+ f"({query}) AND howler.is_bundle:true",
51
+ fl="howler.id",
52
+ )["items"]
53
+
54
+ if len(skipped_hits_bundles) > 0:
55
+ report.append(
56
+ {
57
+ "query": f"({query}) AND howler.is_bundle:true",
58
+ "outcome": "skipped",
59
+ "title": "Skipped Bundles",
60
+ "message": "Bundles cannot be added to a bundle.",
61
+ }
62
+ )
63
+
64
+ skipped_hits_already_added = ds.hit.search(
65
+ f"({query}) AND (howler.bundles:{sanitize_lucene_query(bundle_id)})",
66
+ fl="howler.id",
67
+ )["items"]
68
+
69
+ if len(skipped_hits_already_added) > 0:
70
+ report.append(
71
+ {
72
+ "query": f"({query}) AND (howler.bundles:{sanitize_lucene_query(bundle_id)})",
73
+ "outcome": "skipped",
74
+ "title": "Skipped Hits",
75
+ "message": "These hits have already been added to the specified bundle.",
76
+ }
77
+ )
78
+
79
+ safe_query = f"({query}) AND (-howler.bundles:({sanitize_lucene_query(bundle_id)}) AND howler.is_bundle:false)"
80
+ matching_hits = ds.hit.search(safe_query)["items"]
81
+ if len(matching_hits) < 1:
82
+ report.append(
83
+ {
84
+ "query": safe_query,
85
+ "outcome": "skipped",
86
+ "title": "No Matching Hits",
87
+ "message": "There were no hits matching this query.",
88
+ }
89
+ )
90
+ return report
91
+
92
+ ds.hit.update_by_query(
93
+ safe_query,
94
+ [hit_helper.list_add("howler.bundles", sanitize_lucene_query(bundle_id), if_missing=True)],
95
+ )
96
+
97
+ operations = [
98
+ hit_helper.list_add(
99
+ "howler.hits",
100
+ hit["howler"]["id"],
101
+ if_missing=True,
102
+ )
103
+ for hit in matching_hits
104
+ ]
105
+
106
+ operations.append(hit_helper.update("howler.bundle_size", len(operations)))
107
+ hit_service.update_hit(
108
+ bundle_id,
109
+ operations,
110
+ )
111
+ bundle_hit = hit_service.get_hit(bundle_id, as_odm=True)
112
+ report.append(
113
+ {
114
+ "query": safe_query.replace("-howler.bundles", "howler.bundles"),
115
+ "outcome": "success",
116
+ "title": "Executed Successfully",
117
+ "message": "The specified bundle has had all matching hits added.",
118
+ }
119
+ )
120
+ except Exception as e:
121
+ report.append(
122
+ {
123
+ "query": query,
124
+ "outcome": "error",
125
+ "title": "Failed to Execute",
126
+ "message": f"Unknown exception occurred: {str(e)}",
127
+ }
128
+ )
129
+
130
+ return report
131
+
132
+
133
+ def specification():
134
+ """Specify various properties of the action, such as title, descriptions, permissions and input steps."""
135
+ return {
136
+ "id": OPERATION_ID,
137
+ "title": "Add to Bundle",
138
+ "priority": 6,
139
+ "i18nKey": f"operations.{OPERATION_ID}",
140
+ "description": {
141
+ "short": "Add a set of hits to a bundle",
142
+ "long": execute.__doc__,
143
+ },
144
+ "roles": ["automation_basic"],
145
+ "steps": [
146
+ {
147
+ "args": {"bundle_id": []},
148
+ "options": {},
149
+ "validation": {
150
+ "warn": {"query": "howler.bundles:($bundle_id) OR howler.is_bundle:true"},
151
+ "error": {
152
+ "query": "howler.id:$bundle_id AND howler.is_bundle:false",
153
+ "message": "The bundle id given must be a bundle.",
154
+ },
155
+ },
156
+ }
157
+ ],
158
+ "triggers": VALID_TRIGGERS,
159
+ }
@@ -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
@@ -96,7 +96,7 @@ def execute(
96
96
  "howler.assignment",
97
97
  user.get("uname", "automation") if user else "automation",
98
98
  ),
99
- odm_helper.update("howler.status", Status.RESOLVED),
99
+ odm_helper.update("howler.status", HitStatus.RESOLVED),
100
100
  ],
101
101
  )
102
102
 
@@ -0,0 +1,133 @@
1
+ from typing import Optional
2
+
3
+ from howler.common.exceptions import HowlerException
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.services import hit_service
9
+ from howler.utils.str_utils import sanitize_lucene_query
10
+
11
+ hit_helper = OdmHelper(Hit)
12
+
13
+ OPERATION_ID = "remove_from_bundle"
14
+
15
+
16
+ def execute(query: str, bundle_id: Optional[str] = None, **kwargs):
17
+ """Remove a set of hits matching the query from the specified bundle.
18
+
19
+ Args:
20
+ query (str): The query containing the matching hits
21
+ bundle_id (str): The `howler.id` of the bundle to remove the hits from.
22
+ """
23
+ report = []
24
+
25
+ if not bundle_id:
26
+ return [
27
+ {
28
+ "query": query,
29
+ "outcome": "error",
30
+ "title": "Invalid Bundle ID",
31
+ "message": "Bundle ID cannot be empty.",
32
+ }
33
+ ]
34
+
35
+ try:
36
+ bundle_hit = hit_service.get_hit(bundle_id, as_odm=True)
37
+ if not bundle_hit or not bundle_hit.howler.is_bundle:
38
+ report.append(
39
+ {
40
+ "query": query,
41
+ "outcome": "error",
42
+ "title": "Invalid Bundle",
43
+ "message": f"Either a hit with ID {bundle_id} does not exist, or it is not a bundle.",
44
+ }
45
+ )
46
+ return report
47
+
48
+ ds = datastore()
49
+
50
+ skipped_hits = ds.hit.search(
51
+ f"({query}) AND -howler.bundles:{sanitize_lucene_query(bundle_id)}",
52
+ fl="howler.id",
53
+ )["items"]
54
+
55
+ if len(skipped_hits) > 0:
56
+ report.append(
57
+ {
58
+ "query": f"howler.id:({' OR '.join(h.howler.id for h in skipped_hits)})",
59
+ "outcome": "skipped",
60
+ "title": "Skipped Hit not in Bundle",
61
+ "message": "These hits already are not in the bundle.",
62
+ }
63
+ )
64
+
65
+ safe_query = f"{query} AND (howler.bundles:{bundle_id})"
66
+
67
+ matching_hits = ds.hit.search(safe_query)["items"]
68
+ if len(matching_hits) < 1:
69
+ report.append(
70
+ {
71
+ "query": safe_query,
72
+ "outcome": "skipped",
73
+ "title": "No Matching Hits",
74
+ "message": "There were no hits matching this query.",
75
+ }
76
+ )
77
+ return report
78
+
79
+ ds.hit.update_by_query(
80
+ safe_query,
81
+ [hit_helper.list_remove("howler.bundles", bundle_id)],
82
+ )
83
+
84
+ hit_service.update_hit(
85
+ bundle_id,
86
+ [hit_helper.list_remove("howler.hits", h["howler"]["id"]) for h in ds.hit.search(safe_query)["items"]],
87
+ )
88
+
89
+ if len(ds.hit.get(bundle_id).howler.hits) < 1:
90
+ hit_service.update_hit(bundle_id, [hit_helper.update("howler.is_bundle", False)])
91
+
92
+ report.append(
93
+ {
94
+ "query": query,
95
+ "outcome": "success",
96
+ "title": "Executed Successfully",
97
+ "message": f"Matching hits removed from bundle with id {bundle_id}",
98
+ }
99
+ )
100
+ except HowlerException as e:
101
+ report.append(
102
+ {
103
+ "query": query,
104
+ "outcome": "error",
105
+ "title": "Failed to Execute",
106
+ "message": f"Unknown exception occurred: {str(e)}",
107
+ }
108
+ )
109
+
110
+ return report
111
+
112
+
113
+ def specification():
114
+ """Specify various properties of the action, such as title, descriptions, permissions and input steps."""
115
+ return {
116
+ "id": OPERATION_ID,
117
+ "title": "Remove from Bundle",
118
+ "priority": 5,
119
+ "i18nKey": f"operations.{OPERATION_ID}",
120
+ "description": {
121
+ "short": "Remove a set of hits from a bundle",
122
+ "long": execute.__doc__,
123
+ },
124
+ "roles": ["automation_basic"],
125
+ "steps": [
126
+ {
127
+ "args": {"bundle_id": []},
128
+ "options": {},
129
+ "validation": {"error": {"query": "-howler.bundles:$bundle_id"}},
130
+ }
131
+ ],
132
+ "triggers": VALID_TRIGGERS,
133
+ }
@@ -8,8 +8,8 @@ from howler.helper.workflow import Workflow, WorkflowException
8
8
  from howler.odm.models.action import VALID_TRIGGERS
9
9
  from howler.odm.models.howler_data import (
10
10
  Assessment,
11
+ HitStatus,
11
12
  HitStatusTransition,
12
- Status,
13
13
  Vote,
14
14
  )
15
15
  from howler.odm.models.user import User
@@ -180,13 +180,15 @@ def specification():
180
180
  "steps": [
181
181
  {
182
182
  "args": {"status": []},
183
- "options": {"status": Status.list()},
183
+ "options": {"status": HitStatus.list()},
184
184
  "validation": {"error": {"query": "-howler.status:$status"}},
185
185
  },
186
186
  {
187
187
  "args": {"transition": []},
188
188
  "options": {
189
- "transition": {f"status:{status}": hit_service.get_transitions(status) for status in Status.list()},
189
+ "transition": {
190
+ f"status:{status}": hit_service.get_transitions(status) for status in HitStatus.list()
191
+ },
190
192
  },
191
193
  },
192
194
  {
@@ -24,8 +24,7 @@ logger = get_logger(__file__)
24
24
 
25
25
  def make_subapi_blueprint(name, api_version=1):
26
26
  """Create a flask Blueprint for a subapi in a standard way."""
27
- full_name = f"v{api_version}_{name}"
28
- return Blueprint(full_name, full_name, url_prefix="/".join([API_PREFIX, f"v{api_version}", name]))
27
+ return Blueprint(name, name, url_prefix="/".join([API_PREFIX, f"v{api_version}", name]))
29
28
 
30
29
 
31
30
  def _make_api_response(
@@ -1,39 +1,47 @@
1
1
  from textwrap import dedent
2
2
 
3
- from flask import current_app, request
4
-
5
-
6
- def build_route_docs(version: str, user_types: list[str]):
7
- """This function iterates through all registered Flask routes.
8
-
9
- It generates documentation for those accessible by the specified user types. It collects
10
- route metadata including permissions, methods, descriptions, and completion status.
11
-
12
- Args:
13
- user_types (list[str]): List of user types to filter API routes by.
14
- Only routes that grant access to at least one of these user types
15
- will be included in the output.
16
-
17
- Returns:
18
- dict: A dictionary containing:
19
- - 'apis' (list[dict]): List of API endpoint documentation, each dict includes:
20
- - 'protected' (bool): Whether the endpoint requires authentication.
21
- - 'required_type' (list[str]): User types allowed to access this endpoint.
22
- - 'name' (str): Human-readable name of the endpoint.
23
- - 'id' (str): Unique identifier for the endpoint.
24
- - 'function' (str): Full Python function path.
25
- - 'path' (str): URL path of the endpoint.
26
- - 'ui_only' (bool): Whether endpoint is UI-only.
27
- - 'methods' (list[str]): HTTP methods supported (excluding OPTIONS and HEAD).
28
- - 'description' (str): Docstring documentation or incomplete marker.
29
- - 'complete' (bool): Whether the endpoint is fully documented.
30
- - 'required_priv' (list[str]): Required privileges for the endpoint.
31
- - 'blueprints' (dict[str, str]): Map of blueprint names to their documentation strings.
32
-
33
- Note:
34
- Endpoints without documentation are marked with "[INCOMPLETE]" prefix
35
- in their description and complete field set to False.
3
+ from flask import Blueprint, current_app, request
4
+
5
+ from howler.api import ok
6
+ from howler.security import api_login
7
+
8
+ API_PREFIX = "/api/v1"
9
+ apiv1 = Blueprint("apiv1", __name__, url_prefix=API_PREFIX)
10
+ apiv1._doc = "Api Documentation Version 1" # type: ignore[attr-defined] # type: ignore
11
+
12
+
13
+ @apiv1.route("/")
14
+ @api_login(audit=False, required_priv=["R", "W"], required_type=["user", "admin"])
15
+ def get_api_documentation(**kwargs):
16
+ """Full API doc.
17
+
18
+ Loop through all registered API paths and display their documentation.
19
+ Returns a list of API definition.
20
+
21
+ Variables:
22
+ None
23
+
24
+ Arguments:
25
+ None
26
+
27
+ Result Example:
28
+ [
29
+ {
30
+ 'name': "Api Doc", # Name of the api
31
+ 'path': "/api/path/<variable>/", # API path
32
+ 'ui_only': false, # Is UI only API
33
+ 'methods': ["GET", "POST"], # Allowed HTTP methods
34
+ 'description': "API doc.", # API documentation
35
+ 'id': "api_doc", # Unique ID for the API
36
+ 'function': "apiv1.api_doc", # Function called in the code
37
+ 'protected': False, # Does the API require login?
38
+ 'required_type': ['user'], # Type of users allowed to use API
39
+ 'complete' : True # Is the API stable?
40
+ },
41
+ ]
36
42
  """
43
+ user_types = kwargs["user"]["type"]
44
+
37
45
  api_blueprints = {}
38
46
  api_list = []
39
47
  for rule in current_app.url_map.iter_rules():
@@ -50,7 +58,7 @@ def build_route_docs(version: str, user_types: list[str]):
50
58
  [x.capitalize() for x in rule.endpoint[rule.endpoint.rindex(".") + 1 :].split("_")]
51
59
  )
52
60
  blueprint = rule.endpoint[: rule.endpoint.rindex(".")]
53
- if blueprint == f"api{version}":
61
+ if blueprint == "apiv1":
54
62
  blueprint = "documentation"
55
63
 
56
64
  if blueprint not in api_blueprints:
@@ -66,7 +74,7 @@ def build_route_docs(version: str, user_types: list[str]):
66
74
  else:
67
75
  description = "[INCOMPLETE]\n\nTHIS API HAS NOT BEEN DOCUMENTED YET!"
68
76
 
69
- api_id = rule.endpoint.replace(f"api{version}.", "").replace(".", "_")
77
+ api_id = rule.endpoint.replace("apiv1.", "").replace(".", "_")
70
78
 
71
79
  api_list.append(
72
80
  {
@@ -74,7 +82,7 @@ def build_route_docs(version: str, user_types: list[str]):
74
82
  "required_type": sorted(required_type),
75
83
  "name": func_title,
76
84
  "id": api_id,
77
- "function": f"api.{version}.{rule.endpoint}",
85
+ "function": f"api.v1.{rule.endpoint}",
78
86
  "path": rule.rule,
79
87
  "ui_only": rule.rule.startswith("%sui/" % request.path),
80
88
  "methods": sorted(methods),
@@ -86,4 +94,4 @@ def build_route_docs(version: str, user_types: list[str]):
86
94
 
87
95
  break
88
96
 
89
- return {"apis": api_list, "blueprints": api_blueprints}
97
+ return ok({"apis": api_list, "blueprints": api_blueprints})
@@ -231,7 +231,8 @@ def execute_action(id: str, **kwargs) -> Response:
231
231
  if not isinstance(execute_req, dict):
232
232
  return bad_request(err="Incorrect data structure!")
233
233
 
234
- action = datastore().action.get(id)
234
+ action: Action = datastore().action.get(id)
235
+
235
236
  if not action:
236
237
  return not_found(err="The specified action does not exist")
237
238