howler-api 4.1.0.dev1389__tar.gz → 4.1.0.dev1405__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-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/PKG-INFO +1 -1
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/api/v1/action.py +2 -2
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/api/v2/ingest.py +2 -2
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/datastore/collection.py +83 -9
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/mixins.py +10 -4
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/services/case_service.py +11 -10
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/services/dossier_service.py +2 -2
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/utils/compat.py +3 -3
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/pyproject.toml +1 -1
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/README.md +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/__init__.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/actions/__init__.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/actions/add_label.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/actions/add_to_bundle.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/actions/add_to_case.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/actions/change_field.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/actions/demote.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/actions/example_plugin.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/actions/prioritization.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/actions/promote.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/actions/remove_from_bundle.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/actions/remove_label.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/actions/transition.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/api/__init__.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/api/base.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/api/socket.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/api/v1/__init__.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/api/v1/analytic.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/api/v1/auth.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/api/v1/clue.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/api/v1/configs.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/api/v1/dossier.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/api/v1/help.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/api/v1/hit.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/api/v1/notebook.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/api/v1/overview.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/api/v1/search.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/api/v1/template.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/api/v1/tool.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/api/v1/user.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/api/v1/utils/__init__.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/api/v1/utils/etag.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/api/v1/utils/params.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/api/v1/view.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/api/v2/__init__.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/api/v2/case.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/api/v2/fuzzy.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/api/v2/search.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/app.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/common/README.md +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/common/__init__.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/common/classification.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/common/classification.yml +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/common/exceptions.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/common/loader.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/common/logging/__init__.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/common/logging/audit.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/common/logging/format.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/common/net.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/common/net_static.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/common/random_user.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/common/swagger.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/config.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/cronjobs/__init__.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/cronjobs/action_queue_worker.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/cronjobs/correlation.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/cronjobs/retention.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/cronjobs/view_cleanup.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/datastore/README.md +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/datastore/__init__.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/datastore/bulk.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/datastore/constants.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/datastore/exceptions.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/datastore/howler_store.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/datastore/migrations/fix_process.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/datastore/operations.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/datastore/schemas.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/datastore/store.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/datastore/support/__init__.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/datastore/support/build.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/datastore/support/schemas.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/datastore/types.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/datastore/utils.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/error.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/external/README.md +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/external/__init__.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/external/generate_mitre.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/external/generate_sigma_rules.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/external/generate_tlds.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/external/reindex_data.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/external/wipe_databases.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/gunicorn_config.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/healthz.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/helper/__init__.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/helper/azure.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/helper/discover.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/helper/hit.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/helper/oauth.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/helper/search.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/helper/workflow.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/helper/ws.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/README.md +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/__init__.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/base.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/charter.txt +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/constants.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/helper.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/howler_enum.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/__init__.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/action.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/analytic.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/assemblyline.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/aws.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/azure.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/case.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/cbs.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/clue.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/config.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/dossier.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/ecs/__init__.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/ecs/agent.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/ecs/autonomous_system.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/ecs/client.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/ecs/cloud.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/ecs/code_signature.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/ecs/container.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/ecs/dns.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/ecs/egress.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/ecs/elf.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/ecs/email.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/ecs/error.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/ecs/event.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/ecs/faas.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/ecs/file.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/ecs/geo.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/ecs/group.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/ecs/hash.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/ecs/host.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/ecs/http.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/ecs/ingress.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/ecs/interface.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/ecs/network.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/ecs/observer.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/ecs/organization.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/ecs/os.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/ecs/pe.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/ecs/process.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/ecs/registry.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/ecs/related.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/ecs/rule.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/ecs/server.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/ecs/threat.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/ecs/tls.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/ecs/url.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/ecs/user.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/ecs/user_agent.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/ecs/vulnerability.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/event.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/gcp.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/hit.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/howler_data.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/lead.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/localized_label.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/overview.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/pivot.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/record.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/template.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/user.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/models/view.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/random_data.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/odm/randomizer.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/patched.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/plugins/__init__.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/plugins/config.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/remote/__init__.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/remote/datatypes/README.md +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/remote/datatypes/__init__.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/remote/datatypes/counters.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/remote/datatypes/events.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/remote/datatypes/hash.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/remote/datatypes/lock.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/remote/datatypes/queues/__init__.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/remote/datatypes/queues/comms.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/remote/datatypes/queues/multi.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/remote/datatypes/queues/named.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/remote/datatypes/queues/priority.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/remote/datatypes/set.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/remote/datatypes/user_quota_tracker.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/security/__init__.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/security/socket.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/security/utils.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/services/__init__.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/services/action_service.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/services/analytic_service.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/services/auth_service.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/services/bundle_compat_service.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/services/comms_service.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/services/config_service.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/services/correlation_service.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/services/docs_service.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/services/event_service.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/services/fuzzy_service.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/services/hit_service.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/services/jwt_service.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/services/lucene_service.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/services/notebook_service.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/services/overview_service.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/services/search_service.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/services/template_service.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/services/user_service.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/services/viewer_service.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/telemetry.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/utils/__init__.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/utils/annotations.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/utils/chunk.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/utils/constants.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/utils/dict_utils.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/utils/indexes.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/utils/isotime.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/utils/list_utils.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/utils/lucene.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/utils/net_utils.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/utils/path.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/utils/socket_utils.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/utils/str_utils.py +0 -0
- {howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/utils/uid.py +0 -0
|
@@ -143,7 +143,7 @@ def update_action(id: str, user: User, **kwargs) -> Response:
|
|
|
143
143
|
|
|
144
144
|
ds = datastore()
|
|
145
145
|
|
|
146
|
-
existing_action = ds.action.get(id, as_obj=False)
|
|
146
|
+
existing_action, server_version = ds.action.get(id, as_obj=False, version=True)
|
|
147
147
|
|
|
148
148
|
if not existing_action:
|
|
149
149
|
return not_found(err="The specified automation does not exist")
|
|
@@ -166,7 +166,7 @@ def update_action(id: str, user: User, **kwargs) -> Response:
|
|
|
166
166
|
action_obj = Action(updated_action)
|
|
167
167
|
action_obj.action_id = id
|
|
168
168
|
|
|
169
|
-
ds.action.save(action_obj.action_id, action_obj, refresh=refresh)
|
|
169
|
+
ds.action.save(action_obj.action_id, action_obj, version=server_version, refresh=refresh)
|
|
170
170
|
except HowlerException as e:
|
|
171
171
|
return bad_request(err=str(e))
|
|
172
172
|
|
|
@@ -265,7 +265,7 @@ def overwrite(index: str, id: str, **kwargs):
|
|
|
265
265
|
|
|
266
266
|
ds = datastore()
|
|
267
267
|
|
|
268
|
-
record = ds[index].get(id, as_obj=False, version=
|
|
268
|
+
record, server_version = ds[index].get(id, as_obj=False, version=True)
|
|
269
269
|
if not record:
|
|
270
270
|
return not_found(err="Record %s does not exist" % id)
|
|
271
271
|
|
|
@@ -292,7 +292,7 @@ def overwrite(index: str, id: str, **kwargs):
|
|
|
292
292
|
ds[index].save(
|
|
293
293
|
id,
|
|
294
294
|
odm(new_record) if odm else new_record,
|
|
295
|
-
version=
|
|
295
|
+
version=server_version,
|
|
296
296
|
refresh=refresh,
|
|
297
297
|
)
|
|
298
298
|
|
|
@@ -16,6 +16,7 @@ from typing import Any, Callable, Dict, Generic, Literal, Optional, TypeVar, Uni
|
|
|
16
16
|
import elasticsearch
|
|
17
17
|
from datemath import dm
|
|
18
18
|
from datemath.helpers import DateMathException
|
|
19
|
+
from elastic_transport import ApiResponseMeta
|
|
19
20
|
from opentelemetry import trace
|
|
20
21
|
|
|
21
22
|
from howler import odm
|
|
@@ -1136,13 +1137,46 @@ class ESCollection(Generic[ModelType]):
|
|
|
1136
1137
|
|
|
1137
1138
|
return data
|
|
1138
1139
|
|
|
1140
|
+
def _search_exists(self, key) -> bool:
|
|
1141
|
+
"""Check document existence with an alias-safe search query."""
|
|
1142
|
+
result = self.with_retries(
|
|
1143
|
+
self.datastore.client.search,
|
|
1144
|
+
index=self.name,
|
|
1145
|
+
query={"ids": {"values": [key]}},
|
|
1146
|
+
size=0,
|
|
1147
|
+
track_total_hits=True,
|
|
1148
|
+
)
|
|
1149
|
+
total = result["hits"]["total"]
|
|
1150
|
+
return total["value"] > 0 if isinstance(total, dict) else total > 0
|
|
1151
|
+
|
|
1139
1152
|
def exists(self, key) -> bool:
|
|
1140
1153
|
"""Check if a document exists in the datastore.
|
|
1141
1154
|
|
|
1142
1155
|
:param key: key of the document to get from the datastore
|
|
1143
1156
|
:return: true/false depending if the document exists or not
|
|
1144
1157
|
"""
|
|
1145
|
-
|
|
1158
|
+
if self.ilm_config:
|
|
1159
|
+
return self._search_exists(key)
|
|
1160
|
+
|
|
1161
|
+
try:
|
|
1162
|
+
return cast(bool, self.with_retries(self.datastore.client.exists, index=self.name, id=key, _source=False))
|
|
1163
|
+
except elasticsearch.BadRequestError:
|
|
1164
|
+
logger.exception(
|
|
1165
|
+
"Single-document existence check failed on %s; falling back to an alias-safe search.",
|
|
1166
|
+
self.name,
|
|
1167
|
+
)
|
|
1168
|
+
return self._search_exists(key)
|
|
1169
|
+
|
|
1170
|
+
def _raise_document_not_found(self, key: str, result: Any) -> typing.NoReturn:
|
|
1171
|
+
"""Raise the same error shape as an Elasticsearch document lookup for an empty search."""
|
|
1172
|
+
meta = ApiResponseMeta(
|
|
1173
|
+
status=404,
|
|
1174
|
+
http_version="1.1",
|
|
1175
|
+
headers=cast(Any, {}),
|
|
1176
|
+
duration=0.0,
|
|
1177
|
+
node=cast(Any, None),
|
|
1178
|
+
)
|
|
1179
|
+
raise elasticsearch.exceptions.NotFoundError(f"Document with id {key} not found", meta, result)
|
|
1146
1180
|
|
|
1147
1181
|
@overload
|
|
1148
1182
|
def _get(self, key, retries, version: Literal[False]) -> dict[str, Any]: ...
|
|
@@ -1154,7 +1188,7 @@ class ESCollection(Generic[ModelType]):
|
|
|
1154
1188
|
"""Versioned get-save for atomic update has two paths:
|
|
1155
1189
|
1. Document doesn't exist at all. Create token will be returned for version.
|
|
1156
1190
|
This way only the first query to try and create the document will succeed.
|
|
1157
|
-
2. Document exists
|
|
1191
|
+
2. Document exists. A version string with the info needed to do a versioned save is returned.
|
|
1158
1192
|
|
|
1159
1193
|
The create token is needed to differentiate between "I'm saving a new
|
|
1160
1194
|
document non-atomic (version=None)" and "I'm saving a new document
|
|
@@ -1173,11 +1207,29 @@ class ESCollection(Generic[ModelType]):
|
|
|
1173
1207
|
done = False
|
|
1174
1208
|
while not done:
|
|
1175
1209
|
try:
|
|
1176
|
-
|
|
1210
|
+
if self.ilm_config:
|
|
1211
|
+
result = self.with_retries(
|
|
1212
|
+
self.datastore.client.search,
|
|
1213
|
+
index=self.name,
|
|
1214
|
+
query={"ids": {"values": [key]}},
|
|
1215
|
+
size=1,
|
|
1216
|
+
seq_no_primary_term=True,
|
|
1217
|
+
)
|
|
1218
|
+
hits = result["hits"]["hits"]
|
|
1219
|
+
if not hits:
|
|
1220
|
+
self._raise_document_not_found(key, result)
|
|
1221
|
+
doc = hits[0]
|
|
1222
|
+
else:
|
|
1223
|
+
doc = self.with_retries(self.datastore.client.get, index=self.name, id=key)
|
|
1224
|
+
|
|
1177
1225
|
if version:
|
|
1226
|
+
if self.ilm_config:
|
|
1227
|
+
version_token = f"{doc['_index']}---{doc['_seq_no']}---{doc['_primary_term']}"
|
|
1228
|
+
else:
|
|
1229
|
+
version_token = f"{doc['_seq_no']}---{doc['_primary_term']}"
|
|
1178
1230
|
return (
|
|
1179
1231
|
normalize_output(doc["_source"]),
|
|
1180
|
-
|
|
1232
|
+
version_token,
|
|
1181
1233
|
)
|
|
1182
1234
|
return normalize_output(doc["_source"])
|
|
1183
1235
|
except elasticsearch.exceptions.NotFoundError:
|
|
@@ -1196,6 +1248,19 @@ class ESCollection(Generic[ModelType]):
|
|
|
1196
1248
|
|
|
1197
1249
|
return None
|
|
1198
1250
|
|
|
1251
|
+
def _get_version_write_target(self, version: str) -> tuple[str, str, str]:
|
|
1252
|
+
"""Return the concrete write target and optimistic-concurrency values from a version token."""
|
|
1253
|
+
version_parts = version.split("---")
|
|
1254
|
+
if len(version_parts) == 3:
|
|
1255
|
+
index, seq_no, primary_term = version_parts
|
|
1256
|
+
return index, seq_no, primary_term
|
|
1257
|
+
|
|
1258
|
+
if len(version_parts) == 2:
|
|
1259
|
+
seq_no, primary_term = version_parts
|
|
1260
|
+
return self.name, seq_no, primary_term
|
|
1261
|
+
|
|
1262
|
+
raise DataStoreException(f"Invalid version token for {self.name}: {version!r}")
|
|
1263
|
+
|
|
1199
1264
|
@overload
|
|
1200
1265
|
def get(self, key, as_obj: Literal[True], version: Literal[True]) -> tuple[ModelType | None, str]: ...
|
|
1201
1266
|
|
|
@@ -1325,13 +1390,17 @@ class ESCollection(Generic[ModelType]):
|
|
|
1325
1390
|
|
|
1326
1391
|
saved_data["id"] = key
|
|
1327
1392
|
operation = "index"
|
|
1393
|
+
index = self.name
|
|
1328
1394
|
seq_no = None
|
|
1329
1395
|
primary_term = None
|
|
1330
1396
|
|
|
1397
|
+
if version is None and self.ilm_config:
|
|
1398
|
+
_, version = self.get_if_exists(key, as_obj=False, version=True)
|
|
1399
|
+
|
|
1331
1400
|
if version == CREATE_TOKEN:
|
|
1332
1401
|
operation = "create"
|
|
1333
1402
|
elif version:
|
|
1334
|
-
seq_no, primary_term =
|
|
1403
|
+
index, seq_no, primary_term = self._get_version_write_target(version)
|
|
1335
1404
|
|
|
1336
1405
|
if refresh == "true":
|
|
1337
1406
|
logger.warning(
|
|
@@ -1341,7 +1410,7 @@ class ESCollection(Generic[ModelType]):
|
|
|
1341
1410
|
try:
|
|
1342
1411
|
self.with_retries(
|
|
1343
1412
|
self.datastore.client.index,
|
|
1344
|
-
index=
|
|
1413
|
+
index=index,
|
|
1345
1414
|
id=key,
|
|
1346
1415
|
document=json.dumps(saved_data),
|
|
1347
1416
|
op_type=operation,
|
|
@@ -1541,15 +1610,16 @@ class ESCollection(Generic[ModelType]):
|
|
|
1541
1610
|
"""
|
|
1542
1611
|
operations = self._validate_operations(operations)
|
|
1543
1612
|
script = self._create_scripts_from_operations(operations)
|
|
1613
|
+
index = self.name
|
|
1544
1614
|
seq_no = None
|
|
1545
1615
|
primary_term = None
|
|
1546
1616
|
if version:
|
|
1547
|
-
seq_no, primary_term =
|
|
1617
|
+
index, seq_no, primary_term = self._get_version_write_target(version)
|
|
1548
1618
|
|
|
1549
1619
|
try:
|
|
1550
1620
|
res = self.with_retries(
|
|
1551
1621
|
self.datastore.client.update,
|
|
1552
|
-
index=
|
|
1622
|
+
index=index,
|
|
1553
1623
|
id=key,
|
|
1554
1624
|
script=script,
|
|
1555
1625
|
if_seq_no=seq_no,
|
|
@@ -1559,7 +1629,11 @@ class ESCollection(Generic[ModelType]):
|
|
|
1559
1629
|
)
|
|
1560
1630
|
return (
|
|
1561
1631
|
res["result"] == "updated",
|
|
1562
|
-
|
|
1632
|
+
(
|
|
1633
|
+
f"{res['_index']}---{res['_seq_no']}---{res['_primary_term']}"
|
|
1634
|
+
if self.ilm_config
|
|
1635
|
+
else f"{res['_seq_no']}---{res['_primary_term']}"
|
|
1636
|
+
),
|
|
1563
1637
|
)
|
|
1564
1638
|
except elasticsearch.NotFoundError as e:
|
|
1565
1639
|
logger.warning("Update - elasticsearch.NotFoundError: %s %s", e.message, e.info)
|
|
@@ -15,6 +15,7 @@ from howler.common.exceptions import HowlerRuntimeError
|
|
|
15
15
|
from howler.common.loader import datastore
|
|
16
16
|
from howler.datastore.collection import ESCollection
|
|
17
17
|
from howler.odm.base import Model
|
|
18
|
+
from howler.utils.compat import Self
|
|
18
19
|
|
|
19
20
|
ModelType = TypeVar("ModelType", bound=Model)
|
|
20
21
|
|
|
@@ -28,10 +29,10 @@ class _ObjectsDescriptor(Generic[ModelType]):
|
|
|
28
29
|
"""
|
|
29
30
|
|
|
30
31
|
@overload
|
|
31
|
-
def __get__(self, obj: None, objtype: type[ModelType]) -> ESCollection[ModelType]: ...
|
|
32
|
+
def __get__(self: Self, obj: None, objtype: type[ModelType]) -> ESCollection[ModelType]: ...
|
|
32
33
|
|
|
33
34
|
@overload
|
|
34
|
-
def __get__(self, obj: ModelType, objtype: type[ModelType]) -> ESCollection[ModelType]: ...
|
|
35
|
+
def __get__(self: Self, obj: ModelType, objtype: type[ModelType]) -> ESCollection[ModelType]: ...
|
|
35
36
|
|
|
36
37
|
def __get__(self, obj: ModelType | None, objtype: type[ModelType] | None = None) -> ESCollection[ModelType]:
|
|
37
38
|
"""Return the ESCollection for the owner class.
|
|
@@ -82,7 +83,11 @@ class DatastoreMixin(Generic[ModelType]):
|
|
|
82
83
|
"""
|
|
83
84
|
return datastore()
|
|
84
85
|
|
|
85
|
-
def save(
|
|
86
|
+
def save(
|
|
87
|
+
self: Self,
|
|
88
|
+
refresh: Literal["true", "false", "wait_for"] | None = None,
|
|
89
|
+
version: str | None = None,
|
|
90
|
+
) -> bool:
|
|
86
91
|
"""Persist the current model instance to the datastore.
|
|
87
92
|
|
|
88
93
|
Determines the target index from the lowercase class name, extracts the
|
|
@@ -96,5 +101,6 @@ class DatastoreMixin(Generic[ModelType]):
|
|
|
96
101
|
index_name = self.__class__.__name__.lower()
|
|
97
102
|
id_field = self.__class__._Model__id_field # type: ignore[attr-defined]
|
|
98
103
|
current_id = attrgetter(id_field)(self)
|
|
104
|
+
collection = self.ds[index_name]
|
|
99
105
|
|
|
100
|
-
return
|
|
106
|
+
return collection.save(current_id, self, version=version, refresh=refresh)
|
|
@@ -19,6 +19,7 @@ from howler.common.exceptions import (
|
|
|
19
19
|
from howler.common.loader import APP_NAME, datastore
|
|
20
20
|
from howler.common.logging import get_logger
|
|
21
21
|
from howler.config import CLASSIFICATION
|
|
22
|
+
from howler.datastore.collection import CREATE_TOKEN
|
|
22
23
|
from howler.datastore.exceptions import DataStoreException
|
|
23
24
|
from howler.odm.models.case import Case, CaseItem, CaseItemTypes, CaseLog, CaseRule
|
|
24
25
|
from howler.odm.models.ecs.related import Related
|
|
@@ -56,7 +57,7 @@ def create_case(
|
|
|
56
57
|
|
|
57
58
|
case = Case(case_data)
|
|
58
59
|
case.log = [CaseLog({"timestamp": "NOW", "explanation": "Case created", "user": user.uname if user else "system"})]
|
|
59
|
-
case.save(refresh="wait_for")
|
|
60
|
+
case.save(refresh="wait_for", version=CREATE_TOKEN)
|
|
60
61
|
CREATED_CASES.inc()
|
|
61
62
|
|
|
62
63
|
for item in items:
|
|
@@ -693,18 +694,18 @@ def remove_case_items( # noqa: C901
|
|
|
693
694
|
|
|
694
695
|
# Resolve backing objects for back-reference cleanup
|
|
695
696
|
items_to_remove = [items_by_id[iid] for iid in ids_to_remove if iid in items_by_id]
|
|
696
|
-
backing_objs: list[Hit | Event] = []
|
|
697
|
+
backing_objs: list[tuple[Hit | Event, str]] = []
|
|
697
698
|
for item in items_to_remove:
|
|
698
699
|
if item.type in [CaseItemTypes.HIT, CaseItemTypes.EVENT]:
|
|
699
|
-
obj = ds[item.type].get(item.value)
|
|
700
|
+
obj, version = ds[item.type].get(item.value, as_obj=True, version=True)
|
|
700
701
|
if obj:
|
|
701
|
-
backing_objs.append(obj)
|
|
702
|
+
backing_objs.append((obj, version))
|
|
702
703
|
|
|
703
704
|
case.items = [item for item in case.items if item.id not in ids_to_remove]
|
|
704
705
|
|
|
705
|
-
for backing_obj in backing_objs:
|
|
706
|
+
for backing_obj, version in backing_objs:
|
|
706
707
|
remove_backreference(backing_obj, case.case_id)
|
|
707
|
-
backing_obj.save()
|
|
708
|
+
backing_obj.save(version=version)
|
|
708
709
|
|
|
709
710
|
recompute_case_metadata(case)
|
|
710
711
|
|
|
@@ -745,7 +746,7 @@ def append_hit(case: Case, item: CaseItem, refresh: Literal["true", "false", "wa
|
|
|
745
746
|
"""
|
|
746
747
|
ds = datastore()
|
|
747
748
|
|
|
748
|
-
hit = ds.hit.get(item.value)
|
|
749
|
+
hit, version = ds.hit.get(item.value, as_obj=True, version=True)
|
|
749
750
|
if hit is None:
|
|
750
751
|
raise NotFoundException(f"Hit {item.value} not found, cannot be added to case")
|
|
751
752
|
|
|
@@ -754,7 +755,7 @@ def append_hit(case: Case, item: CaseItem, refresh: Literal["true", "false", "wa
|
|
|
754
755
|
case.items.append(item)
|
|
755
756
|
|
|
756
757
|
add_backreference(hit, case.case_id)
|
|
757
|
-
hit.save()
|
|
758
|
+
hit.save(version=version)
|
|
758
759
|
|
|
759
760
|
recompute_case_metadata(case)
|
|
760
761
|
if not case.save(refresh=refresh): # pragma: no cover
|
|
@@ -783,7 +784,7 @@ def append_event(case: Case, item: CaseItem, refresh: Literal["true", "false", "
|
|
|
783
784
|
"""
|
|
784
785
|
ds = datastore()
|
|
785
786
|
|
|
786
|
-
event = ds.event.get(key=item.value)
|
|
787
|
+
event, version = ds.event.get(key=item.value, as_obj=True, version=True)
|
|
787
788
|
|
|
788
789
|
if event is None:
|
|
789
790
|
raise NotFoundException(f"Event {item.value} not found, cannot be added to case")
|
|
@@ -793,7 +794,7 @@ def append_event(case: Case, item: CaseItem, refresh: Literal["true", "false", "
|
|
|
793
794
|
case.items.append(item)
|
|
794
795
|
|
|
795
796
|
add_backreference(event, case.case_id)
|
|
796
|
-
event.save()
|
|
797
|
+
event.save(version=version)
|
|
797
798
|
|
|
798
799
|
recompute_case_metadata(case)
|
|
799
800
|
if not case.save(refresh=refresh): # pragma: no cover
|
|
@@ -199,7 +199,7 @@ def update_dossier( # noqa: C901
|
|
|
199
199
|
storage = datastore()
|
|
200
200
|
|
|
201
201
|
# Retrieve the existing dossier for access control checks
|
|
202
|
-
existing_dossier
|
|
202
|
+
existing_dossier, server_version = get_dossier(dossier_id, as_odm=True, version=True)
|
|
203
203
|
|
|
204
204
|
# Enforce access control for personal dossiers
|
|
205
205
|
# Only the owner or admin users can modify personal dossiers
|
|
@@ -228,7 +228,7 @@ def update_dossier( # noqa: C901
|
|
|
228
228
|
# Merge the new data with existing dossier data
|
|
229
229
|
new_data = Dossier(cast(dict, merge({}, existing_dossier.as_primitives(), dossier_data)))
|
|
230
230
|
|
|
231
|
-
storage.dossier.save(dossier_id, new_data, refresh=refresh)
|
|
231
|
+
storage.dossier.save(dossier_id, new_data, version=server_version, refresh=refresh)
|
|
232
232
|
|
|
233
233
|
return new_data
|
|
234
234
|
except SearchException:
|
|
@@ -7,7 +7,7 @@ import sys
|
|
|
7
7
|
|
|
8
8
|
if sys.version_info >= (3, 11):
|
|
9
9
|
from enum import StrEnum
|
|
10
|
-
from typing import NotRequired, TypedDict
|
|
10
|
+
from typing import NotRequired, Self, TypedDict
|
|
11
11
|
else:
|
|
12
12
|
from enum import Enum as _Enum
|
|
13
13
|
|
|
@@ -22,6 +22,6 @@ else:
|
|
|
22
22
|
|
|
23
23
|
# typing_extensions.TypedDict supports Generic[T] mixing on Python < 3.11;
|
|
24
24
|
# the stdlib version does not gain that until 3.11.
|
|
25
|
-
from typing_extensions import NotRequired, TypedDict # noqa: F401
|
|
25
|
+
from typing_extensions import NotRequired, Self, TypedDict # noqa: F401
|
|
26
26
|
|
|
27
|
-
__all__ = ["NotRequired", "StrEnum", "TypedDict"]
|
|
27
|
+
__all__ = ["NotRequired", "StrEnum", "TypedDict", "Self"]
|
|
@@ -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.1.0.
|
|
155
|
+
version = "4.1.0.dev1405"
|
|
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
|
{howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/cronjobs/action_queue_worker.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
|
{howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/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
|
{howler_api-4.1.0.dev1389 → howler_api-4.1.0.dev1405}/howler/external/generate_sigma_rules.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
|