howler-api 3.4.0.dev929__tar.gz → 3.4.0.dev933__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/PKG-INFO +1 -1
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/api/v1/hit.py +3 -11
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/api/v1/tool.py +1 -1
- howler_api-3.4.0.dev933/howler/cronjobs/action_queue_worker.py +82 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/config.py +15 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/services/action_service.py +95 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/services/hit_service.py +5 -5
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/pyproject.toml +1 -1
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/README.md +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/__init__.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/actions/__init__.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/actions/add_label.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/actions/add_to_bundle.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/actions/change_field.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/actions/demote.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/actions/example_plugin.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/actions/prioritization.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/actions/promote.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/actions/remove_from_bundle.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/actions/remove_label.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/actions/transition.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/api/__init__.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/api/base.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/api/socket.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/api/v1/__init__.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/api/v1/action.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/api/v1/analytic.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/api/v1/auth.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/api/v1/clue.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/api/v1/configs.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/api/v1/dossier.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/api/v1/help.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/api/v1/notebook.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/api/v1/overview.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/api/v1/search.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/api/v1/template.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/api/v1/user.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/api/v1/utils/__init__.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/api/v1/utils/etag.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/api/v1/view.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/app.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/common/README.md +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/common/__init__.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/common/classification.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/common/classification.yml +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/common/exceptions.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/common/loader.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/common/logging/__init__.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/common/logging/audit.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/common/logging/format.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/common/net.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/common/net_static.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/common/random_user.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/common/swagger.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/config.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/cronjobs/__init__.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/cronjobs/retention.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/cronjobs/rules.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/cronjobs/view_cleanup.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/datastore/README.md +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/datastore/__init__.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/datastore/bulk.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/datastore/collection.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/datastore/constants.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/datastore/exceptions.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/datastore/howler_store.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/datastore/migrations/fix_process.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/datastore/operations.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/datastore/schemas.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/datastore/store.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/datastore/support/__init__.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/datastore/support/build.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/datastore/support/schemas.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/datastore/types.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/error.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/external/README.md +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/external/__init__.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/external/generate_mitre.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/external/generate_sigma_rules.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/external/generate_tlds.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/external/reindex_data.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/external/wipe_databases.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/gunicorn_config.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/healthz.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/helper/__init__.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/helper/azure.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/helper/discover.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/helper/hit.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/helper/oauth.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/helper/search.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/helper/workflow.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/helper/ws.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/README.md +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/__init__.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/base.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/charter.txt +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/helper.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/howler_enum.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/__init__.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/action.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/analytic.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/assemblyline.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/aws.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/azure.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/cbs.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/clue.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/dossier.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/__init__.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/agent.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/autonomous_system.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/client.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/cloud.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/code_signature.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/container.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/dns.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/egress.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/elf.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/email.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/error.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/event.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/faas.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/file.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/geo.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/group.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/hash.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/host.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/http.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/ingress.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/interface.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/network.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/observer.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/organization.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/os.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/pe.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/process.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/registry.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/related.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/rule.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/server.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/threat.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/tls.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/url.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/user.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/user_agent.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/vulnerability.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/gcp.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/hit.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/howler_data.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/lead.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/localized_label.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/overview.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/pivot.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/template.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/user.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/view.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/random_data.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/randomizer.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/patched.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/plugins/__init__.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/plugins/config.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/remote/__init__.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/remote/datatypes/README.md +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/remote/datatypes/__init__.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/remote/datatypes/counters.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/remote/datatypes/events.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/remote/datatypes/hash.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/remote/datatypes/lock.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/remote/datatypes/queues/__init__.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/remote/datatypes/queues/comms.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/remote/datatypes/queues/multi.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/remote/datatypes/queues/named.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/remote/datatypes/queues/priority.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/remote/datatypes/set.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/remote/datatypes/user_quota_tracker.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/security/__init__.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/security/socket.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/security/utils.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/services/__init__.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/services/analytic_service.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/services/auth_service.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/services/config_service.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/services/dossier_service.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/services/event_service.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/services/jwt_service.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/services/lucene_service.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/services/notebook_service.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/services/overview_service.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/services/template_service.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/services/user_service.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/telemetry.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/utils/__init__.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/utils/annotations.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/utils/chunk.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/utils/compat.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/utils/constants.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/utils/dict_utils.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/utils/isotime.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/utils/list_utils.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/utils/lucene.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/utils/path.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/utils/socket_utils.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/utils/str_utils.py +0 -0
- {howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/utils/uid.py +0 -0
|
@@ -123,7 +123,7 @@ def create_hits(user: User, **kwargs):
|
|
|
123
123
|
|
|
124
124
|
datastore().hit.commit()
|
|
125
125
|
|
|
126
|
-
action_service.
|
|
126
|
+
action_service.enqueue_action_execution([odm.howler.id for odm in odms], trigger="create", user=user)
|
|
127
127
|
|
|
128
128
|
response_body["warnings"] = warnings
|
|
129
129
|
|
|
@@ -551,11 +551,7 @@ def add_label(id, label_set, user, **kwargs):
|
|
|
551
551
|
|
|
552
552
|
datastore().hit.commit()
|
|
553
553
|
|
|
554
|
-
action_service.
|
|
555
|
-
f"howler.id:{id}",
|
|
556
|
-
trigger="add_label",
|
|
557
|
-
user=user,
|
|
558
|
-
)
|
|
554
|
+
action_service.enqueue_action_execution([id], trigger="add_label", user=user)
|
|
559
555
|
|
|
560
556
|
hit, version = hit_service.get_hit(id, as_odm=False, version=True)
|
|
561
557
|
|
|
@@ -609,11 +605,7 @@ def remove_labels(id, label_set, user, **kwargs):
|
|
|
609
605
|
|
|
610
606
|
datastore().hit.commit()
|
|
611
607
|
|
|
612
|
-
action_service.
|
|
613
|
-
f"howler.id:{id}",
|
|
614
|
-
trigger="remove_label",
|
|
615
|
-
user=user,
|
|
616
|
-
)
|
|
608
|
+
action_service.enqueue_action_execution([id], trigger="remove_label", user=user)
|
|
617
609
|
|
|
618
610
|
hit, version = hit_service.get_hit(id, as_odm=False, version=True)
|
|
619
611
|
|
|
@@ -178,6 +178,6 @@ def create_one_or_many_hits(tool_name: str, user: User, **kwargs): # noqa: C901
|
|
|
178
178
|
|
|
179
179
|
datastore().hit.commit()
|
|
180
180
|
|
|
181
|
-
action_service.
|
|
181
|
+
action_service.enqueue_action_execution([entry["id"] for entry in out], trigger="create", user=user)
|
|
182
182
|
|
|
183
183
|
return created(out, warnings=warnings)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""Action queue worker cronjob — starts per-trigger action queue consumer threads.
|
|
2
|
+
|
|
3
|
+
Auto-discovered by ``howler.cronjobs.setup_jobs`` when ``HWL_USE_JOB_SYSTEM``
|
|
4
|
+
is enabled. Instead of scheduling a periodic APScheduler job, this module
|
|
5
|
+
starts a long-running daemon thread per trigger type that drains its
|
|
6
|
+
dedicated action queue.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import threading
|
|
10
|
+
|
|
11
|
+
from apscheduler.schedulers.base import BaseScheduler
|
|
12
|
+
|
|
13
|
+
from howler.common.logging import get_logger
|
|
14
|
+
from howler.config import config
|
|
15
|
+
from howler.odm.models.action import VALID_TRIGGERS
|
|
16
|
+
from howler.services.action_service import _get_action_queue, process_action_batch
|
|
17
|
+
|
|
18
|
+
logger = get_logger(__file__)
|
|
19
|
+
|
|
20
|
+
_threads: dict[str, threading.Thread] = {}
|
|
21
|
+
|
|
22
|
+
BATCH_SIZE: int = config.system.action_queue.batch_size
|
|
23
|
+
BATCH_TIMEOUT: int = config.system.action_queue.batch_timeout
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def run_worker(trigger: str) -> None: # pragma: no cover – long-running loop, tested via process_action_batch
|
|
27
|
+
"""Block on the action queue for *trigger* and process batches.
|
|
28
|
+
|
|
29
|
+
Accumulates up to ``BATCH_SIZE`` items or flushes after ``BATCH_TIMEOUT``
|
|
30
|
+
seconds, whichever comes first.
|
|
31
|
+
"""
|
|
32
|
+
if not config.system.action_queue.enabled:
|
|
33
|
+
logger.info("Action queue worker disabled by configuration, not starting for trigger=%s", trigger)
|
|
34
|
+
return
|
|
35
|
+
|
|
36
|
+
queue = _get_action_queue(trigger)
|
|
37
|
+
logger.info(
|
|
38
|
+
"Action queue worker started for trigger=%s (batch_size=%d, timeout=%ds)",
|
|
39
|
+
trigger,
|
|
40
|
+
BATCH_SIZE,
|
|
41
|
+
BATCH_TIMEOUT,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
batch: list[dict] = []
|
|
45
|
+
|
|
46
|
+
while True:
|
|
47
|
+
try:
|
|
48
|
+
item = queue.pop(blocking=True, timeout=BATCH_TIMEOUT)
|
|
49
|
+
|
|
50
|
+
if item is not None:
|
|
51
|
+
batch.append(item)
|
|
52
|
+
|
|
53
|
+
if len(batch) >= BATCH_SIZE or (item is None and batch):
|
|
54
|
+
logger.debug("Processing action batch of %d item(s) for trigger=%s", len(batch), trigger)
|
|
55
|
+
try:
|
|
56
|
+
process_action_batch(trigger, batch)
|
|
57
|
+
logger.info("Action batch complete: %d item(s) processed for trigger=%s", len(batch), trigger)
|
|
58
|
+
except Exception:
|
|
59
|
+
logger.exception("Error processing action batch for trigger=%s", trigger)
|
|
60
|
+
finally:
|
|
61
|
+
batch = []
|
|
62
|
+
except Exception:
|
|
63
|
+
logger.exception("Unexpected error in action queue worker loop for trigger=%s", trigger)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def setup_job(sched: BaseScheduler):
|
|
67
|
+
"""Start an action queue worker thread per trigger type if the action queue is enabled."""
|
|
68
|
+
global _threads
|
|
69
|
+
|
|
70
|
+
if not config.system.action_queue.enabled:
|
|
71
|
+
logger.info("Action queue worker disabled by configuration")
|
|
72
|
+
return
|
|
73
|
+
|
|
74
|
+
for trigger in VALID_TRIGGERS:
|
|
75
|
+
if trigger in _threads and _threads[trigger].is_alive():
|
|
76
|
+
logger.debug("Action queue worker thread for trigger=%s already running", trigger)
|
|
77
|
+
continue
|
|
78
|
+
|
|
79
|
+
thread = threading.Thread(target=run_worker, args=(trigger,), name=f"action-queue-{trigger}", daemon=True)
|
|
80
|
+
thread.start()
|
|
81
|
+
_threads[trigger] = thread
|
|
82
|
+
logger.info("Action queue worker thread started for trigger=%s", trigger)
|
|
@@ -353,6 +353,19 @@ class ViewCleanup(BaseModel):
|
|
|
353
353
|
)
|
|
354
354
|
|
|
355
355
|
|
|
356
|
+
class ActionQueue(BaseModel):
|
|
357
|
+
"""Action queue worker configuration.
|
|
358
|
+
|
|
359
|
+
Controls the background worker that buffers action execution requests
|
|
360
|
+
via a Redis queue, coalescing them into batches to reduce Elasticsearch
|
|
361
|
+
query load during ingestion spikes.
|
|
362
|
+
"""
|
|
363
|
+
|
|
364
|
+
enabled: bool = Field(default=True, description="Enable the action queue worker?")
|
|
365
|
+
batch_size: int = Field(default=100, description="Max action items per batch.")
|
|
366
|
+
batch_timeout: int = Field(default=10, description="Seconds to wait before flushing a partial batch.")
|
|
367
|
+
|
|
368
|
+
|
|
356
369
|
class System(BaseModel):
|
|
357
370
|
"""System-level configuration for Howler.
|
|
358
371
|
|
|
@@ -366,6 +379,8 @@ class System(BaseModel):
|
|
|
366
379
|
"Retention Configuration"
|
|
367
380
|
view_cleanup: ViewCleanup = ViewCleanup()
|
|
368
381
|
"View Cleanup Configuration"
|
|
382
|
+
action_queue: ActionQueue = ActionQueue()
|
|
383
|
+
"Action Queue Worker Configuration"
|
|
369
384
|
|
|
370
385
|
|
|
371
386
|
class UI(BaseModel):
|
|
@@ -9,13 +9,108 @@ from howler.common.exceptions import HowlerValueError
|
|
|
9
9
|
from howler.common.loader import datastore
|
|
10
10
|
from howler.common.logging import get_logger
|
|
11
11
|
from howler.common.logging.audit import audit
|
|
12
|
+
from howler.config import config
|
|
12
13
|
from howler.odm.models.action import VALID_TRIGGERS, Action
|
|
13
14
|
from howler.odm.models.user import User
|
|
15
|
+
from howler.remote.datatypes.queues.named import NamedQueue
|
|
14
16
|
from howler.utils.constants import TESTING
|
|
15
17
|
from howler.utils.str_utils import sanitize_lucene_query
|
|
16
18
|
|
|
17
19
|
logger = get_logger(__file__)
|
|
18
20
|
|
|
21
|
+
# Per-trigger persistent queues for buffering action execution requests.
|
|
22
|
+
_action_queues: dict[str, NamedQueue[dict]] = {}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _get_action_queue(trigger: str) -> NamedQueue[dict]:
|
|
26
|
+
"""Return the action queue for *trigger*, creating it on first use.
|
|
27
|
+
|
|
28
|
+
Raises:
|
|
29
|
+
ValueError: If *trigger* is not in ``VALID_TRIGGERS``.
|
|
30
|
+
"""
|
|
31
|
+
if trigger not in VALID_TRIGGERS:
|
|
32
|
+
raise HowlerValueError(f"Invalid trigger {trigger!r}. Must be one of {VALID_TRIGGERS}")
|
|
33
|
+
|
|
34
|
+
if trigger not in _action_queues:
|
|
35
|
+
_action_queues[trigger] = NamedQueue(
|
|
36
|
+
f"howler.action_queue.{trigger}",
|
|
37
|
+
host=config.core.redis.persistent.host,
|
|
38
|
+
port=config.core.redis.persistent.port,
|
|
39
|
+
private=False,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
return _action_queues[trigger]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def enqueue_action_execution(hit_ids: list[str], trigger: str = "create", user: Optional[User] = None) -> None:
|
|
46
|
+
"""Buffer action execution by pushing to a Redis queue.
|
|
47
|
+
|
|
48
|
+
When the action queue is disabled in configuration, falls back to
|
|
49
|
+
calling ``bulk_execute_on_query`` directly for backwards compatibility.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
hit_ids: List of hit IDs to execute actions against.
|
|
53
|
+
trigger: The trigger type (create, promote, demote, add_label, remove_label).
|
|
54
|
+
user: The user initiating the action.
|
|
55
|
+
"""
|
|
56
|
+
if not hit_ids:
|
|
57
|
+
return
|
|
58
|
+
|
|
59
|
+
if trigger not in VALID_TRIGGERS:
|
|
60
|
+
raise HowlerValueError(f"Invalid trigger {trigger!r}. Must be one of {VALID_TRIGGERS}")
|
|
61
|
+
|
|
62
|
+
if not config.system.action_queue.enabled:
|
|
63
|
+
query = f"howler.id:({' OR '.join(sanitize_lucene_query(h) for h in hit_ids)})"
|
|
64
|
+
bulk_execute_on_query(query, trigger=trigger, user=user)
|
|
65
|
+
return
|
|
66
|
+
|
|
67
|
+
try:
|
|
68
|
+
_get_action_queue(trigger).push(
|
|
69
|
+
{
|
|
70
|
+
"hit_ids": hit_ids,
|
|
71
|
+
"user": user.as_primitives() if user is not None and hasattr(user, "as_primitives") else user,
|
|
72
|
+
}
|
|
73
|
+
)
|
|
74
|
+
except Exception:
|
|
75
|
+
logger.exception("Failed to enqueue action execution, falling back to direct execution")
|
|
76
|
+
query = f"howler.id:({' OR '.join(sanitize_lucene_query(h) for h in hit_ids)})"
|
|
77
|
+
bulk_execute_on_query(query, trigger=trigger, user=user)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def process_action_batch(trigger: str, items: list[dict]) -> None:
|
|
81
|
+
"""Process a batch of queued action execution requests for a single trigger.
|
|
82
|
+
|
|
83
|
+
Groups items by user and issues a single coalesced
|
|
84
|
+
``bulk_execute_on_query`` call per group, reducing Elasticsearch load.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
trigger: The trigger type this batch belongs to.
|
|
88
|
+
items: List of dicts with keys ``hit_ids`` and ``user``.
|
|
89
|
+
"""
|
|
90
|
+
if not items:
|
|
91
|
+
return
|
|
92
|
+
|
|
93
|
+
groups: dict[str | None, tuple[list[str], Any]] = {}
|
|
94
|
+
|
|
95
|
+
for item in items:
|
|
96
|
+
user_data = item.get("user")
|
|
97
|
+
user_key = user_data["uname"] if isinstance(user_data, dict) and "uname" in user_data else None
|
|
98
|
+
|
|
99
|
+
if user_key not in groups:
|
|
100
|
+
groups[user_key] = ([], user_data)
|
|
101
|
+
|
|
102
|
+
groups[user_key][0].extend(item["hit_ids"])
|
|
103
|
+
|
|
104
|
+
for user_key, (hit_ids, user_data) in groups.items():
|
|
105
|
+
# Deduplicate IDs within a batch group
|
|
106
|
+
unique_ids = list(dict.fromkeys(hit_ids))
|
|
107
|
+
query = f"howler.id:({' OR '.join(sanitize_lucene_query(h) for h in unique_ids)})"
|
|
108
|
+
|
|
109
|
+
try:
|
|
110
|
+
bulk_execute_on_query(query, trigger=trigger, user=user_data)
|
|
111
|
+
except Exception:
|
|
112
|
+
logger.exception("Error processing action batch for trigger=%s user=%s", trigger, user_key)
|
|
113
|
+
|
|
19
114
|
|
|
20
115
|
def validate_action(new_action: Any) -> Optional[Response]: # noqa: C901
|
|
21
116
|
"""Validate a new action"""
|
|
@@ -736,12 +736,12 @@ def transition_hit(
|
|
|
736
736
|
# Commit database changes before executing bulk actions
|
|
737
737
|
datastore().hit.commit()
|
|
738
738
|
|
|
739
|
-
# Build query for all processed hits (primary + children)
|
|
740
|
-
all_processed_hits = [primary_hit] + child_hits
|
|
741
|
-
|
|
739
|
+
# Build query for all processed hits (primary + children), excluding missing hits
|
|
740
|
+
all_processed_hits = [primary_hit] + [ch for ch in child_hits if ch]
|
|
741
|
+
hit_ids = [h["howler"]["id"] for h in all_processed_hits]
|
|
742
742
|
|
|
743
|
-
#
|
|
744
|
-
action_service.
|
|
743
|
+
# Enqueue action execution for all hits
|
|
744
|
+
action_service.enqueue_action_execution(hit_ids, trigger=trigger, user=user)
|
|
745
745
|
|
|
746
746
|
# Emit events for all processed hits to notify other systems
|
|
747
747
|
for processed_hit in all_processed_hits:
|
|
@@ -152,7 +152,7 @@ suppress-none-returning = true
|
|
|
152
152
|
[tool.poetry]
|
|
153
153
|
package-mode = true
|
|
154
154
|
name = "howler-api"
|
|
155
|
-
version = "3.4.0.
|
|
155
|
+
version = "3.4.0.dev933"
|
|
156
156
|
description = "Howler - API server"
|
|
157
157
|
authors = [
|
|
158
158
|
"Canadian Centre for Cyber Security <howler@cyber.gc.ca>",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/datastore/migrations/fix_process.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/autonomous_system.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/remote/datatypes/queues/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/remote/datatypes/queues/priority.py
RENAMED
|
File without changes
|
|
File without changes
|
{howler_api-3.4.0.dev929 → howler_api-3.4.0.dev933}/howler/remote/datatypes/user_quota_tracker.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|