Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Validate] Refactor GF #4092

Merged
merged 12 commits into from
Mar 21, 2024
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ class GenericField(ContentItem, content_type=ContentType.GENERIC_FIELD): # type
definition_id: Optional[str] = Field(alias="definitionId")
field_type: Optional[str] = Field(alias="type")
version: Optional[int] = 0
group: int = Field(None, exclude=True)

def metadata_fields(self) -> Set[str]:
return super().metadata_fields().union({"field_type"})
Expand Down
11 changes: 10 additions & 1 deletion demisto_sdk/commands/content_graph/parsers/generic_field.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from functools import cached_property
from pathlib import Path
from typing import List, Optional, Set

Expand All @@ -18,9 +19,13 @@ def __init__(
super().__init__(path, pack_marketplaces, git_sha=git_sha)
self.definition_id = self.json_data.get("definitionId")
self.field_type = self.json_data.get("type") or ""

self.connect_to_dependencies()

@cached_property
def field_mapping(self):
super().field_mapping.update({"group": "group"})
return super().field_mapping

@property
def supported_marketplaces(self) -> Set[MarketplaceVersions]:
return {
Expand All @@ -29,6 +34,10 @@ def supported_marketplaces(self) -> Set[MarketplaceVersions]:
MarketplaceVersions.XSOAR_ON_PREM,
}

@property
def group(self) -> Optional[int]:
return self.json_data.get("group")

def connect_to_dependencies(self) -> None:
"""Collects the generic types associated to the generic field as optional dependencies."""
for associated_type in set(self.json_data.get("associatedTypes") or []):
Expand Down
67 changes: 67 additions & 0 deletions demisto_sdk/commands/validate/tests/GF_validators_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
from demisto_sdk.commands.validate.tests.test_tools import create_generic_field_object
from demisto_sdk.commands.validate.validators.GF_validators.GF100_generic_field_group import (
REQUIRED_GROUP_VALUE,
GenericFieldGroupValidator,
)
from demisto_sdk.commands.validate.validators.GF_validators.GF101_generic_field_id_prefix_validate import (
GenericFieldIdPrefixValidateValidator,
)


def test_GenericFieldIdPrefixValidateValidator_is_valid():
"""
Given:
- GenericField content items
When:
- run is_valid method
Then:
- Ensure that the ValidationResult returned
for the GenericField whose 'id' without `generic_` prefix
- Ensure that no ValidationResult returned
when `id` field with `generic_` prefix
"""
generic_field = create_generic_field_object(paths=["id"], values=["foo"])

# not valid
results = GenericFieldIdPrefixValidateValidator().is_valid([generic_field])
assert results[0].message == "foo is not a valid id, it should start with generic_."

# valid
generic_field.object_id = "generic_foo"
assert not GenericFieldIdPrefixValidateValidator().is_valid([generic_field])


def test_GenericFieldGroupValidator_is_valid():
"""
Given:
- GenericField content items
When:
- run is_valid method
Then:
- Ensure that the ValidationResult returned
for the GenericField whose 'group' field is not valid
- Ensure that no ValidationResult returned when group field set to 4
"""
# not valid
generic_field = create_generic_field_object(paths=["group"], values=[0])
assert GenericFieldGroupValidator().is_valid([generic_field])

# valid
generic_field.group = REQUIRED_GROUP_VALUE
assert not GenericFieldGroupValidator().is_valid([generic_field])


def test_GenericFieldGroupValidator_fix():
"""
Given:
- invalid GenericField that 'group' field is not 4
When:
- run fix method
Then:
- Ensure the fix message as expected
- Ensure the field `group` is set to 4
"""
generic_field = create_generic_field_object(paths=["group"], values=["0"])
result = GenericFieldGroupValidator().fix(generic_field)
assert result.message == f"set the `group` field to {REQUIRED_GROUP_VALUE}."
assert generic_field.group == REQUIRED_GROUP_VALUE
5 changes: 3 additions & 2 deletions demisto_sdk/commands/validate/tests/test_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from demisto_sdk.commands.common.handlers import DEFAULT_JSON_HANDLER as json
from demisto_sdk.commands.common.tools import set_value
from demisto_sdk.commands.content_graph.objects.base_content import BaseContent
from demisto_sdk.commands.content_graph.objects.generic_field import GenericField
from demisto_sdk.commands.content_graph.objects.incident_field import IncidentField
from demisto_sdk.commands.content_graph.objects.integration import Integration
from demisto_sdk.commands.content_graph.objects.pack import Pack
Expand Down Expand Up @@ -590,7 +591,7 @@ def create_generic_definition_object(

def create_generic_field_object(
paths: Optional[List[str]] = None, values: Optional[List[Any]] = None
):
) -> GenericField:
"""Creating an generic_field object with altered fields from a default generic_field json structure.

Args:
Expand All @@ -604,7 +605,7 @@ def create_generic_field_object(
update_keys(json_content, paths, values)
pack = REPO.create_pack()
pack.create_generic_field(name="generic_field", content=json_content)
return BaseContent.from_path(Path(pack.generic_fields[0].path))
return cast(GenericField, BaseContent.from_path(Path(pack.generic_fields[0].path)))


def create_generic_type_object(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from __future__ import annotations

from typing import Iterable, List

from demisto_sdk.commands.content_graph.objects.generic_field import GenericField
from demisto_sdk.commands.validate.validators.base_validator import (
BaseValidator,
FixResult,
ValidationResult,
)

REQUIRED_GROUP_VALUE = 4
ContentTypes = GenericField


class GenericFieldGroupValidator(BaseValidator[ContentTypes]):
error_code = "GF100"
rationale = "Required by the platform."
description = f"Checks if group field is set to {REQUIRED_GROUP_VALUE}."
error_message = f"The `group` key must be set to {REQUIRED_GROUP_VALUE}"
fix_message = f"set the `group` field to {REQUIRED_GROUP_VALUE}."
related_field = "group"
is_auto_fixable = True

def is_valid(self, content_items: Iterable[ContentTypes]) -> List[ValidationResult]:
return [
ValidationResult(
validator=self,
message=self.error_message,
content_object=content_item,
)
for content_item in content_items
if (content_item.group != REQUIRED_GROUP_VALUE)
]

def fix(self, content_item: ContentTypes) -> FixResult:
content_item.group = REQUIRED_GROUP_VALUE
return FixResult(
validator=self,
message=self.fix_message,
content_object=content_item,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from __future__ import annotations

from typing import Iterable, List

from demisto_sdk.commands.content_graph.objects.generic_field import GenericField
from demisto_sdk.commands.validate.validators.base_validator import (
BaseValidator,
ValidationResult,
)

GENERIC_FIELD_ID_PREFIX = "generic_"
ContentTypes = GenericField


class GenericFieldIdPrefixValidateValidator(BaseValidator[ContentTypes]):
error_code = "GF101"
rationale = "Required by the platform."
description = "Checks if the id starts with `generic_`."
error_message = (
"{generic_id} is not a valid id, it should start with {generic_id_prefix}."
)
related_field = "id"

def is_valid(self, content_items: Iterable[ContentTypes]) -> List[ValidationResult]:
return [
ValidationResult(
validator=self,
message=self.error_message.format(
generic_id=content_item.object_id,
generic_id_prefix=GENERIC_FIELD_ID_PREFIX,
),
content_object=content_item,
)
for content_item in content_items
if (not content_item.object_id.startswith(GENERIC_FIELD_ID_PREFIX))
]