splunk-soar-sdk 1.3.4__tar.gz → 1.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.
Files changed (215) hide show
  1. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/PKG-INFO +1 -1
  2. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/pyproject.toml +1 -1
  3. splunk_soar_sdk-1.4.1/release_notes.txt +31 -0
  4. splunk_soar_sdk-1.4.1/release_version.txt +1 -0
  5. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/abstract.py +8 -1
  6. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/app.py +3 -1
  7. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/app_client.py +9 -1
  8. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/asset.py +3 -1
  9. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/cli/init/cli.py +16 -3
  10. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/cli/manifests/deserializers.py +22 -3
  11. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/cli/utils.py +6 -1
  12. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/code_renderers/asset_renderer.py +7 -1
  13. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/params.py +3 -1
  14. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/cli/test_convert_cli.py +58 -0
  15. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/cli/test_deserializers.py +80 -2
  16. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/cli/test_serializers.py +15 -0
  17. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/cli/test_utils.py +12 -7
  18. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/example_app/src/app.py +2 -0
  19. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/test_app_action.py +2 -1
  20. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/test_app_client.py +4 -0
  21. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/test_asset.py +13 -1
  22. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/uv.lock +1 -1
  23. splunk_soar_sdk-1.3.4/release_notes.txt +0 -23
  24. splunk_soar_sdk-1.3.4/release_version.txt +0 -1
  25. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/.github/ISSUE_TEMPLATE/bug.md +0 -0
  26. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  27. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/.github/pull_request_template.md +0 -0
  28. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/.github/utils/github.js +0 -0
  29. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/.github/utils/update_version.py +0 -0
  30. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/.github/workflows/code_quality.yml +0 -0
  31. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/.github/workflows/commit_hygiene.yml +0 -0
  32. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/.github/workflows/generate_docs.yml +0 -0
  33. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/.github/workflows/semantic_release.yml +0 -0
  34. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/.gitignore +0 -0
  35. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/.pre-commit-config.yaml +0 -0
  36. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/.releaserc +0 -0
  37. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/LICENSE +0 -0
  38. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/README.md +0 -0
  39. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/commitlint.config.js +0 -0
  40. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/docs/api_reference.rst +0 -0
  41. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/docs/app_structure/index.rst +0 -0
  42. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/docs/app_structure/pre-commit-config.yaml.rst +0 -0
  43. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/docs/app_structure/pyproject.toml.rst +0 -0
  44. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/docs/app_structure/src_app.rst +0 -0
  45. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/docs/changelog.rst +0 -0
  46. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/docs/cli_reference.rst +0 -0
  47. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/docs/conf.py +0 -0
  48. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/docs/custom_views/index.rst +0 -0
  49. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/docs/custom_views/reusable_components.md +0 -0
  50. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/docs/custom_views/templates.md +0 -0
  51. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/docs/custom_views/view_handlers.md +0 -0
  52. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/docs/getting_started/index.rst +0 -0
  53. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/docs/getting_started/init_app.rst +0 -0
  54. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/docs/getting_started/installation.rst +0 -0
  55. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/docs/index.rst +0 -0
  56. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/__init__.py +0 -0
  57. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/action_results.py +0 -0
  58. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/actions_manager.py +0 -0
  59. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/apis/__init__.py +0 -0
  60. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/apis/artifact.py +0 -0
  61. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/apis/container.py +0 -0
  62. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/apis/utils.py +0 -0
  63. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/apis/vault.py +0 -0
  64. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/app_cli_runner.py +0 -0
  65. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/app_templates/basic_app/.gitignore +0 -0
  66. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/app_templates/basic_app/.pre-commit-config.yaml +0 -0
  67. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/app_templates/basic_app/logo.svg +0 -0
  68. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/app_templates/basic_app/logo_dark.svg +0 -0
  69. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/app_templates/basic_app/src/__init__.py +0 -0
  70. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/app_templates/basic_app/uv.lock +0 -0
  71. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/async_utils.py +0 -0
  72. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/cli/__init__.py +0 -0
  73. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/cli/cli.py +0 -0
  74. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/cli/init/__init__.py +0 -0
  75. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/cli/manifests/__init__.py +0 -0
  76. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/cli/manifests/cli.py +0 -0
  77. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/cli/manifests/processors.py +0 -0
  78. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/cli/manifests/serializers.py +0 -0
  79. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/cli/package/cli.py +0 -0
  80. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/cli/package/utils.py +0 -0
  81. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/cli/path_utils.py +0 -0
  82. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/code_renderers/__init__.py +0 -0
  83. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/code_renderers/action_renderer.py +0 -0
  84. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/code_renderers/app_renderer.py +0 -0
  85. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/code_renderers/renderer.py +0 -0
  86. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/code_renderers/templates/pyproject.toml.jinja +0 -0
  87. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/code_renderers/toml_renderer.py +0 -0
  88. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/colors.py +0 -0
  89. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/compat.py +0 -0
  90. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/crypto.py +0 -0
  91. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/decorators/__init__.py +0 -0
  92. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/decorators/action.py +0 -0
  93. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/decorators/generic_action.py +0 -0
  94. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/decorators/on_poll.py +0 -0
  95. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/decorators/test_connectivity.py +0 -0
  96. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/decorators/view_handler.py +0 -0
  97. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/decorators/webhook.py +0 -0
  98. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/exceptions.py +0 -0
  99. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/input_spec.py +0 -0
  100. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/logging.py +0 -0
  101. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/meta/__init__.py +0 -0
  102. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/meta/actions.py +0 -0
  103. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/meta/adapters.py +0 -0
  104. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/meta/app.py +0 -0
  105. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/meta/datatypes.py +0 -0
  106. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/meta/dependencies.py +0 -0
  107. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/meta/webhooks.py +0 -0
  108. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/models/__init__.py +0 -0
  109. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/models/artifact.py +0 -0
  110. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/models/container.py +0 -0
  111. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/models/vault_attachment.py +0 -0
  112. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/models/view.py +0 -0
  113. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/paths.py +0 -0
  114. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/py.typed +0 -0
  115. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/shims/phantom/action_result.py +0 -0
  116. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/shims/phantom/app.py +0 -0
  117. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/shims/phantom/base_connector.py +0 -0
  118. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/shims/phantom/connector_result.py +0 -0
  119. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/shims/phantom/consts.py +0 -0
  120. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/shims/phantom/encryption_helper.py +0 -0
  121. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/shims/phantom/install_info.py +0 -0
  122. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/shims/phantom/json_keys.py +0 -0
  123. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/shims/phantom/ph_ipc.py +0 -0
  124. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/shims/phantom/vault.py +0 -0
  125. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/shims/phantom_common/app_interface/app_interface.py +0 -0
  126. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/shims/phantom_common/encryption/encryption_manager_factory.py +0 -0
  127. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/templates/base/base_template.html +0 -0
  128. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/templates/base/error.html +0 -0
  129. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/templates/base/header.html +0 -0
  130. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/templates/base/logo_header.html +0 -0
  131. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/templates/components/pie_chart.html +0 -0
  132. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/templates/widgets/widget_resize_snippet.html +0 -0
  133. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/templates/widgets/widget_template.html +0 -0
  134. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/types.py +0 -0
  135. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/views/__init__.py +0 -0
  136. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/views/component_registry.py +0 -0
  137. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/views/components/__init__.py +0 -0
  138. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/views/components/pie_chart.py +0 -0
  139. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/views/template_filters.py +0 -0
  140. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/views/template_renderer.py +0 -0
  141. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/views/view_parser.py +0 -0
  142. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/webhooks/__init__.py +0 -0
  143. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/webhooks/models.py +0 -0
  144. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/src/soar_sdk/webhooks/routing.py +0 -0
  145. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/test.png +0 -0
  146. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/test.txt +0 -0
  147. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/__init__.py +0 -0
  148. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/cli/__init__.py +0 -0
  149. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/cli/datapath_parse.py +0 -0
  150. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/cli/manifests/__init__.py +0 -0
  151. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/cli/manifests/test_processors.py +0 -0
  152. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/cli/test_assets/converted_app/actions.py.txt +0 -0
  153. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/cli/test_cli.py +0 -0
  154. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/cli/test_init_cli.py +0 -0
  155. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/cli/test_manifests_cli.py +0 -0
  156. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/cli/test_package_cli.py +0 -0
  157. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/code_renderers/test_action_renderer.py +0 -0
  158. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/conftest.py +0 -0
  159. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/example_app/app.json +0 -0
  160. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/example_app/example_asset.json +0 -0
  161. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/example_app/logo.svg +0 -0
  162. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/example_app/logo_dark.svg +0 -0
  163. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/example_app/pyproject.toml +0 -0
  164. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/example_app/src/__init__.py +0 -0
  165. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/example_app/src/actions/__init__.py +0 -0
  166. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/example_app/src/actions/async_action.py +0 -0
  167. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/example_app/src/actions/generate_category.py +0 -0
  168. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/example_app/src/actions/reverse_string.py +0 -0
  169. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/example_app/src/ignoreme.txt +0 -0
  170. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/example_app/templates/reverse_string.html +0 -0
  171. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/example_app/uv.lock +0 -0
  172. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/example_app_with_webhook/app.json +0 -0
  173. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/example_app_with_webhook/logo.svg +0 -0
  174. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/example_app_with_webhook/logo_dark.svg +0 -0
  175. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/example_app_with_webhook/pyproject.toml +0 -0
  176. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/example_app_with_webhook/src/__init__.py +0 -0
  177. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/example_app_with_webhook/src/app.py +0 -0
  178. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/example_app_with_webhook/uv.lock +0 -0
  179. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/interfaces/__init__.py +0 -0
  180. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/interfaces/test_artifact_interface.py +0 -0
  181. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/interfaces/test_container_interface.py +0 -0
  182. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/interfaces/test_vault_interface.py +0 -0
  183. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/meta/__init__.py +0 -0
  184. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/meta/test_actions.py +0 -0
  185. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/meta/test_adapters.py +0 -0
  186. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/meta/test_datatypes.py +0 -0
  187. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/meta/test_dependencies.py +0 -0
  188. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/meta/test_webhooks.py +0 -0
  189. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/mocks/__init__.py +0 -0
  190. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/mocks/dynamic_mocks.py +0 -0
  191. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/mocks/importable_action.py +0 -0
  192. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/stubs.py +0 -0
  193. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/test_action_results.py +0 -0
  194. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/test_actions_manager.py +0 -0
  195. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/test_app.py +0 -0
  196. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/test_app_action_params.py +0 -0
  197. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/test_app_action_results.py +0 -0
  198. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/test_app_runner.py +0 -0
  199. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/test_assets/splunk-sdk-2.1.0.tar.gz +0 -0
  200. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/test_async_integration.py +0 -0
  201. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/test_async_utils.py +0 -0
  202. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/test_code_renderers.py +0 -0
  203. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/test_compat.py +0 -0
  204. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/test_container.py +0 -0
  205. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/test_custom_views.py +0 -0
  206. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/test_encryption.py +0 -0
  207. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/test_generic_action.py +0 -0
  208. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/test_logging.py +0 -0
  209. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/test_on_poll.py +0 -0
  210. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/test_params.py +0 -0
  211. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/test_template_renderer.py +0 -0
  212. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/test_test_connectivity.py +0 -0
  213. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/test_view_parser.py +0 -0
  214. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/webhooks/test_models.py +0 -0
  215. {splunk_soar_sdk-1.3.4 → splunk_soar_sdk-1.4.1}/tests/webhooks/test_routing.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: splunk-soar-sdk
3
- Version: 1.3.4
3
+ Version: 1.4.1
4
4
  Summary: The official framework for developing and testing Splunk SOAR Apps
5
5
  Project-URL: Homepage, https://github.com/phantomcyber/splunk-soar-sdk
6
6
  Project-URL: Documentation, https://github.com/phantomcyber/splunk-soar-sdk
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "splunk-soar-sdk"
3
- version = "1.3.4"
3
+ version = "1.4.1"
4
4
  description = "The official framework for developing and testing Splunk SOAR Apps"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.9, <3.14"
@@ -0,0 +1,31 @@
1
+ ## [1.4.1](https://github.com/phantomcyber/splunk-soar-sdk/compare/1.4.0...1.4.1) (2025-09-10)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * cleanup based on pytest and pr review ([8de0911](https://github.com/phantomcyber/splunk-soar-sdk/commit/8de0911de5c1929aaed898ea81e92a38b7a03974))
7
+ * convert illegal field names to aliases when converting asset or action params ([9cde111](https://github.com/phantomcyber/splunk-soar-sdk/commit/9cde111dfb245fb8e2604e59a8a4fc8512768b7d))
8
+ * normalize_field_names removes leading underscores ([3880b09](https://github.com/phantomcyber/splunk-soar-sdk/commit/3880b09f438c8734c506c146cbe5cdd4521219cd))
9
+ * update converted apps to use the default python versions ([156853c](https://github.com/phantomcyber/splunk-soar-sdk/commit/156853cfe3376ab89f37692e52dc343468a314af))
10
+ * use correct datatypes for default asset values when converting ([c2838ff](https://github.com/phantomcyber/splunk-soar-sdk/commit/c2838ff172517d7f38650692f8e6e61176268382))
11
+ * when deserializing action params, treat them as optional by default ([d2d3b48](https://github.com/phantomcyber/splunk-soar-sdk/commit/d2d3b48ddd1ded8a6b432d33f6867fb394fe17ab))
12
+
13
+
14
+
15
+
16
+
17
+ ## [1.4.1](https://github.com/phantomcyber/splunk-soar-sdk/compare/1.4.0...1.4.1) (2025-09-10)
18
+
19
+
20
+ ### Bug Fixes
21
+
22
+ * cleanup based on pytest and pr review ([8de0911](https://github.com/phantomcyber/splunk-soar-sdk/commit/8de0911de5c1929aaed898ea81e92a38b7a03974))
23
+ * convert illegal field names to aliases when converting asset or action params ([9cde111](https://github.com/phantomcyber/splunk-soar-sdk/commit/9cde111dfb245fb8e2604e59a8a4fc8512768b7d))
24
+ * normalize_field_names removes leading underscores ([3880b09](https://github.com/phantomcyber/splunk-soar-sdk/commit/3880b09f438c8734c506c146cbe5cdd4521219cd))
25
+ * update converted apps to use the default python versions ([156853c](https://github.com/phantomcyber/splunk-soar-sdk/commit/156853cfe3376ab89f37692e52dc343468a314af))
26
+ * use correct datatypes for default asset values when converting ([c2838ff](https://github.com/phantomcyber/splunk-soar-sdk/commit/c2838ff172517d7f38650692f8e6e61176268382))
27
+ * when deserializing action params, treat them as optional by default ([d2d3b48](https://github.com/phantomcyber/splunk-soar-sdk/commit/d2d3b48ddd1ded8a6b432d33f6867fb394fe17ab))
28
+
29
+
30
+
31
+
@@ -0,0 +1 @@
1
+ 1.4.1
@@ -63,6 +63,11 @@ class SOARClient(Generic[SummaryType]):
63
63
  """Object governing interaction with the SOAR container API. Subclasses must define."""
64
64
  pass
65
65
 
66
+ @abstractmethod
67
+ def get_executing_container_id(self) -> int:
68
+ """Return the current Container ID passed in the Connector Run Action JSON."""
69
+ pass
70
+
66
71
  def get(
67
72
  self,
68
73
  endpoint: str,
@@ -202,7 +207,9 @@ class SOARClient(Generic[SummaryType]):
202
207
  return "https://localhost:9999/"
203
208
 
204
209
  @abstractmethod
205
- def update_client(self, soar_auth: SOARClientAuth, asset_id: str) -> None:
210
+ def update_client(
211
+ self, soar_auth: SOARClientAuth, asset_id: str, container_id: int = 0
212
+ ) -> None:
206
213
  """Hook to update the SOAR API client before any actions run with the input data.
207
214
 
208
215
  An example of what this function might do is authenticate the API client.
@@ -187,7 +187,9 @@ class App:
187
187
 
188
188
  self.__logger.handler.set_handle(handle)
189
189
  soar_auth = App.create_soar_client_auth_object(input_data)
190
- self.soar_client.update_client(soar_auth, input_data.asset_id)
190
+ self.soar_client.update_client(
191
+ soar_auth, input_data.asset_id, input_data.container_id
192
+ )
191
193
  return self.actions_manager.handle(input_data, handle=handle)
192
194
 
193
195
  @staticmethod
@@ -45,6 +45,7 @@ class AppClient(SOARClient[SummaryType]):
45
45
 
46
46
  self._summary: Optional[SummaryType] = None
47
47
  self._message: Optional[str] = None
48
+ self.__container_id: int = 0
48
49
 
49
50
  @property
50
51
  def client(self) -> httpx.Client:
@@ -66,10 +67,17 @@ class AppClient(SOARClient[SummaryType]):
66
67
  """The SOAR Vault API."""
67
68
  return self._vault_api
68
69
 
69
- def update_client(self, soar_auth: SOARClientAuth, asset_id: str) -> None:
70
+ def get_executing_container_id(self) -> int:
71
+ """Return the current Container ID passed in the Connector Run Action JSON."""
72
+ return self.__container_id
73
+
74
+ def update_client(
75
+ self, soar_auth: SOARClientAuth, asset_id: str, container_id: int = 0
76
+ ) -> None:
70
77
  """Update the SOAR client with the given authentication and asset ID."""
71
78
  self.authenticate_soar_client(soar_auth)
72
79
  self._containers_api.set_executing_asset(asset_id)
80
+ self.__container_id = container_id
73
81
 
74
82
  def authenticate_soar_client(self, soar_auth: SOARClientAuth) -> None:
75
83
  """Authenticate the SOAR client with the given authentication credentials."""
@@ -21,6 +21,7 @@ def AssetField(
21
21
  default: Optional[Any] = None, # noqa: ANN401
22
22
  value_list: Optional[list] = None,
23
23
  sensitive: bool = False,
24
+ alias: Optional[str] = None,
24
25
  ) -> Any: # noqa: ANN401
25
26
  """Representation of an asset configuration field.
26
27
 
@@ -47,6 +48,7 @@ def AssetField(
47
48
  required=required,
48
49
  value_list=value_list,
49
50
  sensitive=sensitive,
51
+ alias=alias,
50
52
  )
51
53
 
52
54
 
@@ -239,7 +241,7 @@ class BaseAsset(BaseModel):
239
241
  if value_list := field.field_info.extra.get("value_list"):
240
242
  params_field["value_list"] = value_list
241
243
 
242
- params[field_name] = params_field
244
+ params[field.alias] = params_field
243
245
 
244
246
  return params
245
247
 
@@ -16,6 +16,7 @@ from rich import print as rprint
16
16
  from rich.markup import escape as rescape
17
17
 
18
18
  from soar_sdk.cli.manifests.deserializers import AppMetaDeserializer
19
+ from soar_sdk.cli.utils import normalize_field_name
19
20
  from soar_sdk.code_renderers.action_renderer import ActionRenderer
20
21
  from soar_sdk.code_renderers.app_renderer import AppContext, AppRenderer
21
22
  from soar_sdk.code_renderers.asset_renderer import AssetContext, AssetRenderer
@@ -42,7 +43,7 @@ def init_callback(
42
43
  "--python-version",
43
44
  "-p",
44
45
  help="Supported Python versions for the app.",
45
- default_factory=lambda: [str(py) for py in PythonVersion.all()],
46
+ default_factory=PythonVersion.all,
46
47
  ),
47
48
  ],
48
49
  dependencies: Annotated[list[str], typer.Option(default_factory=list)],
@@ -268,11 +269,21 @@ def convert_connector_to_sdk(
268
269
  # Convert the main module path to the SDK format, but save a reference to the original
269
270
  app_meta.main_module = "src.app:app"
270
271
 
272
+ app_python_versions = app_meta.python_version
273
+ enforced_python_versions = PythonVersion.all()
274
+ if set(app_python_versions) != set(enforced_python_versions):
275
+ rprint(
276
+ f"[yellow]The provided app declares support for Python versions {[str(v) for v in app_python_versions]}.[/]"
277
+ )
278
+ rprint(
279
+ f"[yellow]The converted app will support the default versions {[str(v) for v in enforced_python_versions]}.[/]"
280
+ )
281
+
271
282
  init_sdk_app(
272
283
  name=app_meta.project_name,
273
284
  description=app_meta.description,
274
285
  authors=[author.name for author in app_meta.contributors],
275
- python_versions=app_meta.python_version,
286
+ python_versions=enforced_python_versions,
276
287
  dependencies=[],
277
288
  app_dir=output_dir,
278
289
  copyright=app_meta.license,
@@ -385,18 +396,20 @@ def generate_asset_definition_ast(app_meta: AppMeta) -> ast.ClassDef:
385
396
  """
386
397
  asset_context: list[AssetContext] = []
387
398
  for name, config_spec in app_meta.configuration.items():
399
+ normalized = normalize_field_name(name)
388
400
  if config_spec["data_type"].startswith("ph"):
389
401
  # Skip the cosmetic placeholder fields
390
402
  continue
391
403
 
392
404
  asset_context.append(
393
405
  AssetContext(
394
- name=name,
406
+ name=normalized.normalized,
395
407
  description=config_spec.get("description"),
396
408
  required=config_spec.get("required", False),
397
409
  default=config_spec.get("default"),
398
410
  data_type=config_spec["data_type"],
399
411
  value_list=config_spec.get("value_list"),
412
+ alias=normalized.original if normalized.modified else None,
400
413
  )
401
414
  )
402
415
 
@@ -6,7 +6,7 @@ import pydantic
6
6
 
7
7
  from soar_sdk.action_results import ActionOutput, OutputFieldSpecification, OutputField
8
8
  from soar_sdk.cli.utils import normalize_field_name, NormalizationResult
9
- from soar_sdk.compat import PythonVersion
9
+ from soar_sdk.compat import PythonVersion, remove_when_soar_newer_than
10
10
  from soar_sdk.meta.actions import ActionMeta
11
11
  from soar_sdk.meta.app import AppMeta
12
12
  from soar_sdk.params import Params, Param
@@ -64,6 +64,21 @@ class AppMetaDeserializer:
64
64
  ]
65
65
  app_meta = AppMeta(project_name=json_path.parent.name, **manifest)
66
66
 
67
+ remove_when_soar_newer_than(
68
+ "7.1.0",
69
+ "Revisit this after upgrading to Pydantic 2.x. If the issue is still present, we might need to consider making `AssetFieldSpecification` a Pydantic model instead of a TypedDict, but that's a pretty big change.",
70
+ )
71
+ # Pydantic converts the default value into a string for some reason, even for non-string types.
72
+ # We will go through and convert it back.
73
+ # This has the side effect of implicitly validating that the default value matches the type.
74
+ for _, spec in app_meta.configuration.items():
75
+ if "default" not in spec:
76
+ continue
77
+ if spec["data_type"] == "numeric":
78
+ spec["default"] = float(spec["default"])
79
+ if spec["data_type"] == "boolean":
80
+ spec["default"] = bool(spec["default"])
81
+
67
82
  has_rest_handlers = isinstance(manifest.get("rest_handler"), str)
68
83
  has_webhooks = isinstance(manifest.get("webhooks"), dict)
69
84
 
@@ -142,7 +157,10 @@ class ActionDeserializer:
142
157
  if param_spec["data_type"].startswith("ph"):
143
158
  # Skip parameters that are placeholders
144
159
  continue
145
- fields[param_name] = cls._create_param_field(param_spec)
160
+ normalized = normalize_field_name(param_name)
161
+ if normalized.modified:
162
+ param_spec["alias"] = normalized.original
163
+ fields[normalized.normalized] = cls._create_param_field(param_spec)
146
164
 
147
165
  # Dynamically create a subclass of Params
148
166
  action_name = cls._clean_action_name(action_name).normalized
@@ -164,13 +182,14 @@ class ActionDeserializer:
164
182
  # Create Param field with all the metadata
165
183
  param_field = Param(
166
184
  description=param_spec.get("description"),
167
- required=param_spec.get("required", True),
185
+ required=param_spec.get("required", False),
168
186
  primary=param_spec.get("primary", False),
169
187
  default=param_spec.get("default"),
170
188
  value_list=param_spec.get("value_list"),
171
189
  cef_types=param_spec.get("contains"), # 'contains' maps to 'cef_types'
172
190
  allow_list=param_spec.get("allow_list", False),
173
191
  sensitive=(data_type == "password"),
192
+ alias=param_spec.get("alias"),
174
193
  )
175
194
 
176
195
  return FieldSpec(python_type, param_field)
@@ -32,7 +32,12 @@ def normalize_field_name(field_name: str) -> NormalizationResult:
32
32
 
33
33
  # Ensure the first character is a letter or underscore
34
34
  if field_name[0].isdigit():
35
- field_name = f"_{field_name}"
35
+ field_name = f"n{field_name}"
36
+
37
+ # Drop leading underscores to avoid Pydantic marking a field as private
38
+ field_name = field_name.lstrip("_")
39
+ if not field_name:
40
+ raise ValueError("Field name must contain at least one letter")
36
41
 
37
42
  # Finally, ensure the field name is not a Python keyword
38
43
  if keyword.iskeyword(field_name):
@@ -17,6 +17,7 @@ class AssetContext:
17
17
  default: Union[str, int, float, bool, None]
18
18
  data_type: str
19
19
  value_list: Optional[list[str]]
20
+ alias: Optional[str] = None
20
21
 
21
22
  @property
22
23
  def is_str(self) -> bool:
@@ -38,7 +39,7 @@ class AssetContext:
38
39
 
39
40
 
40
41
  class AssetRenderer(AstRenderer[list[AssetContext]]):
41
- """A class to render an app's Asset class using Jinja2 templates."""
42
+ """A class to render an app's Asset class using ASTs."""
42
43
 
43
44
  def render_ast(self) -> Iterator[ast.stmt]:
44
45
  """Render the Asset class by building an AST.
@@ -96,6 +97,11 @@ class AssetRenderer(AstRenderer[list[AssetContext]]):
96
97
  )
97
98
  )
98
99
 
100
+ if field.alias is not None:
101
+ field_kwargs.append(
102
+ ast.keyword(arg="alias", value=ast.Constant(value=field.alias))
103
+ )
104
+
99
105
  field_statement = ast.AnnAssign(
100
106
  target=field_name,
101
107
  annotation=field_type,
@@ -21,6 +21,7 @@ def Param(
21
21
  cef_types: Optional[list] = None,
22
22
  allow_list: bool = False,
23
23
  sensitive: bool = False,
24
+ alias: Optional[str] = None,
24
25
  ) -> Any: # noqa: ANN401
25
26
  """Representation of a single complex action parameter.
26
27
 
@@ -64,6 +65,7 @@ def Param(
64
65
  cef_types=cef_types,
65
66
  allow_list=allow_list,
66
67
  sensitive=sensitive,
68
+ alias=alias,
67
69
  )
68
70
 
69
71
 
@@ -135,7 +137,7 @@ class Params(BaseModel):
135
137
  if value_list := field.field_info.extra.get("value_list"):
136
138
  params_field["value_list"] = value_list
137
139
 
138
- params[field_name] = params_field
140
+ params[field.alias] = params_field
139
141
 
140
142
  return params
141
143
 
@@ -6,6 +6,7 @@ import json
6
6
 
7
7
  from typer.testing import CliRunner
8
8
 
9
+ from soar_sdk.compat import PythonVersion
9
10
  from soar_sdk.meta.actions import ActionMeta
10
11
  from soar_sdk.meta.app import AppMeta, AssetFieldSpecification
11
12
 
@@ -68,6 +69,16 @@ def test_generate_asset_definition(app_meta, tmp_path):
68
69
  data_type="timezone",
69
70
  default="UTC",
70
71
  ),
72
+ "number": AssetFieldSpecification(
73
+ label="Number",
74
+ required=False,
75
+ data_type="numeric",
76
+ default=42,
77
+ ),
78
+ "boolean": AssetFieldSpecification(
79
+ label="boolean", required=False, data_type="boolean", default=True
80
+ ),
81
+ "_underscore": AssetFieldSpecification(data_type="string"),
71
82
  }
72
83
  asset_class = cli.generate_asset_definition_ast(app_meta=app_meta)
73
84
 
@@ -78,6 +89,9 @@ def test_generate_asset_definition(app_meta, tmp_path):
78
89
  " username: str = AssetField(required=True, description='The username for the application')",
79
90
  " color: str = AssetField(required=False, default='blue', value_list=['red', 'green', 'blue'])",
80
91
  " timezone: ZoneInfo = AssetField(required=False, default=ZoneInfo('UTC'))",
92
+ " number: float = AssetField(required=False, default=42)",
93
+ " boolean: bool = AssetField(required=False, default=True)",
94
+ " underscore: str = AssetField(required=False, alias='_underscore')",
81
95
  ]
82
96
  )
83
97
 
@@ -173,6 +187,50 @@ def test_convert_cli(runner, tmp_path, app_meta):
173
187
  assert (output_dir / "logo_dark.svg").exists()
174
188
 
175
189
 
190
+ def test_convert_cli_updates_py_versions(runner, tmp_path, app_meta):
191
+ """Test that convert command generates the expected app structure."""
192
+
193
+ app_meta.configuration = {
194
+ "username": AssetFieldSpecification(
195
+ label="Username",
196
+ description="The username for the application",
197
+ required=True,
198
+ data_type="string",
199
+ )
200
+ }
201
+
202
+ app_meta.python_version = [PythonVersion.PY_3_9]
203
+
204
+ app_dir = tmp_path / "test_app"
205
+ app_dir.mkdir()
206
+ (app_dir / "app.json").write_text(app_meta.json())
207
+ (app_dir / app_meta.logo).touch()
208
+ (app_dir / app_meta.logo_dark).touch()
209
+
210
+ output_dir = tmp_path / "output"
211
+
212
+ with patch("subprocess.run"), patch("shutil.which") as mock_which:
213
+ mock_which.return_value = "/usr/bin/example"
214
+
215
+ result = runner.invoke(
216
+ cli.convert,
217
+ [
218
+ str(app_dir),
219
+ str(output_dir),
220
+ ],
221
+ )
222
+
223
+ print(result.output) # For debugging purposes
224
+
225
+ assert result.exit_code == 0
226
+ assert "declares support for Python versions ['3.9']" in result.output
227
+ assert "will support the default versions ['3.9', '3.13']" in result.output
228
+ assert (
229
+ 'requires-python = ">=3.9, <3.14"'
230
+ in (output_dir / "pyproject.toml").read_text()
231
+ )
232
+
233
+
176
234
  def test_convert_cli_with_default_output(runner, tmp_path, app_meta):
177
235
  """Test that convert command uses default output directory if not specified."""
178
236
 
@@ -278,7 +278,21 @@ def test_from_app_json_complex_example(create_app_json):
278
278
  "required": True,
279
279
  "description": "Base Url",
280
280
  "order": 0,
281
- }
281
+ },
282
+ "port": {
283
+ "data_type": "numeric",
284
+ "required": False,
285
+ "description": "Port Number",
286
+ "default": 8080,
287
+ "order": 1,
288
+ },
289
+ "verify": {
290
+ "data_type": "boolean",
291
+ "required": False,
292
+ "description": "Verify",
293
+ "default": True,
294
+ "order": 2,
295
+ },
282
296
  },
283
297
  "actions": [
284
298
  {
@@ -313,6 +327,9 @@ def test_from_app_json_complex_example(create_app_json):
313
327
  assert len(result.actions) == 1
314
328
  assert result.fips_compliant is False
315
329
  assert result.configuration["base_url"]["data_type"] == "string"
330
+ assert result.configuration["port"]["default"] == 8080
331
+ assert type(result.configuration["verify"]["default"]) is bool
332
+ assert result.configuration["verify"]["default"]
316
333
 
317
334
 
318
335
  @pytest.mark.parametrize(
@@ -406,7 +423,19 @@ def test_from_action_json_with_parameters_and_output(mock_action_deserializer):
406
423
  "data_type": "string",
407
424
  "required": True,
408
425
  "description": "Test parameter",
409
- }
426
+ },
427
+ "param2": {
428
+ "data_type": "numeric",
429
+ "required": False,
430
+ "description": "Another parameter",
431
+ "default": 42,
432
+ },
433
+ "param3": {
434
+ "data_type": "boolean",
435
+ "required": False,
436
+ "description": "Yet another parameter",
437
+ "default": True,
438
+ },
410
439
  },
411
440
  "output": [
412
441
  {"data_path": "action_result.data.*.result"},
@@ -427,6 +456,23 @@ def test_from_action_json_with_parameters_and_output(mock_action_deserializer):
427
456
  "custom action", original_parameters
428
457
  )
429
458
  mocks["parse_output"].assert_called_once_with("custom action", original_output)
459
+ assert original_parameters["param1"] == {
460
+ "data_type": "string",
461
+ "description": "Test parameter",
462
+ "required": True,
463
+ }
464
+ assert original_parameters["param2"] == {
465
+ "data_type": "numeric",
466
+ "description": "Another parameter",
467
+ "required": False,
468
+ "default": 42,
469
+ }
470
+ assert original_parameters["param3"] == {
471
+ "data_type": "boolean",
472
+ "description": "Yet another parameter",
473
+ "required": False,
474
+ "default": True,
475
+ }
430
476
 
431
477
 
432
478
  def test_from_action_json_missing_optional_fields(mock_action_deserializer):
@@ -502,6 +548,28 @@ def test_action_deserializer_parse_parameters(input_data, expects_base_class):
502
548
  assert result.__name__ == "TestActionParams"
503
549
 
504
550
 
551
+ def test_action_deserializer_uses_correct_default_params():
552
+ """Test ActionDeserializer sets `required`, `primary`, and `allow_list` false when no defaults are provided."""
553
+ result = ActionDeserializer.parse_parameters(
554
+ "test_action", {"param1": {"data_type": "string", "description": "test param"}}
555
+ )
556
+
557
+ field = result.__fields__["param1"]
558
+ assert not field.field_info.extra.get("required")
559
+ assert not field.field_info.extra.get("primary")
560
+ assert not field.field_info.extra.get("allow_list")
561
+
562
+
563
+ def test_action_deserializer_with_underscored_params():
564
+ """Test that the ActionDeserializer handles underscored parameters as expected"""
565
+ result = ActionDeserializer.parse_parameters(
566
+ "test_action", {"_underscore": {"data_type": "string"}}
567
+ )
568
+
569
+ field = result.__fields__["underscore"]
570
+ assert field.alias == "_underscore"
571
+
572
+
505
573
  @pytest.mark.parametrize(
506
574
  "input_data,expects_base_class",
507
575
  [
@@ -619,6 +687,12 @@ def test_parse_parameters_with_complex_params():
619
687
  "description": "Placeholder parameter",
620
688
  "order": 3,
621
689
  },
690
+ "_param5": {
691
+ "data_type": "string",
692
+ "required": False,
693
+ "description": "Placeholder parameter",
694
+ "order": 3,
695
+ },
622
696
  }
623
697
 
624
698
  result = ActionDeserializer.parse_parameters("complex_action", complex_params)
@@ -633,6 +707,10 @@ def test_parse_parameters_with_complex_params():
633
707
  assert "param1" in result.__annotations__
634
708
  assert "param2" in result.__annotations__
635
709
  assert "param3" in result.__annotations__
710
+ assert "param4" not in result.__annotations__
711
+
712
+ assert "param5" in result.__fields__
713
+ assert result.__fields__["param5"].alias == "_param5"
636
714
 
637
715
 
638
716
  def test_complex_output_specification():
@@ -41,6 +41,7 @@ def test_params_serialize_fields_info():
41
41
  send_notifications: bool = Param(default=True)
42
42
  platform: str = Param(value_list=["windows", "linux", "mac"])
43
43
  api_key: str = Param(sensitive=True)
44
+ underscored_field: str = Param(alias="_underscored_field")
44
45
 
45
46
  serialized_params = ParamsSerializer.serialize_fields_info(SampleParams)
46
47
 
@@ -112,6 +113,15 @@ def test_params_serialize_fields_info():
112
113
  "allow_list": False,
113
114
  "order": 6,
114
115
  },
116
+ "_underscored_field": {
117
+ "name": "underscored_field",
118
+ "description": "Underscored Field",
119
+ "data_type": "string",
120
+ "required": True,
121
+ "primary": False,
122
+ "allow_list": False,
123
+ "order": 7,
124
+ },
115
125
  }
116
126
 
117
127
  assert serialized_params == expected_params
@@ -152,6 +162,7 @@ def test_outputs_serialize_output_class():
152
162
  list_value: list[str]
153
163
  cef_value: str = OutputField(cef_types=["ip"], example_values=["1.1.1.1"])
154
164
  nested_value: SampleNestedOutput
165
+ underscored_value: str = OutputField(alias="_underscored_value")
155
166
 
156
167
  serialized_outputs = OutputsSerializer.serialize_datapaths(Params, SampleOutput)
157
168
 
@@ -188,6 +199,10 @@ def test_outputs_serialize_output_class():
188
199
  "data_type": "boolean",
189
200
  "example_values": [True, False],
190
201
  },
202
+ {
203
+ "data_path": "action_result.data.*._underscored_value",
204
+ "data_type": "string",
205
+ },
191
206
  {
192
207
  "data_path": "summary.total_objects",
193
208
  "data_type": "numeric",
@@ -13,21 +13,21 @@ class TestNormalizeFieldName:
13
13
  # Valid identifiers that shouldn't be modified
14
14
  ("valid_name", "valid_name", False),
15
15
  ("ValidName", "ValidName", False),
16
- ("_private", "_private", False),
17
16
  ("name123", "name123", False),
18
- ("_", "_", False),
19
- ("__dunder__", "__dunder__", False),
17
+ # Names starting with underscores
18
+ ("_private", "private", True),
19
+ ("__dunder__", "dunder__", True),
20
20
  # Names starting with digits
21
- ("123invalid", "_123invalid", True),
21
+ ("123invalid", "n123invalid", True),
22
22
  # Names with invalid characters
23
23
  (
24
24
  "a-b.c d@e$f#g%h!i+j=k/l\\m|n&o*p(q)r[s]t{u}v<w>x:y;z,?'\"",
25
25
  "a_b_c_d_e_f_g_h_i_j_k_l_m_n_o_p_q_r_s_t_u_v_w_x_y_z____",
26
26
  True,
27
27
  ),
28
- ("🚀rocket-ship🚀", "_rocket_ship_", True),
28
+ ("🚀rocket-ship🚀", "rocket_ship_", True),
29
29
  # Mixed issues: starting with digit and invalid characters
30
- ("123field@name", "_123field_name", True),
30
+ ("123field@name", "n123field_name", True),
31
31
  ],
32
32
  )
33
33
  def test_basic_normalization(
@@ -41,7 +41,9 @@ class TestNormalizeFieldName:
41
41
  assert result.modified == expected_modified
42
42
  assert result.normalized.isidentifier()
43
43
 
44
- @pytest.mark.parametrize("keyword", keyword.kwlist)
44
+ @pytest.mark.parametrize(
45
+ "keyword", [kw for kw in keyword.kwlist if not kw.startswith("_")]
46
+ )
45
47
  def test_python_keywords(self, keyword: str):
46
48
  """Test that Python keywords get an underscore appended."""
47
49
  result = normalize_field_name(keyword)
@@ -55,3 +57,6 @@ class TestNormalizeFieldName:
55
57
  """Test edge cases and boundary conditions."""
56
58
  with pytest.raises(ValueError, match="empty"):
57
59
  normalize_field_name("")
60
+
61
+ with pytest.raises(ValueError, match="must contain at least one letter"):
62
+ normalize_field_name("______")
@@ -43,6 +43,8 @@ app = App(
43
43
  @app.test_connectivity()
44
44
  def test_connectivity(soar: SOARClient, asset: Asset) -> None:
45
45
  soar.get("rest/version")
46
+ container_id = soar.get_executing_container_id()
47
+ logger.info(f"current executing container's container_id is: {container_id}")
46
48
  logger.info(f"testing connectivity against {asset.base_url}")
47
49
  logger.debug("hello")
48
50
  logger.warning("this is a warning")
@@ -198,7 +198,8 @@ def test_action_decoration_uses_meta_identifier_for_action_name(simple_app):
198
198
  def test_action_with_mocked_client(simple_app, sample_params):
199
199
  @simple_app.action()
200
200
  def action_function(params: SampleParams, soar: SOARClient) -> ActionOutput:
201
- soar.save_progress("Progress was made")
201
+ container_id = str(soar.get_executing_container_id())
202
+ soar.save_progress(f"Container ID is: {container_id}")
202
203
 
203
204
  client_mock = mock.Mock()
204
205
  client_mock.save_progress = mock.Mock()
@@ -31,3 +31,7 @@ def test_authenticate_soar_client_on_platform(
31
31
  ):
32
32
  simple_connector.authenticate_soar_client(soar_client_auth_token)
33
33
  assert mock_get_any_soar_call.call_count == 1
34
+
35
+
36
+ def test_get_executing_container_id(simple_connector: AppClient):
37
+ assert simple_connector.get_executing_container_id() == 0