xsoar-cli 2.4.0__tar.gz → 2.4.1__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.
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/CHANGELOG.md +5 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/PKG-INFO +1 -1
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/_version.py +2 -2
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/commands/case/README.md +47 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/commands/case/commands.py +106 -0
- xsoar_cli-2.4.1/src/xsoar_cli/xsoar_client/cases.py +78 -0
- xsoar_cli-2.4.1/src/xsoar_cli/xsoar_client/constants.py +8 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/xsoar_client/execution.py +2 -5
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/cli/conftest.py +13 -3
- xsoar_cli-2.4.1/tests/cli/test_case.py +123 -0
- xsoar_cli-2.4.1/tests/unit/test_case_commands.py +39 -0
- xsoar_cli-2.4.1/tests/unit/test_cases.py +353 -0
- xsoar_cli-2.4.0/src/xsoar_cli/xsoar_client/cases.py +0 -25
- xsoar_cli-2.4.0/src/xsoar_cli/xsoar_client/constants.py +0 -4
- xsoar_cli-2.4.0/tests/cli/test_case.py +0 -54
- xsoar_cli-2.4.0/tests/unit/test_cases.py +0 -175
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/.github/workflows/main.yml +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/.github/workflows/pull-request-open.yml +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/.github/workflows/release.yml +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/.github/workflows/update-changelog.yml +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/.gitignore +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/CLAUDE.md +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/CONTRIBUTING.md +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/LICENSE.txt +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/README.md +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/pyproject.toml +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/__init__.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/cli.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/commands/__init__.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/commands/case/__init__.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/commands/completions/README.md +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/commands/completions/__init__.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/commands/completions/commands.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/commands/config/README.md +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/commands/config/__init__.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/commands/config/commands.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/commands/content/README.md +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/commands/content/__init__.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/commands/content/commands.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/commands/execute/README.md +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/commands/execute/__init__.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/commands/execute/commands.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/commands/graph/README.md +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/commands/graph/__init__.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/commands/graph/commands.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/commands/integration/README.md +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/commands/integration/__init__.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/commands/integration/commands.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/commands/manifest/README.md +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/commands/manifest/__init__.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/commands/manifest/commands.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/commands/pack/README.md +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/commands/pack/__init__.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/commands/pack/commands.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/commands/plugins/README.md +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/commands/plugins/__init__.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/commands/plugins/commands.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/commands/rbac/README.md +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/commands/rbac/__init__.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/commands/rbac/commands.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/configuration.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/error_handling/__init__.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/error_handling/connection.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/error_handling/http.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/log.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/plugins/README.md +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/plugins/__init__.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/plugins/manager.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/utilities/__init__.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/utilities/config_file.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/utilities/content.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/utilities/download_content_handlers.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/utilities/manifest.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/utilities/output.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/utilities/validators.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/utilities/version_check.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/xsoar_client/__init__.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/xsoar_client/artifact_providers/__init__.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/xsoar_client/artifact_providers/azure.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/xsoar_client/artifact_providers/base.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/xsoar_client/artifact_providers/s3.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/xsoar_client/client.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/xsoar_client/content.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/xsoar_client/integrations.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/xsoar_client/packs.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/src/xsoar_cli/xsoar_client/rbac.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/__init__.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/cli/__init__.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/cli/test_base.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/cli/test_completions.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/cli/test_config.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/cli/test_content.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/cli/test_execute.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/cli/test_graph.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/cli/test_manifest.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/cli/test_pack.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/cli/test_plugins.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/conftest.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/fixtures/cases/create.json +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/fixtures/cases/get.json +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/fixtures/content/automation_search.json +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/fixtures/content/playbook_search.json +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/fixtures/content/user_commands.json +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/fixtures/integrations/instances.json +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/fixtures/manifest/manifest_base.json +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/fixtures/manifest/manifest_invalid.json +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/fixtures/manifest/manifest_with_pack_not_on_server.json +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/fixtures/manifest/server_base_response.json +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/fixtures/manifest/server_base_response_missing_one_pack.json +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/fixtures/manifest/server_base_response_with_updates.json +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/fixtures/manifest/server_base_response_with_updates_and_one_extra.json +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/fixtures/packs/installed.json +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/fixtures/packs/installed_expired.json +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/fixtures/rbac/roles.json +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/fixtures/rbac/user_groups.json +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/fixtures/rbac/users.json +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/mock_content/Download/playbook-empty.yml +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/mock_content/Packs/MyOrg_CommonPlaybooks/.pack-ignore +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/mock_content/Packs/MyOrg_CommonPlaybooks/.secrets-ignore +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/mock_content/Packs/MyOrg_CommonPlaybooks/Author_image.png +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/mock_content/Packs/MyOrg_CommonPlaybooks/Playbooks/GenericPlaybook.yml +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/mock_content/Packs/MyOrg_CommonPlaybooks/README.md +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/mock_content/Packs/MyOrg_CommonPlaybooks/pack_metadata.json +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/mock_content/Packs/MyOrg_CommonScripts/.pack-ignore +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/mock_content/Packs/MyOrg_CommonScripts/.secrets-ignore +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/mock_content/Packs/MyOrg_CommonScripts/Author_image.png +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/mock_content/Packs/MyOrg_CommonScripts/README.md +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/mock_content/Packs/MyOrg_CommonScripts/Scripts/GenericScript/GenericScript.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/mock_content/Packs/MyOrg_CommonScripts/Scripts/GenericScript/GenericScript.yml +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/mock_content/Packs/MyOrg_CommonScripts/Scripts/GenericScript/README.md +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/mock_content/Packs/MyOrg_CommonScripts/pack_metadata.json +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/mock_content/Packs/MyOrg_EDR/.pack-ignore +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/mock_content/Packs/MyOrg_EDR/.secrets-ignore +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/mock_content/Packs/MyOrg_EDR/Author_image.png +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/mock_content/Packs/MyOrg_EDR/Playbooks/EDR_InitialTriage.yml +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/mock_content/Packs/MyOrg_EDR/README.md +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/mock_content/Packs/MyOrg_EDR/Scripts/EDR_FetchFile/EDR_FetchFile.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/mock_content/Packs/MyOrg_EDR/Scripts/EDR_FetchFile/EDR_FetchFile.yml +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/mock_content/Packs/MyOrg_EDR/Scripts/EDR_FetchFile/README.md +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/mock_content/Packs/MyOrg_EDR/Scripts/EDR_Triage/EDR_Triage.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/mock_content/Packs/MyOrg_EDR/Scripts/EDR_Triage/EDR_Triage.yml +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/mock_content/Packs/MyOrg_EDR/Scripts/EDR_Triage/README.md +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/mock_content/Packs/MyOrg_EDR/Scripts/LegacyItem/LegacyItem.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/mock_content/Packs/MyOrg_EDR/Scripts/LegacyItem/LegacyItem.yml +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/mock_content/Packs/MyOrg_EDR/Scripts/LegacyItem/README.md +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/mock_content/Packs/MyOrg_EDR/Scripts/LegacyItem/test_data/basescript-dummy.json +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/mock_content/Packs/MyOrg_EDR/pack_metadata.json +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/mock_content/Packs/MyOrg_Layouts/.pack-ignore +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/mock_content/Packs/MyOrg_Layouts/.secrets-ignore +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/mock_content/Packs/MyOrg_Layouts/Author_image.png +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/mock_content/Packs/MyOrg_Layouts/Layouts/layoutscontainer-GenericLayout.json +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/mock_content/Packs/MyOrg_Layouts/README.md +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/mock_content/Packs/MyOrg_Layouts/pack_metadata.json +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/mock_content/Packs/Not_applicable/Playbooks/Empty.yml +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/unit/__init__.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/unit/conftest.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/unit/test_artifact_azure.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/unit/test_artifact_base.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/unit/test_artifact_s3.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/unit/test_client.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/unit/test_config_file.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/unit/test_content_domain.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/unit/test_content_filters.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/unit/test_content_handlers.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/unit/test_error_handling.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/unit/test_execution.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/unit/test_integrations.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/unit/test_main.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/unit/test_manifest_utils.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/unit/test_output.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/unit/test_packs.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/unit/test_plugin_manager.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/unit/test_rbac.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/unit/test_validators.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/tests/unit/test_version_check.py +0 -0
- {xsoar_cli-2.4.0 → xsoar_cli-2.4.1}/uv.lock +0 -0
|
@@ -6,12 +6,17 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [2.4.0] - 2026-06-17
|
|
10
|
+
|
|
9
11
|
### Added
|
|
10
12
|
|
|
11
13
|
- `content get-detached --type all` queries both scripts and playbooks and displays results for each type.
|
|
12
14
|
- `content list --output-format` option (choices: `table`, `json`, `plain`, default: `table`). `table` is human-readable aligned columns, `json` is machine-readable, and `plain` is tab-separated values suitable for piping to tools like `grep`.
|
|
13
15
|
- `content list --search TERM` option. Filters the listed items with a case-insensitive substring match against each item's id, name, and description. Content types and integration brands with no matching items are omitted, and a `No content matching '<term>' found.` message is shown when nothing matches.
|
|
14
16
|
- `content describe --type [script|playbook|command] NAME` command. Shows a single content item in detail: description, arguments, and inputs/outputs. Commands also show the integration brand and its configured instances (name and state). Supports `--output-format [table|json]` (default: `table`). See the [content README](src/xsoar_cli/commands/content/README.md) for details.
|
|
17
|
+
- `case get-context CASENUMBER` command. Retrieves the investigation context tree for a case as JSON, matching `demisto.context()` at runtime. The incident record is not merged in. See the [case README](src/xsoar_cli/commands/case/README.md) for details.
|
|
18
|
+
- `case get-entry ENTRY_ID` command. Retrieves a single War Room entry by its full ID (e.g. `112@153483`) as JSON. The case ID is derived from the entry ID. Exits non-zero with a clear message when the entry ID is malformed or the entry is not found. See the [case README](src/xsoar_cli/commands/case/README.md) for details.
|
|
19
|
+
- `case get-entries CASENUMBER` command. Retrieves all War Room entries for a case as a JSON array, or an empty array when the case has no entries. See the [case README](src/xsoar_cli/commands/case/README.md) for details.
|
|
15
20
|
|
|
16
21
|
### Changed
|
|
17
22
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: xsoar-cli
|
|
3
|
-
Version: 2.4.
|
|
3
|
+
Version: 2.4.1
|
|
4
4
|
Summary: A command-line interface for managing Palo Alto Networks XSOAR (Cortex XSOAR)
|
|
5
5
|
Project-URL: Documentation, https://github.com/tlium/xsoar-cli#readme
|
|
6
6
|
Project-URL: Issues, https://github.com/tlium/xsoar-cli/issues
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '2.4.
|
|
22
|
-
__version_tuple__ = version_tuple = (2, 4,
|
|
21
|
+
__version__ = version = '2.4.1'
|
|
22
|
+
__version_tuple__ = version_tuple = (2, 4, 1)
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -17,6 +17,53 @@ xsoar-cli case get 312412
|
|
|
17
17
|
xsoar-cli case get --environment prod 312412
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
+
## Get Context
|
|
21
|
+
|
|
22
|
+
Retrieve the investigation context tree for a single case. Output is the raw context tree as JSON, matching `demisto.context()` at runtime. The incident record is not merged in.
|
|
23
|
+
|
|
24
|
+
**Syntax:** `xsoar-cli case get-context [OPTIONS] CASENUMBER`
|
|
25
|
+
|
|
26
|
+
**Options:**
|
|
27
|
+
- `--environment TEXT` - Target environment (default: uses default environment from config)
|
|
28
|
+
|
|
29
|
+
**Examples:**
|
|
30
|
+
```
|
|
31
|
+
xsoar-cli case get-context 153483
|
|
32
|
+
xsoar-cli case get-context --environment prod 153483
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Get Entry
|
|
36
|
+
|
|
37
|
+
Retrieve a single War Room entry by its full ID. Output is the raw entry as JSON. The command exits non-zero with a clear message when no entry with that ID exists in the case.
|
|
38
|
+
|
|
39
|
+
`ENTRY_ID` is the full entry ID as shown in the GUI, in the form `<n>@<case-id>` (e.g. `112@153483`). The case ID is taken from the entry ID, so it does not need to be supplied separately.
|
|
40
|
+
|
|
41
|
+
**Syntax:** `xsoar-cli case get-entry [OPTIONS] ENTRY_ID`
|
|
42
|
+
|
|
43
|
+
**Options:**
|
|
44
|
+
- `--environment TEXT` - Target environment (default: uses default environment from config)
|
|
45
|
+
|
|
46
|
+
**Examples:**
|
|
47
|
+
```
|
|
48
|
+
xsoar-cli case get-entry 112@153483
|
|
49
|
+
xsoar-cli case get-entry --environment prod 112@153483
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Get Entries
|
|
53
|
+
|
|
54
|
+
Retrieve all War Room entries for a single case. Output is the full list of entries as a JSON array, or an empty array when the case has no entries.
|
|
55
|
+
|
|
56
|
+
**Syntax:** `xsoar-cli case get-entries [OPTIONS] CASENUMBER`
|
|
57
|
+
|
|
58
|
+
**Options:**
|
|
59
|
+
- `--environment TEXT` - Target environment (default: uses default environment from config)
|
|
60
|
+
|
|
61
|
+
**Examples:**
|
|
62
|
+
```
|
|
63
|
+
xsoar-cli case get-entries 153483
|
|
64
|
+
xsoar-cli case get-entries --environment prod 153483
|
|
65
|
+
```
|
|
66
|
+
|
|
20
67
|
## Clone
|
|
21
68
|
|
|
22
69
|
Clone a case from one environment to another. Useful for copying production cases to development environment for testing.
|
|
@@ -25,6 +25,19 @@ def parse_string_to_dict(input_string: str | None, delimiter: str) -> dict:
|
|
|
25
25
|
return {key.strip(): value.strip() for key, value in valid_pairs}
|
|
26
26
|
|
|
27
27
|
|
|
28
|
+
def parse_entry_id(entry_id: str) -> int:
|
|
29
|
+
"""Extract the numeric case ID from a full entry ID of the form '<n>@<case-id>'.
|
|
30
|
+
|
|
31
|
+
Raises ValueError if the entry ID is not in that form.
|
|
32
|
+
"""
|
|
33
|
+
parts = entry_id.split("@")
|
|
34
|
+
expected_parts = 2
|
|
35
|
+
if len(parts) != expected_parts or not parts[0].isdigit() or not parts[1].isdigit():
|
|
36
|
+
msg = f"invalid entry id '{entry_id}', expected the form <n>@<case-id> (e.g. 112@153483)"
|
|
37
|
+
raise ValueError(msg)
|
|
38
|
+
return int(parts[1])
|
|
39
|
+
|
|
40
|
+
|
|
28
41
|
@click.group()
|
|
29
42
|
def case() -> None:
|
|
30
43
|
"""Create, retrieve, and clone cases"""
|
|
@@ -210,6 +223,99 @@ def create( # noqa: PLR0913
|
|
|
210
223
|
click.echo(f"Created XSOAR case {case_id}")
|
|
211
224
|
|
|
212
225
|
|
|
226
|
+
@click.command(name="get-context")
|
|
227
|
+
@click.argument("casenumber", type=int)
|
|
228
|
+
@click.option("--environment", default=None, help="Default environment set in config file.")
|
|
229
|
+
@click.pass_context
|
|
230
|
+
@load_config
|
|
231
|
+
@validate_xsoar_connectivity
|
|
232
|
+
def get_context(ctx: click.Context, casenumber: int, environment: str | None) -> None:
|
|
233
|
+
"""Retrieve the investigation context tree for a single case.
|
|
234
|
+
|
|
235
|
+
CASENUMBER is the numeric case ID to look up. Output is the raw context
|
|
236
|
+
tree as JSON, matching demisto.context() at runtime.
|
|
237
|
+
|
|
238
|
+
Usage examples:
|
|
239
|
+
|
|
240
|
+
xsoar-cli case get-context 153483
|
|
241
|
+
"""
|
|
242
|
+
config = get_xsoar_config(ctx)
|
|
243
|
+
xsoar_client: Client = config.get_client(environment)
|
|
244
|
+
try:
|
|
245
|
+
response = xsoar_client.cases.get_context(casenumber)
|
|
246
|
+
except HTTPError as ex:
|
|
247
|
+
handler = HTTPErrorHandler()
|
|
248
|
+
click.echo(f"Error: {handler.get_message(ex, context='case')}")
|
|
249
|
+
ctx.exit(1)
|
|
250
|
+
click.echo(json.dumps(response, indent=4))
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
@click.command(name="get-entry")
|
|
254
|
+
@click.argument("entry_id", type=str)
|
|
255
|
+
@click.option("--environment", default=None, help="Default environment set in config file.")
|
|
256
|
+
@click.pass_context
|
|
257
|
+
@load_config
|
|
258
|
+
@validate_xsoar_connectivity
|
|
259
|
+
def get_entry(ctx: click.Context, entry_id: str, environment: str | None) -> None:
|
|
260
|
+
"""Retrieve a single War Room entry by its full ID.
|
|
261
|
+
|
|
262
|
+
ENTRY_ID is the full entry ID as shown in the GUI, in the form
|
|
263
|
+
<n>@<case-id> (e.g. 112@153483). The case ID is taken from the entry ID, so
|
|
264
|
+
it does not need to be supplied separately. Output is the raw entry as JSON.
|
|
265
|
+
|
|
266
|
+
Usage examples:
|
|
267
|
+
|
|
268
|
+
xsoar-cli case get-entry 112@153483
|
|
269
|
+
"""
|
|
270
|
+
try:
|
|
271
|
+
casenumber = parse_entry_id(entry_id)
|
|
272
|
+
except ValueError as ex:
|
|
273
|
+
click.echo(f"Error: {ex}")
|
|
274
|
+
ctx.exit(1)
|
|
275
|
+
config = get_xsoar_config(ctx)
|
|
276
|
+
xsoar_client: Client = config.get_client(environment)
|
|
277
|
+
try:
|
|
278
|
+
response = xsoar_client.cases.get_entry(casenumber, entry_id)
|
|
279
|
+
except HTTPError as ex:
|
|
280
|
+
handler = HTTPErrorHandler()
|
|
281
|
+
click.echo(f"Error: {handler.get_message(ex, context='case')}")
|
|
282
|
+
ctx.exit(1)
|
|
283
|
+
except ValueError as ex:
|
|
284
|
+
click.echo(f"Error: {ex}")
|
|
285
|
+
ctx.exit(1)
|
|
286
|
+
click.echo(json.dumps(response, indent=4))
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
@click.command(name="get-entries")
|
|
290
|
+
@click.argument("casenumber", type=int)
|
|
291
|
+
@click.option("--environment", default=None, help="Default environment set in config file.")
|
|
292
|
+
@click.pass_context
|
|
293
|
+
@load_config
|
|
294
|
+
@validate_xsoar_connectivity
|
|
295
|
+
def get_entries(ctx: click.Context, casenumber: int, environment: str | None) -> None:
|
|
296
|
+
"""Retrieve all War Room entries for a single case.
|
|
297
|
+
|
|
298
|
+
CASENUMBER is the numeric case ID. Output is the full list of entries as a
|
|
299
|
+
JSON array, or an empty array when the case has no entries.
|
|
300
|
+
|
|
301
|
+
Usage examples:
|
|
302
|
+
|
|
303
|
+
xsoar-cli case get-entries 153483
|
|
304
|
+
"""
|
|
305
|
+
config = get_xsoar_config(ctx)
|
|
306
|
+
xsoar_client: Client = config.get_client(environment)
|
|
307
|
+
try:
|
|
308
|
+
response = xsoar_client.cases.get_entries(casenumber)
|
|
309
|
+
except HTTPError as ex:
|
|
310
|
+
handler = HTTPErrorHandler()
|
|
311
|
+
click.echo(f"Error: {handler.get_message(ex, context='case')}")
|
|
312
|
+
ctx.exit(1)
|
|
313
|
+
click.echo(json.dumps(response, indent=4))
|
|
314
|
+
|
|
315
|
+
|
|
213
316
|
case.add_command(get)
|
|
214
317
|
case.add_command(clone)
|
|
215
318
|
case.add_command(create)
|
|
319
|
+
case.add_command(get_context)
|
|
320
|
+
case.add_command(get_entry)
|
|
321
|
+
case.add_command(get_entries)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from .constants import INVESTIGATION_PAGE_SIZE
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from .client import Client
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Cases:
|
|
12
|
+
def __init__(self, client: Client) -> None:
|
|
13
|
+
self.client = client
|
|
14
|
+
|
|
15
|
+
def get(self, case_id: int) -> dict:
|
|
16
|
+
"""Fetches a case by ID."""
|
|
17
|
+
endpoint = f"/incident/load/{case_id}"
|
|
18
|
+
response = self.client.make_request(endpoint=endpoint, method="GET")
|
|
19
|
+
response.raise_for_status()
|
|
20
|
+
return response.json()
|
|
21
|
+
|
|
22
|
+
def create(self, data: dict) -> dict:
|
|
23
|
+
"""Creates a new case."""
|
|
24
|
+
endpoint = self.client.resolve_endpoint(v6="/incident", v8="/xsoar/public/v1/incident")
|
|
25
|
+
response = self.client.make_request(endpoint=endpoint, json=data, method="POST")
|
|
26
|
+
response.raise_for_status()
|
|
27
|
+
return response.json()
|
|
28
|
+
|
|
29
|
+
def get_context(self, case_id: int) -> dict:
|
|
30
|
+
"""Fetches the investigation context tree for a case, verbatim.
|
|
31
|
+
|
|
32
|
+
Returns the raw POST /investigation/<id>/context response, which matches
|
|
33
|
+
demisto.context() at runtime. The incident record is not merged in.
|
|
34
|
+
|
|
35
|
+
The body query '${.}' is a DT expression meaning "the entire context
|
|
36
|
+
root", so the full context tree is returned.
|
|
37
|
+
"""
|
|
38
|
+
endpoint = f"/investigation/{case_id}/context"
|
|
39
|
+
response = self.client.make_request(endpoint=endpoint, method="POST", json={"query": "${.}"})
|
|
40
|
+
response.raise_for_status()
|
|
41
|
+
return response.json()
|
|
42
|
+
|
|
43
|
+
def get_entry(self, case_id: int, entry_id: str) -> dict:
|
|
44
|
+
"""Fetches a single War Room entry by its full ID (e.g. '112@153483').
|
|
45
|
+
|
|
46
|
+
XSOAR has no per-entry endpoint, so the full War Room history is fetched
|
|
47
|
+
via POST /investigation/<id> and the entry with the matching id is
|
|
48
|
+
returned verbatim. Raises ValueError if no entry with that id exists.
|
|
49
|
+
"""
|
|
50
|
+
for entry in self._fetch_entries(case_id):
|
|
51
|
+
if entry.get("id") == entry_id:
|
|
52
|
+
return entry
|
|
53
|
+
msg = f"Entry '{entry_id}' not found in case {case_id}"
|
|
54
|
+
raise ValueError(msg)
|
|
55
|
+
|
|
56
|
+
def get_entries(self, case_id: int) -> list[dict]:
|
|
57
|
+
"""Fetches all War Room entries for a case, verbatim.
|
|
58
|
+
|
|
59
|
+
Returns the full entries list from POST /investigation/<id>. An empty
|
|
60
|
+
list is returned when the case has no entries.
|
|
61
|
+
"""
|
|
62
|
+
return self._fetch_entries(case_id)
|
|
63
|
+
|
|
64
|
+
def _fetch_entries(self, case_id: int) -> list[dict]:
|
|
65
|
+
"""Fetches the full War Room entry history for a case.
|
|
66
|
+
|
|
67
|
+
XSOAR has no per-entry endpoint, so the whole history is retrieved with
|
|
68
|
+
a single POST /investigation/<id> call and the entries list is returned
|
|
69
|
+
verbatim.
|
|
70
|
+
"""
|
|
71
|
+
data, _, _ = self.client.demisto_py_instance.generic_request(
|
|
72
|
+
path=f"/investigation/{case_id}",
|
|
73
|
+
method="POST",
|
|
74
|
+
body={"pageSize": INVESTIGATION_PAGE_SIZE},
|
|
75
|
+
content_type="application/json",
|
|
76
|
+
response_type=object,
|
|
77
|
+
)
|
|
78
|
+
return data.get("entries") or []
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""Shared constants for the xsoar_client package."""
|
|
2
|
+
|
|
3
|
+
XSOAR_OLD_VERSION = 6
|
|
4
|
+
HTTP_CALL_TIMEOUT = 30
|
|
5
|
+
|
|
6
|
+
# Page size requested when fetching investigation entries via POST /investigation/<id>.
|
|
7
|
+
# Set high so that all War Room entries are returned in a single response.
|
|
8
|
+
INVESTIGATION_PAGE_SIZE = 1000
|
|
@@ -4,6 +4,8 @@ import logging
|
|
|
4
4
|
import time
|
|
5
5
|
from typing import TYPE_CHECKING
|
|
6
6
|
|
|
7
|
+
from .constants import INVESTIGATION_PAGE_SIZE
|
|
8
|
+
|
|
7
9
|
if TYPE_CHECKING:
|
|
8
10
|
from .client import Client
|
|
9
11
|
|
|
@@ -23,11 +25,6 @@ DEFAULT_SYNC_TIMEOUT = 30
|
|
|
23
25
|
# result entries.
|
|
24
26
|
SYNC_POLL_INTERVAL = 2
|
|
25
27
|
|
|
26
|
-
# Page size requested when fetching investigation entries during polling. The
|
|
27
|
-
# playground accumulates history over time, so this is set high to ensure a
|
|
28
|
-
# freshly created result entry is included in the response.
|
|
29
|
-
INVESTIGATION_PAGE_SIZE = 1000
|
|
30
|
-
|
|
31
28
|
|
|
32
29
|
def _format_arg_value(value: str) -> str:
|
|
33
30
|
"""Format a single argument value for a War Room command string.
|
|
@@ -272,9 +272,10 @@ def mock_execute_env(mock_config_file) -> Iterator[types.SimpleNamespace]: # no
|
|
|
272
272
|
def mock_case_env(mock_config_file, make_case_response, make_case_create_response) -> Iterator[types.SimpleNamespace]: # noqa: ANN001
|
|
273
273
|
"""Mock environment for ``case`` commands.
|
|
274
274
|
|
|
275
|
-
Patches ``Cases.get
|
|
276
|
-
|
|
277
|
-
|
|
275
|
+
Patches ``Cases.get``, ``Cases.create``, ``Cases.get_context``,
|
|
276
|
+
``Cases.get_entry``, and ``Cases.get_entries`` with default success
|
|
277
|
+
responses. The mocks are exposed on the yielded namespace so tests can
|
|
278
|
+
override return values or side effects as needed.
|
|
278
279
|
"""
|
|
279
280
|
import types as _types
|
|
280
281
|
|
|
@@ -282,13 +283,22 @@ def mock_case_env(mock_config_file, make_case_response, make_case_create_respons
|
|
|
282
283
|
patch("xsoar_cli.xsoar_client.client.Client.test_connectivity", return_value=True) as mock_conn,
|
|
283
284
|
patch("xsoar_cli.xsoar_client.cases.Cases.get") as mock_get,
|
|
284
285
|
patch("xsoar_cli.xsoar_client.cases.Cases.create") as mock_create,
|
|
286
|
+
patch("xsoar_cli.xsoar_client.cases.Cases.get_context") as mock_get_context,
|
|
287
|
+
patch("xsoar_cli.xsoar_client.cases.Cases.get_entry") as mock_get_entry,
|
|
288
|
+
patch("xsoar_cli.xsoar_client.cases.Cases.get_entries") as mock_get_entries,
|
|
285
289
|
):
|
|
286
290
|
mock_get.return_value = make_case_response()
|
|
287
291
|
mock_create.return_value = make_case_create_response()
|
|
292
|
+
mock_get_context.return_value = {"playbook_name": "My Playbook", "File": []}
|
|
293
|
+
mock_get_entry.return_value = {"id": "112@153483", "contents": "entry data"}
|
|
294
|
+
mock_get_entries.return_value = [{"id": "112@153483", "contents": "entry data"}]
|
|
288
295
|
ns = _types.SimpleNamespace(
|
|
289
296
|
config=mock_config_file,
|
|
290
297
|
connectivity=mock_conn,
|
|
291
298
|
get=mock_get,
|
|
292
299
|
create=mock_create,
|
|
300
|
+
get_context=mock_get_context,
|
|
301
|
+
get_entry=mock_get_entry,
|
|
302
|
+
get_entries=mock_get_entries,
|
|
293
303
|
)
|
|
294
304
|
yield ns
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"""CLI integration tests for the ``case`` command group."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from tests.cli.conftest import InvokeHelper
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TestCaseGroup:
|
|
12
|
+
"""The bare ``case`` command and its help output."""
|
|
13
|
+
|
|
14
|
+
def test_shows_help(self, invoke: InvokeHelper) -> None:
|
|
15
|
+
result = invoke(["case"])
|
|
16
|
+
assert result.exit_code == 0
|
|
17
|
+
|
|
18
|
+
def test_help_flag(self, invoke: InvokeHelper) -> None:
|
|
19
|
+
result = invoke(["case", "--help"])
|
|
20
|
+
assert result.exit_code == 0
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class TestCaseGet:
|
|
24
|
+
"""Tests for ``case get``."""
|
|
25
|
+
|
|
26
|
+
def test_get_case_success(self, invoke: InvokeHelper, mock_case_env) -> None:
|
|
27
|
+
result = invoke(["case", "get", "152230"])
|
|
28
|
+
assert result.exit_code == 0
|
|
29
|
+
|
|
30
|
+
def test_get_case_http_error(self, invoke: InvokeHelper, mock_case_env, make_http_error) -> None:
|
|
31
|
+
mock_case_env.get.side_effect = make_http_error(400, text="Bad Request")
|
|
32
|
+
result = invoke(["case", "get", "152230"])
|
|
33
|
+
assert result.exit_code == 1
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class TestCaseCreate:
|
|
37
|
+
"""Tests for ``case create``."""
|
|
38
|
+
|
|
39
|
+
def test_create_case_success(self, invoke: InvokeHelper, mock_case_env) -> None:
|
|
40
|
+
result = invoke(["case", "create"])
|
|
41
|
+
assert result.exit_code == 0
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class TestCaseClone:
|
|
45
|
+
"""Tests for ``case clone``."""
|
|
46
|
+
|
|
47
|
+
def test_clone_missing_options(self, invoke: InvokeHelper, mock_case_env) -> None:
|
|
48
|
+
result = invoke(["case", "clone", "152230"])
|
|
49
|
+
assert result.exit_code == 2
|
|
50
|
+
|
|
51
|
+
def test_clone_with_bogus_environments(self, invoke: InvokeHelper, mock_case_env) -> None:
|
|
52
|
+
result = invoke(["case", "clone", "--dest", "bogus", "--source", "bogus", "152230"])
|
|
53
|
+
assert result.exit_code == 1
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class TestCaseGetContext:
|
|
57
|
+
"""Tests for ``case get-context``."""
|
|
58
|
+
|
|
59
|
+
def test_get_context_success(self, invoke: InvokeHelper, mock_case_env) -> None:
|
|
60
|
+
result = invoke(["case", "get-context", "153483"])
|
|
61
|
+
assert result.exit_code == 0
|
|
62
|
+
mock_case_env.get_context.assert_called_once_with(153483)
|
|
63
|
+
|
|
64
|
+
def test_get_context_http_error(self, invoke: InvokeHelper, mock_case_env, make_http_error) -> None:
|
|
65
|
+
mock_case_env.get_context.side_effect = make_http_error(404, text="Not Found")
|
|
66
|
+
result = invoke(["case", "get-context", "153483"])
|
|
67
|
+
assert result.exit_code == 1
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class TestCaseGetEntry:
|
|
71
|
+
"""Tests for ``case get-entry``."""
|
|
72
|
+
|
|
73
|
+
def test_get_entry_success(self, invoke: InvokeHelper, mock_case_env) -> None:
|
|
74
|
+
result = invoke(["case", "get-entry", "112@153483"])
|
|
75
|
+
assert result.exit_code == 0
|
|
76
|
+
mock_case_env.get_entry.assert_called_once_with(153483, "112@153483")
|
|
77
|
+
|
|
78
|
+
def test_get_entry_not_found(self, invoke: InvokeHelper, mock_case_env) -> None:
|
|
79
|
+
mock_case_env.get_entry.side_effect = ValueError("Entry '999@153483' not found in case 153483")
|
|
80
|
+
result = invoke(["case", "get-entry", "999@153483"])
|
|
81
|
+
assert result.exit_code == 1
|
|
82
|
+
assert "not found" in result.output
|
|
83
|
+
|
|
84
|
+
def test_get_entry_http_error(self, invoke: InvokeHelper, mock_case_env, make_http_error) -> None:
|
|
85
|
+
mock_case_env.get_entry.side_effect = make_http_error(400, text="Bad Request")
|
|
86
|
+
result = invoke(["case", "get-entry", "112@153483"])
|
|
87
|
+
assert result.exit_code == 1
|
|
88
|
+
|
|
89
|
+
def test_get_entry_missing_entry_id_argument(self, invoke: InvokeHelper, mock_case_env) -> None:
|
|
90
|
+
result = invoke(["case", "get-entry"])
|
|
91
|
+
assert result.exit_code == 2
|
|
92
|
+
|
|
93
|
+
def test_get_entry_malformed_entry_id(self, invoke: InvokeHelper, mock_case_env) -> None:
|
|
94
|
+
result = invoke(["case", "get-entry", "112"])
|
|
95
|
+
assert result.exit_code == 1
|
|
96
|
+
assert "invalid entry id" in result.output
|
|
97
|
+
mock_case_env.get_entry.assert_not_called()
|
|
98
|
+
|
|
99
|
+
def test_get_entry_non_numeric_entry_number(self, invoke: InvokeHelper, mock_case_env) -> None:
|
|
100
|
+
result = invoke(["case", "get-entry", "12INVALID21@1234"])
|
|
101
|
+
assert result.exit_code == 1
|
|
102
|
+
assert "invalid entry id" in result.output
|
|
103
|
+
mock_case_env.get_entry.assert_not_called()
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class TestCaseGetEntries:
|
|
107
|
+
"""Tests for ``case get-entries``."""
|
|
108
|
+
|
|
109
|
+
def test_get_entries_success(self, invoke: InvokeHelper, mock_case_env) -> None:
|
|
110
|
+
result = invoke(["case", "get-entries", "153483"])
|
|
111
|
+
assert result.exit_code == 0
|
|
112
|
+
mock_case_env.get_entries.assert_called_once_with(153483)
|
|
113
|
+
|
|
114
|
+
def test_get_entries_empty(self, invoke: InvokeHelper, mock_case_env) -> None:
|
|
115
|
+
mock_case_env.get_entries.return_value = []
|
|
116
|
+
result = invoke(["case", "get-entries", "153483"])
|
|
117
|
+
assert result.exit_code == 0
|
|
118
|
+
assert result.output.strip() == "[]"
|
|
119
|
+
|
|
120
|
+
def test_get_entries_http_error(self, invoke: InvokeHelper, mock_case_env, make_http_error) -> None:
|
|
121
|
+
mock_case_env.get_entries.side_effect = make_http_error(404, text="Not Found")
|
|
122
|
+
result = invoke(["case", "get-entries", "153483"])
|
|
123
|
+
assert result.exit_code == 1
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""Unit tests for helpers in ``xsoar_cli.commands.case.commands``."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
|
|
7
|
+
from xsoar_cli.commands.case.commands import parse_entry_id
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TestParseEntryId:
|
|
11
|
+
def test_valid_entry_id_returns_case_id(self) -> None:
|
|
12
|
+
assert parse_entry_id("112@153483") == 153483
|
|
13
|
+
|
|
14
|
+
def test_multi_digit_entry_number(self) -> None:
|
|
15
|
+
assert parse_entry_id("9@7") == 7
|
|
16
|
+
|
|
17
|
+
def test_missing_at_separator_raises(self) -> None:
|
|
18
|
+
with pytest.raises(ValueError, match="invalid entry id"):
|
|
19
|
+
parse_entry_id("112")
|
|
20
|
+
|
|
21
|
+
def test_empty_entry_number_raises(self) -> None:
|
|
22
|
+
with pytest.raises(ValueError, match="invalid entry id"):
|
|
23
|
+
parse_entry_id("@153483")
|
|
24
|
+
|
|
25
|
+
def test_non_numeric_entry_number_raises(self) -> None:
|
|
26
|
+
with pytest.raises(ValueError, match="invalid entry id"):
|
|
27
|
+
parse_entry_id("12INVALID21@1234")
|
|
28
|
+
|
|
29
|
+
def test_empty_case_id_raises(self) -> None:
|
|
30
|
+
with pytest.raises(ValueError, match="invalid entry id"):
|
|
31
|
+
parse_entry_id("112@")
|
|
32
|
+
|
|
33
|
+
def test_non_numeric_case_id_raises(self) -> None:
|
|
34
|
+
with pytest.raises(ValueError, match="invalid entry id"):
|
|
35
|
+
parse_entry_id("112@abc")
|
|
36
|
+
|
|
37
|
+
def test_too_many_parts_raises(self) -> None:
|
|
38
|
+
with pytest.raises(ValueError, match="invalid entry id"):
|
|
39
|
+
parse_entry_id("112@153483@extra")
|