Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 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
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
5 changes: 5 additions & 0 deletions TestSuite/agentix_action.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,8 @@ 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})


12 changes: 10 additions & 2 deletions TestSuite/repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ def __init__(self, tmpdir: Path, init_git: bool = False):
"GenericDefinitions": [],
"Jobs": [],
"Wizards": [],
"AgentixActions": []
}
)
self.graph_interface: Optional[ContentGraphInterface] = None
Expand All @@ -100,7 +101,7 @@ def __del__(self):
self.graph_interface.close()

def setup_one_pack(
self, name: Optional[str] = None, marketplaces: List[str] = DEFAULT_MARKETPLACES
self, name: Optional[str] = None, marketplaces: List[str] = DEFAULT_MARKETPLACES
) -> Pack:
"""Sets up a new pack in the repo, and includes one per each content entity.

Expand All @@ -115,6 +116,13 @@ 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 Expand Up @@ -298,7 +306,7 @@ def setup_one_pack(
return pack

def setup_content_repo(
self, number_of_packs, marketplaces: List[str] = DEFAULT_MARKETPLACES
self, number_of_packs, marketplaces: List[str] = DEFAULT_MARKETPLACES
):
"""Creates a fully constructed content repository, where packs names will pack_<index>.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,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 @@ -482,6 +484,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 @@ -323,6 +323,8 @@ select = [
"AG101",
"AG102",
"AG103",
"AG109",
"AG110",
"PA100",
"PA101",
"PA102",
Expand Down
39 changes: 39 additions & 0 deletions demisto_sdk/commands/validate/tests/AG_validators_test.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
from pathlib import Path

from TestSuite.repo import Repo
from demisto_sdk.commands.common.hook_validations.base_validator import BaseValidator
from demisto_sdk.commands.content_graph.objects.agentix_action import AgentixAction
from demisto_sdk.commands.content_graph.objects.agentix_agent import AgentixAgent
from demisto_sdk.commands.content_graph.objects.script import Script
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,
)
from demisto_sdk.commands.validate.validators.AG_validators.AG101_is_correct_mp import (
IsCorrectMPValidator,
)
from demisto_sdk.commands.validate.validators.AG_validators.AG110_is_agentix_action_name_already_exists_valid import \
IsAgentixActionNameAlreadyExistsValidator


def test_is_forbidden_content_item():
Expand Down Expand Up @@ -215,3 +220,37 @@
assert results[0].message == (
"The following Agentix related content item 'test' should have only marketplace 'platform'."
)


def test_IsAgentixActionNameAlreadyExistsValidator_obtain_invalid_content_items(
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(

Check failure on line 251 in demisto_sdk/commands/validate/tests/AG_validators_test.py

View workflow job for this annotation

GitHub Actions / Unit Tests / Python 3.9

test_IsAgentixActionNameAlreadyExistsValidator_obtain_invalid_content_items NotImplementedError

Check failure on line 251 in demisto_sdk/commands/validate/tests/AG_validators_test.py

View workflow job for this annotation

GitHub Actions / Unit Tests / Python 3.12

test_IsAgentixActionNameAlreadyExistsValidator_obtain_invalid_content_items NotImplementedError

Check failure on line 251 in demisto_sdk/commands/validate/tests/AG_validators_test.py

View workflow job for this annotation

GitHub Actions / Unit Tests / Python 3.11

test_IsAgentixActionNameAlreadyExistsValidator_obtain_invalid_content_items NotImplementedError

Check failure on line 251 in demisto_sdk/commands/validate/tests/AG_validators_test.py

View workflow job for this annotation

GitHub Actions / Unit Tests / Python 3.10

test_IsAgentixActionNameAlreadyExistsValidator_obtain_invalid_content_items NotImplementedError
[graph_repo.packs[0], graph_repo.packs[2]]
)
)

assert len(results) == 1
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_display_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
]
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.AG110_is_agentix_action_name_already_exists_valid import (
IsAgentixActionNameAlreadyExistsValidator,
)
from demisto_sdk.commands.validate.validators.base_validator import ValidationResult

ContentTypes = Integration


class IsAgentixActionNameAlreadyExistsValidatorAllFiles(
IsAgentixActionNameAlreadyExistsValidator
):
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)
Loading
Loading