howler-api 3.4.0.dev927__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.dev927 → howler_api-3.4.0.dev933}/PKG-INFO +1 -1
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/api/v1/hit.py +3 -11
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/api/v1/search.py +29 -0
- {howler_api-3.4.0.dev927 → 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.dev933/howler/cronjobs/retention.py +129 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/datastore/collection.py +124 -204
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/datastore/howler_store.py +1 -2
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/datastore/store.py +3 -12
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/datastore/types.py +4 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/config.py +15 -45
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/services/action_service.py +95 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/services/hit_service.py +5 -5
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/pyproject.toml +1 -1
- howler_api-3.4.0.dev927/howler/cronjobs/retention.py +0 -61
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/README.md +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/__init__.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/actions/__init__.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/actions/add_label.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/actions/add_to_bundle.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/actions/change_field.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/actions/demote.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/actions/example_plugin.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/actions/prioritization.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/actions/promote.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/actions/remove_from_bundle.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/actions/remove_label.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/actions/transition.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/api/__init__.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/api/base.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/api/socket.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/api/v1/__init__.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/api/v1/action.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/api/v1/analytic.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/api/v1/auth.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/api/v1/clue.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/api/v1/configs.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/api/v1/dossier.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/api/v1/help.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/api/v1/notebook.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/api/v1/overview.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/api/v1/template.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/api/v1/user.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/api/v1/utils/__init__.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/api/v1/utils/etag.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/api/v1/view.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/app.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/common/README.md +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/common/__init__.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/common/classification.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/common/classification.yml +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/common/exceptions.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/common/loader.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/common/logging/__init__.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/common/logging/audit.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/common/logging/format.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/common/net.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/common/net_static.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/common/random_user.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/common/swagger.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/config.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/cronjobs/__init__.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/cronjobs/rules.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/cronjobs/view_cleanup.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/datastore/README.md +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/datastore/__init__.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/datastore/bulk.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/datastore/constants.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/datastore/exceptions.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/datastore/migrations/fix_process.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/datastore/operations.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/datastore/schemas.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/datastore/support/__init__.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/datastore/support/build.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/datastore/support/schemas.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/error.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/external/README.md +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/external/__init__.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/external/generate_mitre.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/external/generate_sigma_rules.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/external/generate_tlds.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/external/reindex_data.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/external/wipe_databases.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/gunicorn_config.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/healthz.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/helper/__init__.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/helper/azure.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/helper/discover.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/helper/hit.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/helper/oauth.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/helper/search.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/helper/workflow.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/helper/ws.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/README.md +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/__init__.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/base.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/charter.txt +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/helper.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/howler_enum.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/__init__.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/action.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/analytic.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/assemblyline.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/aws.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/azure.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/cbs.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/clue.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/dossier.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/__init__.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/agent.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/autonomous_system.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/client.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/cloud.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/code_signature.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/container.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/dns.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/egress.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/elf.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/email.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/error.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/event.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/faas.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/file.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/geo.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/group.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/hash.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/host.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/http.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/ingress.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/interface.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/network.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/observer.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/organization.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/os.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/pe.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/process.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/registry.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/related.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/rule.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/server.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/threat.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/tls.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/url.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/user.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/user_agent.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/ecs/vulnerability.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/gcp.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/hit.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/howler_data.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/lead.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/localized_label.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/overview.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/pivot.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/template.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/user.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/models/view.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/random_data.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/odm/randomizer.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/patched.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/plugins/__init__.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/plugins/config.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/remote/__init__.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/remote/datatypes/README.md +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/remote/datatypes/__init__.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/remote/datatypes/counters.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/remote/datatypes/events.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/remote/datatypes/hash.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/remote/datatypes/lock.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/remote/datatypes/queues/__init__.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/remote/datatypes/queues/comms.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/remote/datatypes/queues/multi.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/remote/datatypes/queues/named.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/remote/datatypes/queues/priority.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/remote/datatypes/set.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/remote/datatypes/user_quota_tracker.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/security/__init__.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/security/socket.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/security/utils.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/services/__init__.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/services/analytic_service.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/services/auth_service.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/services/config_service.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/services/dossier_service.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/services/event_service.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/services/jwt_service.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/services/lucene_service.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/services/notebook_service.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/services/overview_service.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/services/template_service.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/services/user_service.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/telemetry.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/utils/__init__.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/utils/annotations.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/utils/chunk.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/utils/compat.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/utils/constants.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/utils/dict_utils.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/utils/isotime.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/utils/list_utils.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/utils/lucene.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/utils/path.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/utils/socket_utils.py +0 -0
- {howler_api-3.4.0.dev927 → howler_api-3.4.0.dev933}/howler/utils/str_utils.py +0 -0
- {howler_api-3.4.0.dev927 → 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
|
|
|
@@ -78,6 +78,7 @@ def search(index, **kwargs):
|
|
|
78
78
|
sort => How to sort the results (not available in deep paging)
|
|
79
79
|
fl => List of fields to return
|
|
80
80
|
timeout => Maximum execution time (ms)
|
|
81
|
+
use_archive => Allow access to the datastore achive (Default: False)
|
|
81
82
|
track_total_hits => Track the total number of query matches, instead of stopping at 10000 (Default: False)
|
|
82
83
|
metadata => A list of additional features to be added to the result alongside the raw results
|
|
83
84
|
|
|
@@ -117,9 +118,18 @@ def search(index, **kwargs):
|
|
|
117
118
|
"track_total_hits",
|
|
118
119
|
]
|
|
119
120
|
multi_fields = ["filters", "metadata"]
|
|
121
|
+
boolean_fields = ["use_archive"]
|
|
120
122
|
|
|
121
123
|
params, req_data = generate_params(request, fields, multi_fields)
|
|
122
124
|
|
|
125
|
+
params.update(
|
|
126
|
+
{
|
|
127
|
+
k: str(req_data.get(k, "false")).lower() in ["true", ""]
|
|
128
|
+
for k in boolean_fields
|
|
129
|
+
if req_data.get(k, None) is not None
|
|
130
|
+
}
|
|
131
|
+
)
|
|
132
|
+
|
|
123
133
|
if has_access_control(index):
|
|
124
134
|
params.update({"access_control": user["access_control"]})
|
|
125
135
|
|
|
@@ -340,9 +350,18 @@ def sigma_search(index, **kwargs):
|
|
|
340
350
|
"track_total_hits",
|
|
341
351
|
]
|
|
342
352
|
multi_fields = ["filters"]
|
|
353
|
+
boolean_fields = ["use_archive"]
|
|
343
354
|
|
|
344
355
|
params, req_data = generate_params(request, fields, multi_fields)
|
|
345
356
|
|
|
357
|
+
params.update(
|
|
358
|
+
{
|
|
359
|
+
k: str(req_data.get(k, "false")).lower() in ["true", ""]
|
|
360
|
+
for k in boolean_fields
|
|
361
|
+
if req_data.get(k, None) is not None
|
|
362
|
+
}
|
|
363
|
+
)
|
|
364
|
+
|
|
346
365
|
if has_access_control(index):
|
|
347
366
|
params.update({"access_control": user["access_control"]})
|
|
348
367
|
|
|
@@ -501,6 +520,7 @@ def count(index, **kwargs):
|
|
|
501
520
|
Optional Arguments:
|
|
502
521
|
filters => List of additional filter queries limit the data
|
|
503
522
|
timeout => Maximum execution time (ms)
|
|
523
|
+
use_archive => Allow access to the datastore achive (Default: False)
|
|
504
524
|
|
|
505
525
|
Data Block:
|
|
506
526
|
# Note that the data block is for POST requests only!
|
|
@@ -523,6 +543,15 @@ def count(index, **kwargs):
|
|
|
523
543
|
|
|
524
544
|
params, req_data = generate_params(request, [], [])
|
|
525
545
|
|
|
546
|
+
boolean_fields = ["use_archive"]
|
|
547
|
+
params.update(
|
|
548
|
+
{
|
|
549
|
+
k: str(req_data.get(k, "false")).lower() in ["true", ""]
|
|
550
|
+
for k in boolean_fields
|
|
551
|
+
if req_data.get(k, None) is not None
|
|
552
|
+
}
|
|
553
|
+
)
|
|
554
|
+
|
|
526
555
|
if has_access_control(index):
|
|
527
556
|
params.update({"access_control": user["access_control"]})
|
|
528
557
|
|
|
@@ -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)
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from datetime import datetime, timedelta
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from apscheduler.schedulers.base import BaseScheduler
|
|
6
|
+
from apscheduler.triggers.cron import CronTrigger
|
|
7
|
+
from pytz import timezone
|
|
8
|
+
|
|
9
|
+
from howler.common.logging import get_logger
|
|
10
|
+
from howler.config import DEBUG, config
|
|
11
|
+
from howler.datastore.howler_store import HowlerDatastore
|
|
12
|
+
|
|
13
|
+
logger = get_logger(__file__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def execute():
|
|
17
|
+
"""Delete any hits older than the configured time"""
|
|
18
|
+
from howler.common.loader import datastore
|
|
19
|
+
|
|
20
|
+
delta_kwargs = {str(config.system.retention.limit_unit): config.system.retention.limit_amount}
|
|
21
|
+
|
|
22
|
+
cutoff = (datetime.now() - timedelta(**delta_kwargs)).strftime("%Y-%m-%d")
|
|
23
|
+
|
|
24
|
+
logger.debug("Removing hits older than %s", cutoff)
|
|
25
|
+
|
|
26
|
+
ds = datastore()
|
|
27
|
+
|
|
28
|
+
ds.hit.delete_by_query(f"event.created:{{* TO {cutoff}}} OR howler.expiry:{{* TO now}}")
|
|
29
|
+
|
|
30
|
+
ds.hit.commit()
|
|
31
|
+
|
|
32
|
+
logger.debug("Deletion complete")
|
|
33
|
+
|
|
34
|
+
logger.debug("Cleaning analytics with no matching hits")
|
|
35
|
+
_remove_analytics_without_hits(ds)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _remove_analytics_without_hits(ds: HowlerDatastore):
|
|
39
|
+
|
|
40
|
+
matched_analytics = _find_analytics_with_hits(ds)
|
|
41
|
+
|
|
42
|
+
if matched_analytics is not None:
|
|
43
|
+
ds.analytic.delete_by_search_object(
|
|
44
|
+
{
|
|
45
|
+
"bool": {
|
|
46
|
+
"filter": [
|
|
47
|
+
{
|
|
48
|
+
"bool": {
|
|
49
|
+
"must_not": [
|
|
50
|
+
{"exists": {"field": "rule"}},
|
|
51
|
+
{"exists": {"field": "rule_type"}},
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
],
|
|
56
|
+
"must_not": [{"terms": {"name": matched_analytics}}],
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
)
|
|
60
|
+
ds.analytic.commit()
|
|
61
|
+
else:
|
|
62
|
+
logger.warning(
|
|
63
|
+
"Aggregation search for matched analytics did not run or returned no results. "
|
|
64
|
+
"There is likely an issue with the query. Skipping cleanup."
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _find_analytics_with_hits(ds: HowlerDatastore) -> list[str] | None:
|
|
69
|
+
|
|
70
|
+
total_analytics = ds.analytic.count("id:*", filters=None)["count"]
|
|
71
|
+
|
|
72
|
+
if total_analytics:
|
|
73
|
+
matched_analytics = ds.hit.search(
|
|
74
|
+
"howler.id:*",
|
|
75
|
+
aggregations=[
|
|
76
|
+
(
|
|
77
|
+
"matched_analytics",
|
|
78
|
+
{
|
|
79
|
+
"terms": {
|
|
80
|
+
"field": "howler.analytic",
|
|
81
|
+
"size": total_analytics,
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
)
|
|
85
|
+
],
|
|
86
|
+
rows=0,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
if "matched_analytics" in matched_analytics["aggregations"]:
|
|
90
|
+
matched_analytic_names = [
|
|
91
|
+
bucket["key"] for bucket in matched_analytics["aggregations"]["matched_analytics"]["buckets"]
|
|
92
|
+
]
|
|
93
|
+
else:
|
|
94
|
+
return None
|
|
95
|
+
|
|
96
|
+
else:
|
|
97
|
+
matched_analytic_names = []
|
|
98
|
+
|
|
99
|
+
return matched_analytic_names
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def setup_job(sched: BaseScheduler):
|
|
103
|
+
"""Initialize the retention job"""
|
|
104
|
+
if not config.system.retention.enabled:
|
|
105
|
+
if not DEBUG or config.system.type == "production":
|
|
106
|
+
logger.warning("Retention cronjob disabled! This is not recommended for a production settings.")
|
|
107
|
+
|
|
108
|
+
return
|
|
109
|
+
|
|
110
|
+
logger.debug("Initializing retention cronjob with cron %s", config.system.retention.crontab)
|
|
111
|
+
|
|
112
|
+
if DEBUG:
|
|
113
|
+
_kwargs: dict[str, Any] = {"next_run_time": datetime.now()}
|
|
114
|
+
else:
|
|
115
|
+
_kwargs = {}
|
|
116
|
+
|
|
117
|
+
if sched.get_job("retention"):
|
|
118
|
+
logger.debug("Retention job already running!")
|
|
119
|
+
return
|
|
120
|
+
|
|
121
|
+
sched.add_job(
|
|
122
|
+
id="retention",
|
|
123
|
+
func=execute,
|
|
124
|
+
trigger=CronTrigger.from_crontab(
|
|
125
|
+
config.system.retention.crontab, timezone=timezone(os.getenv("SCHEDULER_TZ", "America/Toronto"))
|
|
126
|
+
),
|
|
127
|
+
**_kwargs,
|
|
128
|
+
)
|
|
129
|
+
logger.debug("Initialization complete")
|