Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
8ec1f6d
added agentix action to test tools
mayyagoldman Oct 15, 2025
f37b3ba
changelog
mayyagoldman Oct 15, 2025
cff1ecd
pre-commit
mayyagoldman Oct 15, 2025
c9448bd
Update demisto_sdk/commands/validate/tests/test_tools.py
mayyagoldman Oct 15, 2025
05096c2
pr comment
mayyagoldman Oct 15, 2025
e8a437d
Merge remote-tracking branch 'origin/CRTX-189927/action' into CRTX-18…
mayyagoldman Oct 15, 2025
5307947
pr comment
mayyagoldman Oct 15, 2025
f95ea55
added validations to check uniqness of agentixactions name and displa…
mayyagoldman Oct 15, 2025
c72dd65
pre commit
mayyagoldman Oct 15, 2025
57bc3d3
pre commit
mayyagoldman Oct 15, 2025
651c1da
https://github.com/demisto/demisto-sdk/pull/5088#discussion_r2432284348
mayyagoldman Oct 15, 2025
5aba426
changelog
mayyagoldman Oct 15, 2025
47f7fa3
Merge branch 'master' into check-uniqe-action-name
mayyagoldman Oct 15, 2025
aa17797
kobys comments
mayyagoldman Oct 15, 2025
00a2c0d
pre commit
mayyagoldman Oct 15, 2025
8a92cd7
pre commit
mayyagoldman Oct 15, 2025
e2db9d5
RENAME
mayyagoldman Oct 15, 2025
172f547
Merge remote-tracking branch 'origin/check-uniqe-action-name' into ch…
mayyagoldman Oct 15, 2025
bb0ccc7
pre commit
mayyagoldman Oct 15, 2025
2903544
test path
mayyagoldman Oct 15, 2025
0884d8d
remove extra
mayyagoldman Oct 15, 2025
6a87455
fixes
mayyagoldman Oct 15, 2025
83580a2
Merge branch 'master' into CRTX-189927/action
mayyagoldman Oct 15, 2025
c3d60b8
pre commit
mayyagoldman Oct 15, 2025
d3bbc2b
Merge branch 'CRTX-189927/action' into check-uniqe-action-name
mayyagoldman Oct 16, 2025
18b0d6a
Merge branch 'master' into check-uniqe-action-name
mayyagoldman Oct 16, 2025
3cb6fec
agentix action added to the pack
mayyagoldman Oct 19, 2025
9984889
Merge remote-tracking branch 'origin/check-uniqe-action-name' into ch…
mayyagoldman Oct 19, 2025
caf6225
added test
mayyagoldman Oct 19, 2025
bf9d2d6
Merge branch 'master' into check-uniqe-action-name
mayyagoldman Oct 21, 2025
02a086a
Merge branch 'master' into check-uniqe-action-name
mayyagoldman Nov 2, 2025
66efb3d
test
mayyagoldman Nov 3, 2025
354a905
test
mayyagoldman Nov 3, 2025
65669a7
test
mayyagoldman Nov 3, 2025
b7b67b5
FIXED FAILING TESTS
mayyagoldman Nov 9, 2025
f895b4e
merge
mayyagoldman Nov 20, 2025
c798cf1
pre commit
mayyagoldman Nov 20, 2025
769eab5
changed path to path not string
mayyagoldman Nov 20, 2025
e0a1684
changed path to path not string
mayyagoldman Nov 20, 2025
ac00fac
changed path to path not string
mayyagoldman Nov 20, 2025
508b53f
changed path to path not string
mayyagoldman Nov 20, 2025
ba3e12a
changed path to path not string
mayyagoldman Nov 20, 2025
d6241ed
changed path to path not string
mayyagoldman Nov 20, 2025
d4cb77b
changed action test class
mayyagoldman Nov 20, 2025
d9861a4
changed action test class
mayyagoldman Nov 20, 2025
fe5d952
pre commit
mayyagoldman Nov 20, 2025
257e981
pre commit
mayyagoldman Nov 20, 2025
ec21891
pre commit
mayyagoldman Nov 20, 2025
5eb8720
added object_id
mayyagoldman Nov 20, 2025
6940b5e
added object_id
mayyagoldman Nov 20, 2025
0f4ad5e
Merge branch 'master' into check-uniqe-action-name
mayyagoldman Nov 23, 2025
75538e8
self.node_id = name
mayyagoldman Nov 23, 2025
b9fdfa1
Merge remote-tracking branch 'origin/check-uniqe-action-name' into ch…
mayyagoldman Nov 23, 2025
6c3a0c1
Merge branch 'master' into working-state-CRTX-201484
mayyagoldman Dec 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .changelog/5093.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
changes:
- description: Added new AG109 and AG110 validations.
type: internal
pr_number: 5093
25 changes: 20 additions & 5 deletions TestSuite/agentix_action.py
Original file line number Diff line number Diff line change
@@ -1,30 +1,35 @@
from pathlib import Path
from typing import Optional

from demisto_sdk.commands.common.tools import set_value
from TestSuite.test_suite_base import TestSuiteBase
from TestSuite.yml import YAML, yaml


class AgentixAction(YAML):
class AgentixAction(TestSuiteBase):
def __init__(self, tmpdir: Path, name: str, repo):
# Save entities
self.object_id = name
self.name = name
self.node_id = name
self._repo = repo
self.repo_path = repo.path
self.path = str(tmpdir)
super().__init__(tmp_path=tmpdir / f"{self.name}.yml", repo_path=str(repo.path))
self.path = tmpdir / f"{self.name}.yml"
self.yaml = YAML(tmp_path=self.path, repo_path=str(repo.path))
super().__init__(self.path)

@property
def yml(self):
# for backward compatible
return self
return self.yaml

def build(
self,
yml: Optional[dict] = None,
):
"""Writes not None objects to files."""
if yml is not None:
self.write_dict(yml)
self.yaml.write_dict(yml)

def create_default_agentix_action(
self,
Expand All @@ -44,3 +49,13 @@ def create_default_agentix_action(
yml["id"] = action_id
yml["name"] = name
self.build(yml=yml)

def set_agentix_action_name(self, name: str):
self.yml.update({"name": name})

def set_data(self, **key_path_to_val):
yml_contents = self.yml.read_dict()
for key_path, val in key_path_to_val.items():
set_value(yml_contents, key_path, val)
self.yml.write_dict(yml_contents)
self.clear_from_path_cache()
7 changes: 7 additions & 0 deletions TestSuite/repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ def __init__(self, tmpdir: Path, init_git: bool = False):
"GenericDefinitions": [],
"Jobs": [],
"Wizards": [],
"AgentixActions": [],
}
)
self.graph_interface: Optional[ContentGraphInterface] = None
Expand Down Expand Up @@ -116,6 +117,12 @@ def setup_one_pack(
pack = self.create_pack(name)
pack.pack_metadata.update({"marketplaces": marketplaces})

agentix_action = pack.create_agentix_action(f"{name}_agentix_action")
agentix_action.create_default_agentix_action()
agentix_action.yml.update({"commonfields": {"id": f"{name}_agentix_action"}})
agentix_action.yml.update({"name": f"{name}_agentix_action"})
agentix_action.yml.update({"display": f"{name}_agentix_action"})

script = pack.create_script(f"{name}_script")
script.create_default_script()
script.yml.update({"commonfields": {"id": f"{name}_script"}})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@
validate_duplicate_ids,
validate_fromversion,
validate_marketplaces,
validate_multiple_agentix_actions_with_same_display_name,
validate_multiple_agentix_actions_with_same_name,
validate_multiple_packs_with_same_display_name,
validate_multiple_script_with_same_name,
validate_packs_with_hidden_mandatory_dependencies,
Expand Down Expand Up @@ -494,6 +496,24 @@ def validate_duplicate_ids(
duplicate_models.append((self._id_to_obj[content_item.element_id], dups))
return duplicate_models

def validate_duplicate_agentix_action_display_names(
self, file_paths: List[str]
) -> List[Tuple[str, List[str]]]:
with self.driver.session() as session:
results = session.execute_read(
validate_multiple_agentix_actions_with_same_display_name, file_paths
)
return results

def validate_duplicate_agentix_action_names(
self, file_paths: List[str]
) -> List[Tuple[str, List[str]]]:
with self.driver.session() as session:
results = session.execute_read(
validate_multiple_agentix_actions_with_same_name, file_paths
)
return results

def find_uses_paths_with_invalid_fromversion(
self, file_paths: List[str], for_supported_versions=False
) -> List[BaseNode]:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,44 @@ def validate_multiple_packs_with_same_display_name(
]


def validate_multiple_agentix_actions_with_same_display_name(
tx: Transaction, file_paths: List[str]
) -> List[Tuple[str, List[str]]]:
query = f"""// Returns all the Agentix Actions that have the same display name but different id
MATCH (a:{ContentType.AGENTIX_ACTION}), (b:{ContentType.AGENTIX_ACTION})
WHERE a.display = b.display
"""
if file_paths:
query += f"AND a.path in {file_paths}"
query += """
AND elementId(a) <> elementId(b)
RETURN a.object_id AS a_object_id, collect(b.object_id) AS b_object_ids
"""
return [
(item.get("a_object_id"), item.get("b_object_ids"))
for item in run_query(tx, query)
]


def validate_multiple_agentix_actions_with_same_name(
tx: Transaction, file_paths: List[str]
) -> List[Tuple[str, List[str]]]:
query = f"""// Returns all the Agentix Actions that have the same name but different id
MATCH (a:{ContentType.AGENTIX_ACTION}), (b:{ContentType.AGENTIX_ACTION})
WHERE a.name = b.name
"""
if file_paths:
query += f"AND a.path in {file_paths}"
query += """
AND elementId(a) <> elementId(b)
RETURN a.object_id AS a_object_id, collect(b.object_id) AS b_object_ids
"""
return [
(item.get("a_object_id"), item.get("b_object_ids"))
for item in run_query(tx, query)
]


def validate_multiple_script_with_same_name(
tx: Transaction, file_paths: List[str]
) -> Dict[str, str]:
Expand Down
2 changes: 2 additions & 0 deletions demisto_sdk/commands/validate/sdk_validation_config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,8 @@ select = [
"AG101",
"AG102",
"AG103",
"AG109",
"AG110",
"AG105",
"AG106",
"AG107",
Expand Down
43 changes: 43 additions & 0 deletions demisto_sdk/commands/validate/tests/AG_validators_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import pytest

from demisto_sdk.commands.common.hook_validations.base_validator import BaseValidator
from demisto_sdk.commands.content_graph.objects.agentix_action import (
AgentixAction,
)
Expand All @@ -10,6 +11,9 @@
from demisto_sdk.commands.validate.tests.test_tools import (
create_agentix_action_object,
)
from demisto_sdk.commands.validate.validators.AG_validators import (
AG110_is_agentix_action_name_already_exists_valid,
)
from demisto_sdk.commands.validate.validators.AG_validators.AG100_is_forbidden_content_item import (
IsForbiddenContentItemValidator,
)
Expand All @@ -25,6 +29,10 @@
from demisto_sdk.commands.validate.validators.AG_validators.AG107_is_display_name_valid import (
IsDisplayNameValidValidator,
)
from demisto_sdk.commands.validate.validators.AG_validators.AG110_is_agentix_action_name_already_exists_valid import (
IsAgentixActionNameAlreadyExistsValidator,
)
from TestSuite.repo import Repo


def test_is_forbidden_content_item():
Expand Down Expand Up @@ -383,6 +391,41 @@ def test_is_type_valid():
) in results[1].message


def test_IsAgentixActionNameAlreadyExistsValidator_obtain_invalid_content_items_using_graph(
mocker, graph_repo: Repo
):
"""
Given
- 3 packs, with 1 agentix action in each, and 2 of them are with the same name
When
- running IsAgentixActionNameAlreadyExistsValidator obtain_invalid_content_items function, on one of the packs with the duplicate agentix action name.
Then
- Validate that we got the error messages for the duplicate name.
"""
mocker.patch.object(
AG110_is_agentix_action_name_already_exists_valid,
"CONTENT_PATH",
new=graph_repo.path,
)
graph_repo.setup_one_pack(name="pack1")
graph_repo.setup_one_pack(name="pack2")
graph_repo.setup_one_pack(name="pack3")
graph_repo.packs[1].agentix_actions[0].set_agentix_action_name("test")
graph_repo.packs[2].agentix_actions[0].set_agentix_action_name("test")

BaseValidator.graph_interface = graph_repo.create_graph()

results = IsAgentixActionNameAlreadyExistsValidator().obtain_invalid_content_items_using_graph(
[
graph_repo.packs[0].agentix_actions[0],
graph_repo.packs[2].agentix_actions[0],
],
validate_all_files=False,
)

assert len(results) == 1


@pytest.mark.parametrize(
"content_items, expected_number_of_failures",
[
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from __future__ import annotations

from abc import ABC
from typing import Iterable, List

from demisto_sdk.commands.common.content_constant_paths import CONTENT_PATH
from demisto_sdk.commands.content_graph.objects.agentix_action import AgentixAction
from demisto_sdk.commands.validate.validators.base_validator import (
BaseValidator,
ValidationResult,
)

ContentTypes = AgentixAction


class IsAgentixActionDisplayNameAlreadyExistsValidator(
BaseValidator[ContentTypes], ABC
):
error_code = "AG109"
description = "Validate that there are no duplicate display names of Agentix Actions in the repo."
rationale = "Prevent confusion between Agentix Actions."
error_message = "Agentix Action '{content_id}' has a duplicate display name as: {duplicate_display_name_ids}."
related_field = "display"
is_auto_fixable = False

def obtain_invalid_content_items_using_graph(
self, content_items: Iterable[ContentTypes], validate_all_files: bool
) -> List[ValidationResult]:
file_paths_to_objects = {
str(content_item.path.relative_to(CONTENT_PATH)): content_item
for content_item in content_items
}
content_id_to_objects = {item.object_id: item for item in content_items} # type: ignore[attr-defined]

query_list = list(file_paths_to_objects) if not validate_all_files else []

query_results = self.graph.validate_duplicate_agentix_action_display_names(
query_list
)

return [
ValidationResult(
validator=self,
message=self.error_message.format(
content_id=content_id,
duplicate_display_name_ids=(", ".join(duplicate_display_name_ids)),
),
content_object=content_id_to_objects[content_id],
)
for content_id, duplicate_display_name_ids in query_results
if content_id in content_id_to_objects
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from __future__ import annotations

from typing import Iterable, List

from demisto_sdk.commands.common.constants import ExecutionMode
from demisto_sdk.commands.content_graph.objects.integration import Integration
from demisto_sdk.commands.validate.validators.AG_validators.AG109_is_agentix_action_display_name_already_exists_valid import (
IsAgentixActionDisplayNameAlreadyExistsValidator,
)
from demisto_sdk.commands.validate.validators.base_validator import ValidationResult

ContentTypes = Integration


class IsAgentixActionDisplayNameAlreadyExistsValidatorAllFiles(
IsAgentixActionDisplayNameAlreadyExistsValidator
):
expected_execution_mode = [ExecutionMode.ALL_FILES]

def obtain_invalid_content_items(
self, content_items: Iterable[ContentTypes]
) -> List[ValidationResult]:
return self.obtain_invalid_content_items_using_graph(content_items, True)
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from __future__ import annotations

from typing import Iterable, List

from demisto_sdk.commands.common.constants import ExecutionMode
from demisto_sdk.commands.content_graph.objects.integration import Integration
from demisto_sdk.commands.validate.validators.AG_validators.AG109_is_agentix_action_display_name_already_exists_valid import (
IsAgentixActionDisplayNameAlreadyExistsValidator,
)
from demisto_sdk.commands.validate.validators.base_validator import ValidationResult

ContentTypes = Integration


class IsAgentixActionDisplayNameAlreadyExistsValidatorListFiles(
IsAgentixActionDisplayNameAlreadyExistsValidator
):
expected_execution_mode = [ExecutionMode.SPECIFIC_FILES, ExecutionMode.USE_GIT]

def obtain_invalid_content_items(
self, content_items: Iterable[ContentTypes]
) -> List[ValidationResult]:
return self.obtain_invalid_content_items_using_graph(content_items, False)
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from __future__ import annotations

from abc import ABC
from typing import Iterable, List

from demisto_sdk.commands.common.content_constant_paths import CONTENT_PATH
from demisto_sdk.commands.content_graph.objects.agentix_action import AgentixAction
from demisto_sdk.commands.validate.validators.base_validator import (
BaseValidator,
ValidationResult,
)

ContentTypes = AgentixAction


class IsAgentixActionNameAlreadyExistsValidator(BaseValidator[ContentTypes], ABC):
error_code = "AG110"
description = (
"Validate that there are no duplicate names of Agentix Actions in the repo."
)
rationale = "Prevent confusion between Agentix Actions."
error_message = (
"Agentix Action '{content_id}' has a duplicate name as: {duplicate_name_ids}."
)
related_field = "name"
is_auto_fixable = False

def obtain_invalid_content_items_using_graph(
self, content_items: Iterable[ContentTypes], validate_all_files: bool
) -> List[ValidationResult]:
file_paths_to_objects = {
str(content_item.path.relative_to(CONTENT_PATH)): content_item
for content_item in content_items
}
content_id_to_objects = {item.object_id: item for item in content_items} # type: ignore[attr-defined]

query_list = list(file_paths_to_objects) if not validate_all_files else []

query_results = self.graph.validate_duplicate_agentix_action_names(query_list)

return [
ValidationResult(
validator=self,
message=self.error_message.format(
content_id=content_id,
duplicate_name_ids=(", ".join(duplicate_name_ids)),
),
content_object=content_id_to_objects[content_id],
)
for content_id, duplicate_name_ids in query_results
if content_id in content_id_to_objects
]
Loading
Loading