howler-api 4.0.0.dev657__tar.gz → 4.0.0.dev664__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 (208) hide show
  1. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/PKG-INFO +1 -1
  2. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/api/v2/case.py +15 -13
  3. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/services/case_service.py +44 -18
  4. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/pyproject.toml +1 -1
  5. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/README.md +0 -0
  6. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/__init__.py +0 -0
  7. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/actions/__init__.py +0 -0
  8. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/actions/add_label.py +0 -0
  9. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/actions/change_field.py +0 -0
  10. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/actions/demote.py +0 -0
  11. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/actions/example_plugin.py +0 -0
  12. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/actions/prioritization.py +0 -0
  13. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/actions/promote.py +0 -0
  14. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/actions/remove_label.py +0 -0
  15. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/actions/transition.py +0 -0
  16. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/api/__init__.py +0 -0
  17. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/api/base.py +0 -0
  18. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/api/socket.py +0 -0
  19. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/api/v1/__init__.py +0 -0
  20. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/api/v1/action.py +0 -0
  21. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/api/v1/analytic.py +0 -0
  22. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/api/v1/auth.py +0 -0
  23. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/api/v1/clue.py +0 -0
  24. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/api/v1/configs.py +0 -0
  25. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/api/v1/dossier.py +0 -0
  26. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/api/v1/help.py +0 -0
  27. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/api/v1/hit.py +0 -0
  28. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/api/v1/notebook.py +0 -0
  29. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/api/v1/overview.py +0 -0
  30. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/api/v1/search.py +0 -0
  31. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/api/v1/template.py +0 -0
  32. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/api/v1/tool.py +0 -0
  33. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/api/v1/user.py +0 -0
  34. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/api/v1/utils/__init__.py +0 -0
  35. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/api/v1/utils/etag.py +0 -0
  36. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/api/v1/view.py +0 -0
  37. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/api/v2/__init__.py +0 -0
  38. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/api/v2/ingest.py +0 -0
  39. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/api/v2/search.py +0 -0
  40. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/app.py +0 -0
  41. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/common/README.md +0 -0
  42. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/common/__init__.py +0 -0
  43. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/common/classification.py +0 -0
  44. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/common/classification.yml +0 -0
  45. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/common/exceptions.py +0 -0
  46. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/common/loader.py +0 -0
  47. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/common/logging/__init__.py +0 -0
  48. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/common/logging/audit.py +0 -0
  49. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/common/logging/format.py +0 -0
  50. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/common/net.py +0 -0
  51. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/common/net_static.py +0 -0
  52. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/common/random_user.py +0 -0
  53. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/common/swagger.py +0 -0
  54. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/config.py +0 -0
  55. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/cronjobs/__init__.py +0 -0
  56. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/cronjobs/retention.py +0 -0
  57. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/cronjobs/view_cleanup.py +0 -0
  58. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/datastore/README.md +0 -0
  59. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/datastore/__init__.py +0 -0
  60. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/datastore/bulk.py +0 -0
  61. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/datastore/collection.py +0 -0
  62. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/datastore/constants.py +0 -0
  63. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/datastore/exceptions.py +0 -0
  64. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/datastore/howler_store.py +0 -0
  65. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/datastore/migrations/fix_process.py +0 -0
  66. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/datastore/operations.py +0 -0
  67. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/datastore/schemas.py +0 -0
  68. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/datastore/store.py +0 -0
  69. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/datastore/support/__init__.py +0 -0
  70. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/datastore/support/build.py +0 -0
  71. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/datastore/support/schemas.py +0 -0
  72. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/datastore/types.py +0 -0
  73. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/error.py +0 -0
  74. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/external/__init__.py +0 -0
  75. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/external/generate_mitre.py +0 -0
  76. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/external/generate_sigma_rules.py +0 -0
  77. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/external/generate_tlds.py +0 -0
  78. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/external/reindex_data.py +0 -0
  79. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/external/wipe_databases.py +0 -0
  80. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/gunicorn_config.py +0 -0
  81. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/healthz.py +0 -0
  82. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/helper/__init__.py +0 -0
  83. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/helper/azure.py +0 -0
  84. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/helper/discover.py +0 -0
  85. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/helper/hit.py +0 -0
  86. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/helper/oauth.py +0 -0
  87. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/helper/search.py +0 -0
  88. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/helper/workflow.py +0 -0
  89. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/helper/ws.py +0 -0
  90. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/README.md +0 -0
  91. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/__init__.py +0 -0
  92. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/base.py +0 -0
  93. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/charter.txt +0 -0
  94. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/constants.py +0 -0
  95. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/helper.py +0 -0
  96. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/howler_enum.py +0 -0
  97. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/__init__.py +0 -0
  98. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/action.py +0 -0
  99. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/analytic.py +0 -0
  100. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/assemblyline.py +0 -0
  101. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/aws.py +0 -0
  102. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/azure.py +0 -0
  103. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/case.py +0 -0
  104. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/cbs.py +0 -0
  105. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/clue.py +0 -0
  106. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/config.py +0 -0
  107. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/dossier.py +0 -0
  108. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/__init__.py +0 -0
  109. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/agent.py +0 -0
  110. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/autonomous_system.py +0 -0
  111. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/client.py +0 -0
  112. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/cloud.py +0 -0
  113. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/code_signature.py +0 -0
  114. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/container.py +0 -0
  115. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/dns.py +0 -0
  116. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/egress.py +0 -0
  117. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/elf.py +0 -0
  118. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/email.py +0 -0
  119. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/error.py +0 -0
  120. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/event.py +0 -0
  121. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/faas.py +0 -0
  122. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/file.py +0 -0
  123. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/geo.py +0 -0
  124. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/group.py +0 -0
  125. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/hash.py +0 -0
  126. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/host.py +0 -0
  127. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/http.py +0 -0
  128. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/ingress.py +0 -0
  129. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/interface.py +0 -0
  130. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/network.py +0 -0
  131. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/observer.py +0 -0
  132. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/organization.py +0 -0
  133. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/os.py +0 -0
  134. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/pe.py +0 -0
  135. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/process.py +0 -0
  136. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/registry.py +0 -0
  137. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/related.py +0 -0
  138. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/rule.py +0 -0
  139. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/server.py +0 -0
  140. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/threat.py +0 -0
  141. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/tls.py +0 -0
  142. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/url.py +0 -0
  143. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/user.py +0 -0
  144. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/user_agent.py +0 -0
  145. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/vulnerability.py +0 -0
  146. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/gcp.py +0 -0
  147. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/hit.py +0 -0
  148. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/howler_data.py +0 -0
  149. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/lead.py +0 -0
  150. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/localized_label.py +0 -0
  151. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/observable.py +0 -0
  152. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/overview.py +0 -0
  153. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/pivot.py +0 -0
  154. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/record.py +0 -0
  155. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/template.py +0 -0
  156. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/user.py +0 -0
  157. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/models/view.py +0 -0
  158. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/random_data.py +0 -0
  159. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/odm/randomizer.py +0 -0
  160. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/patched.py +0 -0
  161. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/plugins/__init__.py +0 -0
  162. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/plugins/config.py +0 -0
  163. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/remote/__init__.py +0 -0
  164. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/remote/datatypes/README.md +0 -0
  165. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/remote/datatypes/__init__.py +0 -0
  166. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/remote/datatypes/counters.py +0 -0
  167. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/remote/datatypes/events.py +0 -0
  168. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/remote/datatypes/hash.py +0 -0
  169. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/remote/datatypes/lock.py +0 -0
  170. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/remote/datatypes/queues/__init__.py +0 -0
  171. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/remote/datatypes/queues/comms.py +0 -0
  172. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/remote/datatypes/queues/multi.py +0 -0
  173. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/remote/datatypes/queues/named.py +0 -0
  174. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/remote/datatypes/queues/priority.py +0 -0
  175. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/remote/datatypes/set.py +0 -0
  176. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/remote/datatypes/user_quota_tracker.py +0 -0
  177. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/security/__init__.py +0 -0
  178. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/security/socket.py +0 -0
  179. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/security/utils.py +0 -0
  180. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/services/__init__.py +0 -0
  181. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/services/action_service.py +0 -0
  182. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/services/analytic_service.py +0 -0
  183. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/services/auth_service.py +0 -0
  184. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/services/config_service.py +0 -0
  185. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/services/docs_service.py +0 -0
  186. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/services/dossier_service.py +0 -0
  187. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/services/event_service.py +0 -0
  188. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/services/hit_service.py +0 -0
  189. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/services/jwt_service.py +0 -0
  190. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/services/lucene_service.py +0 -0
  191. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/services/notebook_service.py +0 -0
  192. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/services/observable_service.py +0 -0
  193. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/services/overview_service.py +0 -0
  194. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/services/search_service.py +0 -0
  195. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/services/template_service.py +0 -0
  196. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/services/user_service.py +0 -0
  197. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/utils/__init__.py +0 -0
  198. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/utils/annotations.py +0 -0
  199. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/utils/chunk.py +0 -0
  200. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/utils/compat.py +0 -0
  201. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/utils/dict_utils.py +0 -0
  202. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/utils/isotime.py +0 -0
  203. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/utils/list_utils.py +0 -0
  204. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/utils/lucene.py +0 -0
  205. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/utils/path.py +0 -0
  206. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/utils/socket_utils.py +0 -0
  207. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/howler/utils/str_utils.py +0 -0
  208. {howler_api-4.0.0.dev657 → howler_api-4.0.0.dev664}/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.dev657
3
+ Version: 4.0.0.dev664
4
4
  Summary: Howler - API server
5
5
  License: MIT
6
6
  Keywords: howler,alerting,gc,canada,cse-cst,cse,cst,cyber,cccs
@@ -246,42 +246,44 @@ def append_item(id: str, user: User, **kwargs): # noqa: C901
246
246
 
247
247
 
248
248
  @generate_swagger_docs()
249
- @case_api.route("/<id>/items", methods=["DELETE"])
249
+ @case_api.route("/<case_id>/items", methods=["DELETE"])
250
250
  @api_login(required_priv=["R", "W"])
251
- def delete_item(id: str, **kwargs):
252
- """Delete an item from a case
251
+ def delete_item(case_id: str, **kwargs):
252
+ """Delete one or more items from a case
253
253
 
254
- This endpoint removes an item from a case's items list. If the item is a hit or
254
+ This endpoint removes items from a case's items list. If an item is a hit or
255
255
  observable, the bidirectional relationship is cleaned up - the case reference will
256
256
  be removed from the backing object's related.cases list.
257
257
 
258
258
  Variables:
259
- id => The id of the case to modify
259
+ case_id => The id of the case to modify
260
260
 
261
261
  Arguments:
262
262
  None
263
263
 
264
264
  Data Block:
265
265
  {
266
- "value": "item-id-123" # The value of the item to delete
266
+ "values": ["item-id-123", "item-id-456"] # The values of the items to delete
267
267
  }
268
268
 
269
269
  Result Example:
270
270
  {
271
- "success": true # Did the deletion succeed?
271
+ ...case # The updated case data
272
272
  }
273
273
  """
274
274
  body = request.json
275
275
 
276
- if not body or not isinstance(body, dict) or "value" not in body:
277
- return bad_request(err="Request body must be a JSON object with a 'value' field.")
276
+ if not body or not isinstance(body, dict) or "values" not in body:
277
+ return bad_request(err="Request body must be a JSON object with a 'values' field.")
278
+
279
+ values = body["values"]
280
+ if not isinstance(values, list) or not values:
281
+ return bad_request(err="'values' must be a non-empty list.")
278
282
 
279
283
  try:
280
- case_service.remove_case_item(id, item_value=body["value"])
284
+ return ok(case_service.remove_case_items(case_id, values))
281
285
  except DataStoreException as e:
282
286
  logger.exception("Save Error")
283
287
  return internal_error(err=str(e))
284
- except InvalidDataException as e:
288
+ except (InvalidDataException, NotFoundException) as e:
285
289
  return bad_request(err=str(e))
286
-
287
- return ok()
@@ -320,8 +320,14 @@ def append_hit(case_id: str, item: CaseItem) -> Case:
320
320
 
321
321
  hit = ds.hit.get(item.value)
322
322
 
323
+ if hit is None:
324
+ raise NotFoundException(f"Hit {item.value} not found, cannot be added to case")
325
+
323
326
  _case.items.append(item)
324
327
 
328
+ if not ds.case.save(_case.case_id, _case):
329
+ raise DataStoreException(f"Failed to save {_case.case_id} with new item {item.value}")
330
+
325
331
  _add_backreference(hit, _case.case_id)
326
332
 
327
333
  _sync_case_metadata(_case.case_id)
@@ -362,7 +368,7 @@ def append_observable(case_id: str, item: CaseItem) -> Case:
362
368
 
363
369
  _case.items.append(item)
364
370
 
365
- if not datastore().case.save(_case.case_id, _case):
371
+ if not ds.case.save(_case.case_id, _case):
366
372
  raise DataStoreException(f"Failed to save {_case.case_id} with new item {item.value}")
367
373
 
368
374
  _add_backreference(observable, _case.case_id)
@@ -585,20 +591,26 @@ def remove_backreference(backing_obj: Hit | Observable | None, case_id: str):
585
591
  datastore()[backing_obj.__class__.__name__.lower()].save(backing_obj.howler.id, backing_obj)
586
592
 
587
593
 
588
- def remove_case_item(case_id: str, item_value: str):
589
- """Remove an item from a case and clean up any associated back-references.
594
+ def remove_case_items(case_id: str, values: list[str]):
595
+ """Remove one or more items from a case in a single atomic operation.
590
596
 
591
- Locates the item within the case by its value, removes it from the case's
592
- items list, persists the updated case, and removes the back-reference from
593
- the backing hit or observable if applicable.
597
+ Validates that every requested value exists within the case before making
598
+ any modifications. If any value is missing the call raises NotFoundException
599
+ without altering the case. When all values are confirmed, all matching
600
+ items are removed in memory, the case is persisted once, and back-references
601
+ are cleaned up from any associated hits or observables.
594
602
 
595
603
  Args:
596
- case_id: Unique identifier of the case to remove the item from.
597
- item_value: The value/identifier of the item to remove.
604
+ case_id: Unique identifier of the case to remove items from.
605
+ item_values: List of item values (IDs / URLs) to remove.
606
+
607
+ Returns:
608
+ The updated Case object.
598
609
 
599
610
  Raises:
600
- NotFoundException: If the case does not exist.
601
- DataStoreException: If saving the updated case fails.
611
+ NotFoundException: If the case does not exist, or if any requested value
612
+ is not present in the case's items list.
613
+ DataStoreException: If persisting the updated case fails.
602
614
  """
603
615
  ds = datastore()
604
616
 
@@ -607,20 +619,34 @@ def remove_case_item(case_id: str, item_value: str):
607
619
  if not _case:
608
620
  raise NotFoundException(f"Case {case_id} does not exist")
609
621
 
610
- item = next((_item for _item in _case.items if _item["value"] == item_value), None)
611
- if not item:
612
- raise NotFoundException(f"Case item {item_value} does not exist")
622
+ # Build a lookup of value → item for all items currently in the case.
623
+ items_by_value = {_item["value"]: _item for _item in _case.items}
613
624
 
614
- backing_obj: Hit | Observable | None = None
615
- if item.type in [CaseItemTypes.HIT, CaseItemTypes.OBSERVABLE]:
616
- backing_obj = ds[item.type].get(item.value)
625
+ # Pre-validate all requested values before touching anything.
626
+ missing = [v for v in values if v not in items_by_value]
627
+ if missing:
628
+ raise NotFoundException(f"Case item(s) not found in case: {', '.join(missing)}")
617
629
 
618
- _case.items.remove(item)
630
+ # Resolve items and collect backing objects that need back-reference cleanup.
631
+ items_to_remove = [items_by_value[v] for v in values]
632
+ backing_objs: list[Hit | Observable] = []
633
+ for item in items_to_remove:
634
+ if item.type in [CaseItemTypes.HIT, CaseItemTypes.OBSERVABLE]:
635
+ obj = ds[item.type].get(item.value)
636
+ if obj:
637
+ backing_objs.append(obj)
638
+
639
+ # Remove all items in memory, then persist the case once.
640
+ for item in items_to_remove:
641
+ _case.items.remove(item)
619
642
 
620
643
  if not ds.case.save(_case.case_id, _case):
621
644
  raise DataStoreException("Failed to save case after item removal")
622
645
 
623
- if backing_obj:
646
+ # Clean up back-references after the case is safely persisted.
647
+ for backing_obj in backing_objs:
624
648
  remove_backreference(backing_obj, _case.case_id)
625
649
 
626
650
  _sync_case_metadata(case_id)
651
+
652
+ return _case
@@ -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.dev657"
155
+ version = "4.0.0.dev664"
156
156
  description = "Howler - API server"
157
157
  authors = [
158
158
  "Canadian Centre for Cyber Security <howler@cyber.gc.ca>",