howler-api 4.0.0.dev652__tar.gz → 4.0.0.dev664__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/PKG-INFO +1 -1
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/api/v2/case.py +27 -23
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/view.py +8 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/services/case_service.py +103 -67
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/pyproject.toml +1 -1
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/README.md +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/__init__.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/actions/__init__.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/actions/add_label.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/actions/change_field.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/actions/demote.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/actions/example_plugin.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/actions/prioritization.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/actions/promote.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/actions/remove_label.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/actions/transition.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/api/__init__.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/api/base.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/api/socket.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/api/v1/__init__.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/api/v1/action.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/api/v1/analytic.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/api/v1/auth.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/api/v1/clue.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/api/v1/configs.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/api/v1/dossier.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/api/v1/help.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/api/v1/hit.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/api/v1/notebook.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/api/v1/overview.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/api/v1/search.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/api/v1/template.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/api/v1/tool.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/api/v1/user.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/api/v1/utils/__init__.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/api/v1/utils/etag.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/api/v1/view.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/api/v2/__init__.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/api/v2/ingest.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/api/v2/search.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/app.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/common/README.md +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/common/__init__.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/common/classification.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/common/classification.yml +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/common/exceptions.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/common/loader.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/common/logging/__init__.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/common/logging/audit.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/common/logging/format.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/common/net.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/common/net_static.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/common/random_user.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/common/swagger.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/config.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/cronjobs/__init__.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/cronjobs/retention.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/cronjobs/view_cleanup.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/datastore/README.md +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/datastore/__init__.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/datastore/bulk.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/datastore/collection.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/datastore/constants.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/datastore/exceptions.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/datastore/howler_store.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/datastore/migrations/fix_process.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/datastore/operations.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/datastore/schemas.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/datastore/store.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/datastore/support/__init__.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/datastore/support/build.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/datastore/support/schemas.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/datastore/types.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/error.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/external/__init__.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/external/generate_mitre.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/external/generate_sigma_rules.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/external/generate_tlds.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/external/reindex_data.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/external/wipe_databases.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/gunicorn_config.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/healthz.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/helper/__init__.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/helper/azure.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/helper/discover.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/helper/hit.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/helper/oauth.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/helper/search.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/helper/workflow.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/helper/ws.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/README.md +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/__init__.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/base.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/charter.txt +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/constants.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/helper.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/howler_enum.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/__init__.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/action.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/analytic.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/assemblyline.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/aws.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/azure.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/case.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/cbs.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/clue.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/config.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/dossier.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/__init__.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/agent.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/autonomous_system.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/client.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/cloud.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/code_signature.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/container.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/dns.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/egress.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/elf.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/email.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/error.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/event.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/faas.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/file.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/geo.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/group.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/hash.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/host.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/http.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/ingress.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/interface.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/network.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/observer.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/organization.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/os.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/pe.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/process.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/registry.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/related.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/rule.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/server.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/threat.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/tls.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/url.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/user.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/user_agent.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/ecs/vulnerability.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/gcp.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/hit.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/howler_data.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/lead.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/localized_label.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/observable.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/overview.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/pivot.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/record.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/template.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/models/user.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/random_data.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/odm/randomizer.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/patched.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/plugins/__init__.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/plugins/config.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/remote/__init__.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/remote/datatypes/README.md +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/remote/datatypes/__init__.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/remote/datatypes/counters.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/remote/datatypes/events.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/remote/datatypes/hash.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/remote/datatypes/lock.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/remote/datatypes/queues/__init__.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/remote/datatypes/queues/comms.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/remote/datatypes/queues/multi.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/remote/datatypes/queues/named.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/remote/datatypes/queues/priority.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/remote/datatypes/set.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/remote/datatypes/user_quota_tracker.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/security/__init__.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/security/socket.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/security/utils.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/services/__init__.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/services/action_service.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/services/analytic_service.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/services/auth_service.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/services/config_service.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/services/docs_service.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/services/dossier_service.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/services/event_service.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/services/hit_service.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/services/jwt_service.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/services/lucene_service.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/services/notebook_service.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/services/observable_service.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/services/overview_service.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/services/search_service.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/services/template_service.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/services/user_service.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/utils/__init__.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/utils/annotations.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/utils/chunk.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/utils/compat.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/utils/dict_utils.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/utils/isotime.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/utils/list_utils.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/utils/lucene.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/utils/path.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/utils/socket_utils.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/utils/str_utils.py +0 -0
- {howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/howler/utils/uid.py +0 -0
|
@@ -7,6 +7,7 @@ from howler.common.loader import datastore
|
|
|
7
7
|
from howler.common.logging import get_logger
|
|
8
8
|
from howler.common.swagger import generate_swagger_docs
|
|
9
9
|
from howler.datastore.exceptions import DataStoreException
|
|
10
|
+
from howler.odm.models.case import CaseItem
|
|
10
11
|
from howler.odm.models.user import User
|
|
11
12
|
from howler.security import api_login
|
|
12
13
|
from howler.services import case_service
|
|
@@ -217,7 +218,8 @@ def append_item(id: str, user: User, **kwargs): # noqa: C901
|
|
|
217
218
|
Data Block:
|
|
218
219
|
{
|
|
219
220
|
"type": "hit", # Type of item to append: "hit", "observable", "case", "table", "lead", or "reference"
|
|
220
|
-
"value": "item-id-123" # The ID or reference value for the item
|
|
221
|
+
"value": "item-id-123" # The ID or reference value for the item,
|
|
222
|
+
"path": "example/path/Title"
|
|
221
223
|
}
|
|
222
224
|
|
|
223
225
|
Result Example:
|
|
@@ -230,56 +232,58 @@ def append_item(id: str, user: User, **kwargs): # noqa: C901
|
|
|
230
232
|
except UnsupportedMediaType:
|
|
231
233
|
return bad_request(err="Invalid JSON body")
|
|
232
234
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
if "type" not in body:
|
|
237
|
-
return bad_request(err="Case 'type' missing")
|
|
235
|
+
for field in ["value", "type", "path"]:
|
|
236
|
+
if field not in body:
|
|
237
|
+
return bad_request(err=f"CaseItem '{field}' is required")
|
|
238
238
|
|
|
239
239
|
try:
|
|
240
|
-
case_service.append_case_item(
|
|
241
|
-
id, item_type=body["type"], item_value=body["value"], item_path=body.get("path", None)
|
|
242
|
-
)
|
|
240
|
+
return ok(case_service.append_case_item(id, item=CaseItem(body)))
|
|
243
241
|
except DataStoreException as e:
|
|
244
242
|
logger.exception("Save Error")
|
|
245
243
|
return internal_error(err=str(e))
|
|
246
244
|
except InvalidDataException as e:
|
|
247
245
|
return bad_request(err=str(e))
|
|
248
246
|
|
|
249
|
-
return ok()
|
|
250
|
-
|
|
251
247
|
|
|
252
248
|
@generate_swagger_docs()
|
|
253
|
-
@case_api.route("/<
|
|
249
|
+
@case_api.route("/<case_id>/items", methods=["DELETE"])
|
|
254
250
|
@api_login(required_priv=["R", "W"])
|
|
255
|
-
def delete_item(
|
|
256
|
-
"""Delete
|
|
251
|
+
def delete_item(case_id: str, **kwargs):
|
|
252
|
+
"""Delete one or more items from a case
|
|
257
253
|
|
|
258
|
-
This endpoint removes
|
|
254
|
+
This endpoint removes items from a case's items list. If an item is a hit or
|
|
259
255
|
observable, the bidirectional relationship is cleaned up - the case reference will
|
|
260
256
|
be removed from the backing object's related.cases list.
|
|
261
257
|
|
|
262
258
|
Variables:
|
|
263
|
-
|
|
264
|
-
value => The value of the item to delete (must match the item's value field)
|
|
259
|
+
case_id => The id of the case to modify
|
|
265
260
|
|
|
266
261
|
Arguments:
|
|
267
262
|
None
|
|
268
263
|
|
|
269
264
|
Data Block:
|
|
270
|
-
|
|
265
|
+
{
|
|
266
|
+
"values": ["item-id-123", "item-id-456"] # The values of the items to delete
|
|
267
|
+
}
|
|
271
268
|
|
|
272
269
|
Result Example:
|
|
273
270
|
{
|
|
274
|
-
|
|
271
|
+
...case # The updated case data
|
|
275
272
|
}
|
|
276
273
|
"""
|
|
274
|
+
body = request.json
|
|
275
|
+
|
|
276
|
+
if not body or not isinstance(body, dict) or "values" not in body:
|
|
277
|
+
return bad_request(err="Request body must be a JSON object with a 'values' field.")
|
|
278
|
+
|
|
279
|
+
values = body["values"]
|
|
280
|
+
if not isinstance(values, list) or not values:
|
|
281
|
+
return bad_request(err="'values' must be a non-empty list.")
|
|
282
|
+
|
|
277
283
|
try:
|
|
278
|
-
case_service.
|
|
284
|
+
return ok(case_service.remove_case_items(case_id, values))
|
|
279
285
|
except DataStoreException as e:
|
|
280
286
|
logger.exception("Save Error")
|
|
281
287
|
return internal_error(err=str(e))
|
|
282
|
-
except InvalidDataException as e:
|
|
288
|
+
except (InvalidDataException, NotFoundException) as e:
|
|
283
289
|
return bad_request(err=str(e))
|
|
284
|
-
|
|
285
|
-
return ok()
|
|
@@ -11,9 +11,17 @@ class Settings(odm.Model):
|
|
|
11
11
|
)
|
|
12
12
|
|
|
13
13
|
|
|
14
|
+
DEFAULT_INDEXES = ["hit"]
|
|
15
|
+
|
|
16
|
+
|
|
14
17
|
@odm.model(index=True, store=True, description="Model of views")
|
|
15
18
|
class View(odm.Model):
|
|
16
19
|
view_id: str = odm.UUID(description="A UUID for this view")
|
|
20
|
+
indexes: list[str] = odm.List(
|
|
21
|
+
odm.Keyword(),
|
|
22
|
+
default=DEFAULT_INDEXES,
|
|
23
|
+
description="What indexes this view applies to.",
|
|
24
|
+
)
|
|
17
25
|
title: str = odm.CaseInsensitiveKeyword(description="The name of this view.")
|
|
18
26
|
query: str = odm.Keyword(description="The query to run in this view.")
|
|
19
27
|
sort: str = odm.Keyword(description="The sorting to use with this view.", optional=True)
|
|
@@ -222,13 +222,13 @@ def update_case(case_id: str, case_data: dict[str, Any], user: User) -> Case:
|
|
|
222
222
|
|
|
223
223
|
|
|
224
224
|
@overload
|
|
225
|
-
def append_case_item(case_id: str, item: CaseItem): ...
|
|
225
|
+
def append_case_item(case_id: str, item: CaseItem) -> Case: ...
|
|
226
226
|
|
|
227
227
|
|
|
228
228
|
@overload
|
|
229
229
|
def append_case_item(
|
|
230
230
|
case_id: str, item: None = None, item_type: str = ..., item_value: str = ..., item_path: str = ...
|
|
231
|
-
): ...
|
|
231
|
+
) -> Case: ...
|
|
232
232
|
|
|
233
233
|
|
|
234
234
|
def append_case_item( # noqa: C901
|
|
@@ -236,8 +236,8 @@ def append_case_item( # noqa: C901
|
|
|
236
236
|
item: CaseItem | None = None,
|
|
237
237
|
item_type: str | None = None,
|
|
238
238
|
item_value: str | None = None,
|
|
239
|
-
item_path: str = "related
|
|
240
|
-
):
|
|
239
|
+
item_path: str = "related",
|
|
240
|
+
) -> Case:
|
|
241
241
|
"""Append an item to a case, dispatching to the appropriate handler based on item type.
|
|
242
242
|
|
|
243
243
|
Can be called either with a pre-built CaseItem object or with individual
|
|
@@ -251,12 +251,13 @@ def append_case_item( # noqa: C901
|
|
|
251
251
|
"table", "lead", "reference"). Required if item is not provided.
|
|
252
252
|
item_value: The value/identifier of the item to append. Required if item
|
|
253
253
|
is not provided.
|
|
254
|
-
item_path:
|
|
255
|
-
|
|
254
|
+
item_path: Path for organizing the item within the case. Must not end
|
|
255
|
+
with a trailing "/".
|
|
256
256
|
|
|
257
257
|
Raises:
|
|
258
258
|
InvalidDataException: If item is not provided and item_type or item_value
|
|
259
|
-
are missing, or if item_type is not a valid CaseItemTypes value
|
|
259
|
+
are missing, or if item_type is not a valid CaseItemTypes value, or
|
|
260
|
+
if the resolved item path ends with a trailing "/".
|
|
260
261
|
"""
|
|
261
262
|
if item is None:
|
|
262
263
|
if not all([item_type, item_value]):
|
|
@@ -266,28 +267,31 @@ def append_case_item( # noqa: C901
|
|
|
266
267
|
raise InvalidDataException(f"Invalid item type: {item_type}, valid types are: {', '.join(CaseItemTypes)}")
|
|
267
268
|
|
|
268
269
|
if not item_path:
|
|
269
|
-
item_path = "related
|
|
270
|
+
item_path = "related"
|
|
270
271
|
|
|
271
272
|
item = CaseItem({"type": item_type, "value": item_value, "path": item_path})
|
|
272
273
|
|
|
274
|
+
if item.path.endswith("/"):
|
|
275
|
+
raise InvalidDataException("item path must not end with a trailing '/'")
|
|
276
|
+
|
|
273
277
|
match item.type:
|
|
274
278
|
case CaseItemTypes.HIT:
|
|
275
|
-
append_hit(case_id, item)
|
|
279
|
+
return append_hit(case_id, item)
|
|
276
280
|
case CaseItemTypes.OBSERVABLE:
|
|
277
|
-
append_observable(case_id, item)
|
|
281
|
+
return append_observable(case_id, item)
|
|
278
282
|
case CaseItemTypes.CASE:
|
|
279
|
-
append_case(case_id, item)
|
|
283
|
+
return append_case(case_id, item)
|
|
280
284
|
case CaseItemTypes.TABLE:
|
|
281
|
-
append_table(case_id, item)
|
|
285
|
+
return append_table(case_id, item)
|
|
282
286
|
case CaseItemTypes.LEAD:
|
|
283
|
-
append_lead(case_id, item)
|
|
287
|
+
return append_lead(case_id, item)
|
|
284
288
|
case CaseItemTypes.REFERENCE:
|
|
285
|
-
append_reference(case_id, item)
|
|
289
|
+
return append_reference(case_id, item)
|
|
286
290
|
case _:
|
|
287
291
|
raise InvalidDataException(f"Unsupported item type: {item_type}")
|
|
288
292
|
|
|
289
293
|
|
|
290
|
-
def append_hit(case_id: str, item: CaseItem):
|
|
294
|
+
def append_hit(case_id: str, item: CaseItem) -> Case:
|
|
291
295
|
"""Append a hit item to a case and create a back-reference on the hit.
|
|
292
296
|
|
|
293
297
|
Validates that the case and hit both exist and that the hit is not already
|
|
@@ -306,37 +310,36 @@ def append_hit(case_id: str, item: CaseItem):
|
|
|
306
310
|
"""
|
|
307
311
|
ds = datastore()
|
|
308
312
|
|
|
309
|
-
|
|
313
|
+
_case = ds.case.get(case_id)
|
|
310
314
|
|
|
311
|
-
if
|
|
315
|
+
if _case is None:
|
|
312
316
|
raise NotFoundException(f"Case {case_id} does not exist")
|
|
313
317
|
|
|
314
|
-
if any(item.value == case_item["value"] for case_item in
|
|
318
|
+
if any(item.value == case_item["value"] for case_item in _case.items):
|
|
315
319
|
raise InvalidDataException(f"Hit {item.value} already exists in case {case_id}")
|
|
316
320
|
|
|
317
|
-
hit = ds.hit.get(
|
|
321
|
+
hit = ds.hit.get(item.value)
|
|
318
322
|
|
|
319
323
|
if hit is None:
|
|
320
324
|
raise NotFoundException(f"Hit {item.value} not found, cannot be added to case")
|
|
321
325
|
|
|
322
|
-
|
|
323
|
-
item.path = f"alerts/{hit.howler.analytic} ({hit.howler.id})"
|
|
326
|
+
_case.items.append(item)
|
|
324
327
|
|
|
325
|
-
case.
|
|
328
|
+
if not ds.case.save(_case.case_id, _case):
|
|
329
|
+
raise DataStoreException(f"Failed to save {_case.case_id} with new item {item.value}")
|
|
326
330
|
|
|
327
|
-
|
|
328
|
-
raise DataStoreException(f"Failed to save {case.case_id} with new item {item.value}")
|
|
331
|
+
_add_backreference(hit, _case.case_id)
|
|
329
332
|
|
|
330
|
-
|
|
331
|
-
_sync_case_metadata(case_id)
|
|
333
|
+
_sync_case_metadata(_case.case_id)
|
|
332
334
|
|
|
335
|
+
return _case
|
|
333
336
|
|
|
334
|
-
|
|
337
|
+
|
|
338
|
+
def append_observable(case_id: str, item: CaseItem) -> Case:
|
|
335
339
|
"""Append an observable item to a case and create a back-reference on the observable.
|
|
336
340
|
|
|
337
341
|
Validates that the case and observable both exist and that the observable is
|
|
338
|
-
not already present in the case.
|
|
339
|
-
observable's ID, then persists the updated case and adds a back-reference
|
|
342
|
+
not already present in the case. It then persists the updated case and adds a back-reference
|
|
340
343
|
from the observable to the case.
|
|
341
344
|
|
|
342
345
|
Args:
|
|
@@ -363,24 +366,22 @@ def append_observable(case_id: str, item: CaseItem):
|
|
|
363
366
|
if observable is None:
|
|
364
367
|
raise NotFoundException(f"Observable {item.value} not found, cannot be added to case")
|
|
365
368
|
|
|
366
|
-
if item.path == "related/":
|
|
367
|
-
item.path = f"observables/{observable.howler.id}"
|
|
368
|
-
|
|
369
369
|
_case.items.append(item)
|
|
370
370
|
|
|
371
|
-
if not
|
|
371
|
+
if not ds.case.save(_case.case_id, _case):
|
|
372
372
|
raise DataStoreException(f"Failed to save {_case.case_id} with new item {item.value}")
|
|
373
373
|
|
|
374
374
|
_add_backreference(observable, _case.case_id)
|
|
375
375
|
_sync_case_metadata(case_id)
|
|
376
376
|
|
|
377
|
+
return _case
|
|
378
|
+
|
|
377
379
|
|
|
378
|
-
def append_case(case_id: str, item: CaseItem):
|
|
380
|
+
def append_case(case_id: str, item: CaseItem) -> Case:
|
|
379
381
|
"""Append a case reference item to a case.
|
|
380
382
|
|
|
381
383
|
Validates that both the parent case and the referenced case exist, and that
|
|
382
|
-
the referenced case is not already present in the parent case.
|
|
383
|
-
item's path to include the referenced case's ID, then persists the updated
|
|
384
|
+
the referenced case is not already present in the parent case. It then persists the updated
|
|
384
385
|
parent case.
|
|
385
386
|
|
|
386
387
|
Args:
|
|
@@ -394,7 +395,7 @@ def append_case(case_id: str, item: CaseItem):
|
|
|
394
395
|
"""
|
|
395
396
|
ds = datastore()
|
|
396
397
|
|
|
397
|
-
_case = ds.case.get(
|
|
398
|
+
_case = ds.case.get(case_id)
|
|
398
399
|
|
|
399
400
|
if _case is None:
|
|
400
401
|
raise NotFoundException(f"Case {case_id} does not exist")
|
|
@@ -407,18 +408,15 @@ def append_case(case_id: str, item: CaseItem):
|
|
|
407
408
|
if referenced_case is None:
|
|
408
409
|
raise NotFoundException(f"Referenced case {item.value} not found, cannot be added to case")
|
|
409
410
|
|
|
410
|
-
if item.path == "related/":
|
|
411
|
-
item.path = "cases/"
|
|
412
|
-
|
|
413
|
-
item.path += f"{referenced_case.case_id}"
|
|
414
|
-
|
|
415
411
|
_case.items.append(item)
|
|
416
412
|
|
|
417
413
|
if not datastore().case.save(_case.case_id, _case):
|
|
418
414
|
raise DataStoreException(f"Failed to save {_case.case_id} with new item {item.value}")
|
|
419
415
|
|
|
416
|
+
return _case
|
|
417
|
+
|
|
420
418
|
|
|
421
|
-
def append_table(case_id: str, item: CaseItem):
|
|
419
|
+
def append_table(case_id: str, item: CaseItem) -> Case:
|
|
422
420
|
"""Append a table item to a case.
|
|
423
421
|
|
|
424
422
|
Not yet implemented.
|
|
@@ -433,7 +431,7 @@ def append_table(case_id: str, item: CaseItem):
|
|
|
433
431
|
raise NotImplementedError
|
|
434
432
|
|
|
435
433
|
|
|
436
|
-
def append_lead(case_id: str, item: CaseItem):
|
|
434
|
+
def append_lead(case_id: str, item: CaseItem) -> Case:
|
|
437
435
|
"""Append a lead item to a case.
|
|
438
436
|
|
|
439
437
|
Not yet implemented.
|
|
@@ -448,19 +446,37 @@ def append_lead(case_id: str, item: CaseItem):
|
|
|
448
446
|
raise NotImplementedError
|
|
449
447
|
|
|
450
448
|
|
|
451
|
-
def append_reference(case_id: str, item: CaseItem):
|
|
452
|
-
"""Append
|
|
449
|
+
def append_reference(case_id: str, item: CaseItem) -> Case:
|
|
450
|
+
"""Append an external reference item to a case.
|
|
453
451
|
|
|
454
|
-
|
|
452
|
+
Validates that the case exists and that the reference URL is not already
|
|
453
|
+
present in the case. It then persists the updated case.
|
|
455
454
|
|
|
456
455
|
Args:
|
|
457
456
|
case_id: Unique identifier of the case to append the reference to.
|
|
458
|
-
item: A CaseItem
|
|
457
|
+
item: A CaseItem whose ``value`` is the external URL to reference.
|
|
459
458
|
|
|
460
459
|
Raises:
|
|
461
|
-
|
|
460
|
+
NotFoundException: If the case does not exist.
|
|
461
|
+
InvalidDataException: If the reference URL is already present in the case.
|
|
462
|
+
DataStoreException: If saving the updated case fails.
|
|
462
463
|
"""
|
|
463
|
-
|
|
464
|
+
ds = datastore()
|
|
465
|
+
|
|
466
|
+
_case = ds.case.get_if_exists(key=case_id, as_obj=True)
|
|
467
|
+
|
|
468
|
+
if _case is None:
|
|
469
|
+
raise NotFoundException(f"Case {case_id} does not exist")
|
|
470
|
+
|
|
471
|
+
if any(item.value == case_item["value"] for case_item in _case.items):
|
|
472
|
+
raise InvalidDataException(f"Reference {item.value} already exists in case {case_id}")
|
|
473
|
+
|
|
474
|
+
_case.items.append(item)
|
|
475
|
+
|
|
476
|
+
if not datastore().case.save(_case.case_id, _case):
|
|
477
|
+
raise DataStoreException(f"Failed to save {_case.case_id} with new item {item.value}")
|
|
478
|
+
|
|
479
|
+
return _case
|
|
464
480
|
|
|
465
481
|
|
|
466
482
|
def _collect_indicators_from_related(related: Related | None) -> set[str]:
|
|
@@ -575,20 +591,26 @@ def remove_backreference(backing_obj: Hit | Observable | None, case_id: str):
|
|
|
575
591
|
datastore()[backing_obj.__class__.__name__.lower()].save(backing_obj.howler.id, backing_obj)
|
|
576
592
|
|
|
577
593
|
|
|
578
|
-
def
|
|
579
|
-
"""Remove
|
|
594
|
+
def remove_case_items(case_id: str, values: list[str]):
|
|
595
|
+
"""Remove one or more items from a case in a single atomic operation.
|
|
580
596
|
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
the
|
|
597
|
+
Validates that every requested value exists within the case before making
|
|
598
|
+
any modifications. If any value is missing the call raises NotFoundException
|
|
599
|
+
without altering the case. When all values are confirmed, all matching
|
|
600
|
+
items are removed in memory, the case is persisted once, and back-references
|
|
601
|
+
are cleaned up from any associated hits or observables.
|
|
584
602
|
|
|
585
603
|
Args:
|
|
586
|
-
case_id: Unique identifier of the case to remove
|
|
587
|
-
|
|
604
|
+
case_id: Unique identifier of the case to remove items from.
|
|
605
|
+
item_values: List of item values (IDs / URLs) to remove.
|
|
606
|
+
|
|
607
|
+
Returns:
|
|
608
|
+
The updated Case object.
|
|
588
609
|
|
|
589
610
|
Raises:
|
|
590
|
-
NotFoundException: If the case does not exist
|
|
591
|
-
|
|
611
|
+
NotFoundException: If the case does not exist, or if any requested value
|
|
612
|
+
is not present in the case's items list.
|
|
613
|
+
DataStoreException: If persisting the updated case fails.
|
|
592
614
|
"""
|
|
593
615
|
ds = datastore()
|
|
594
616
|
|
|
@@ -597,20 +619,34 @@ def remove_case_item(case_id: str, item_value: str):
|
|
|
597
619
|
if not _case:
|
|
598
620
|
raise NotFoundException(f"Case {case_id} does not exist")
|
|
599
621
|
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
raise NotFoundException(f"Case item {item_value} does not exist")
|
|
622
|
+
# Build a lookup of value → item for all items currently in the case.
|
|
623
|
+
items_by_value = {_item["value"]: _item for _item in _case.items}
|
|
603
624
|
|
|
604
|
-
|
|
605
|
-
if
|
|
606
|
-
|
|
625
|
+
# Pre-validate all requested values before touching anything.
|
|
626
|
+
missing = [v for v in values if v not in items_by_value]
|
|
627
|
+
if missing:
|
|
628
|
+
raise NotFoundException(f"Case item(s) not found in case: {', '.join(missing)}")
|
|
607
629
|
|
|
608
|
-
|
|
630
|
+
# Resolve items and collect backing objects that need back-reference cleanup.
|
|
631
|
+
items_to_remove = [items_by_value[v] for v in values]
|
|
632
|
+
backing_objs: list[Hit | Observable] = []
|
|
633
|
+
for item in items_to_remove:
|
|
634
|
+
if item.type in [CaseItemTypes.HIT, CaseItemTypes.OBSERVABLE]:
|
|
635
|
+
obj = ds[item.type].get(item.value)
|
|
636
|
+
if obj:
|
|
637
|
+
backing_objs.append(obj)
|
|
638
|
+
|
|
639
|
+
# Remove all items in memory, then persist the case once.
|
|
640
|
+
for item in items_to_remove:
|
|
641
|
+
_case.items.remove(item)
|
|
609
642
|
|
|
610
643
|
if not ds.case.save(_case.case_id, _case):
|
|
611
644
|
raise DataStoreException("Failed to save case after item removal")
|
|
612
645
|
|
|
613
|
-
|
|
646
|
+
# Clean up back-references after the case is safely persisted.
|
|
647
|
+
for backing_obj in backing_objs:
|
|
614
648
|
remove_backreference(backing_obj, _case.case_id)
|
|
615
649
|
|
|
616
650
|
_sync_case_metadata(case_id)
|
|
651
|
+
|
|
652
|
+
return _case
|
|
@@ -152,7 +152,7 @@ suppress-none-returning = true
|
|
|
152
152
|
[tool.poetry]
|
|
153
153
|
package-mode = true
|
|
154
154
|
name = "howler-api"
|
|
155
|
-
version = "4.0.0.
|
|
155
|
+
version = "4.0.0.dev664"
|
|
156
156
|
description = "Howler - API server"
|
|
157
157
|
authors = [
|
|
158
158
|
"Canadian Centre for Cyber Security <howler@cyber.gc.ca>",
|
|
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
|
|
File without changes
|
|
File without changes
|
{howler_api-4.0.0.dev652 → howler_api-4.0.0.dev664}/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
|