diff --git a/TestSuite/case_field.py b/TestSuite/case_field.py new file mode 100644 index 00000000000..f108a75577d --- /dev/null +++ b/TestSuite/case_field.py @@ -0,0 +1,29 @@ +from pathlib import Path + +from TestSuite.json_based import JSONBased + + +class CaseField(JSONBased): + def __init__(self, name: str, case_field_dir_path: Path, json_content: dict = None): + self.case_field_file_path = case_field_dir_path / f"{name}.json" + super().__init__( + dir_path=case_field_dir_path, + name=name, + prefix="casefield", + json_content=json_content, + ) + + def create_default(self): + self.write_json( + { + "id": self.id, + "description": "test description", + "cliName": self.id.lower(), + "name": self.id, + "associatedToAll": False, + "type": "shortText", + "associatedTypes": [], + "threshold": 72, + "fromVersion": "8.7.0", + } + ) diff --git a/TestSuite/case_layout.py b/TestSuite/case_layout.py new file mode 100644 index 00000000000..a398ae5e3d0 --- /dev/null +++ b/TestSuite/case_layout.py @@ -0,0 +1,45 @@ +from pathlib import Path + +from TestSuite.json_based import JSONBased + + +class CaseLayout(JSONBased): + def __init__( + self, name: str, case_layout_dir_path: Path, json_content: dict = None + ): + self.case_layout_tmp_path = case_layout_dir_path / f"{name}.json" + super().__init__( + dir_path=case_layout_dir_path, + name=name, + prefix="layoutcontainer", + json_content=json_content, + ) + + def create_default(self): + self.write_json( + { + "detailsV2": { + "tabs": [ + {"id": "overview", "name": "Overview", "type": "overview"}, + { + "id": "alerts_and_insights", + "name": "Alerts \u0026 Insights", + "type": "alertInsights", + }, + {"id": "timeline", "name": "Timeline", "type": "timeline"}, + { + "id": "executions", + "name": "Executions", + "type": "executions", + }, + ] + }, + "group": "case", + "id": self.id, + "name": self.id, + "system": False, + "version": -1, + "fromVersion": "8.7.0", + "description": "", + } + ) diff --git a/TestSuite/case_layout_rule.py b/TestSuite/case_layout_rule.py new file mode 100644 index 00000000000..7e5c9967c00 --- /dev/null +++ b/TestSuite/case_layout_rule.py @@ -0,0 +1,41 @@ +from pathlib import Path + +from TestSuite.json_based import JSONBased + + +class CaseLayoutRule(JSONBased): + def __init__( + self, name: str, case_layout_rule_dir_path: Path, json_content: dict = None + ): + self.layout_rule_tmp_path = case_layout_rule_dir_path / f"{name}.json" + self.name = name + self.rule_id = name + + super().__init__(dir_path=case_layout_rule_dir_path, name=name, prefix="") + + if json_content: + self.write_json(json_content) + else: + self.create_default_case_layout_rule() + + def create_default_case_layout_rule(self): + self.write_json( + { + "rule_id": self.rule_id, + "rule_name": self.rule_id, + "layout_id": "test_layout", + "description": "", + "incidents_filter": { + "filter": { + "AND": [ + { + "SEARCH_FIELD": "STATUS", + "SEARCH_TYPE": "NEQ", + "SEARCH_VALUE": "STATUS_030_RESOLVED_THREAT_HANDLED", + } + ] + } + }, + "fromVersion": "8.7.0", + } + ) diff --git a/TestSuite/pack.py b/TestSuite/pack.py index a9f85848890..6f299e81845 100644 --- a/TestSuite/pack.py +++ b/TestSuite/pack.py @@ -3,6 +3,9 @@ from demisto_sdk.commands.common.constants import ( ASSETS_MODELING_RULES_DIR, + CASE_FIELDS_DIR, + CASE_LAYOUT_RULES_DIR, + CASE_LAYOUTS_DIR, CORRELATION_RULES_DIR, DEFAULT_IMAGE_BASE64, LAYOUT_RULES_DIR, @@ -13,6 +16,9 @@ XSIAM_DASHBOARDS_DIR, XSIAM_REPORTS_DIR, ) +from TestSuite.case_field import CaseField +from TestSuite.case_layout import CaseLayout +from TestSuite.case_layout_rule import CaseLayoutRule from TestSuite.classifier import Classifier from TestSuite.content_list import ContentList from TestSuite.correlation_rule import CorrelationRule @@ -105,6 +111,9 @@ def __init__(self, packs_dir: Path, name: str, repo): self.xdrc_templates: List[XDRCTemplate] = list() self.layout_rules: List[LayoutRule] = list() self.assets_modeling_rules: List[Rule] = list() + self.case_fields: List[CaseField] = list() + self.case_layouts: List[CaseLayout] = list() + self.case_layout_rules: List[CaseLayoutRule] = list() # Create base pack self._pack_path = packs_dir / self.name @@ -227,6 +236,15 @@ def __init__(self, packs_dir: Path, name: str, repo): self._xsiam_layout_rules_path = self._pack_path / LAYOUT_RULES_DIR self._xsiam_layout_rules_path.mkdir() + self._case_layout_rules_path = self._pack_path / CASE_LAYOUT_RULES_DIR + self._case_layout_rules_path.mkdir() + + self._case_layouts_path = self._pack_path / CASE_LAYOUTS_DIR + self._case_layouts_path.mkdir() + + self._case_fields_path = self._pack_path / CASE_FIELDS_DIR + self._case_fields_path.mkdir() + self.contributors: Optional[TextBased] = None self._assets_modeling_rules_path = self._pack_path / ASSETS_MODELING_RULES_DIR @@ -764,5 +782,29 @@ def create_layout_rule(self, name: str = None, content: dict = None) -> LayoutRu self.layout_rules.append(layout_rule) return layout_rule + def create_case_layout_rule( + self, name: str = None, content: dict = None + ) -> CaseLayoutRule: + if not name: + name = f"case_layout_rule{len(self.case_layout_rules)}" + case_layout_rule = CaseLayoutRule(name, self._case_layout_rules_path, content) + self.case_layout_rules.append(case_layout_rule) + return case_layout_rule + + def create_case_layout(self, name: str = None, content: dict = None) -> CaseLayout: + if not name: + name = f"case_layout{len(self.case_layouts)}" + case_layout = CaseLayout(name, self._case_layouts_path, content) + self.case_layouts.append(case_layout) + return case_layout + + def create_case_field(self, name: str = None, content: dict = None) -> CaseField: + if not name: + name = f"casefield{len(self.incident_fields)}" + case_field = CaseField(name, self._case_fields_path, content) + + self.case_fields.append(case_field) + return case_field + def set_data(self, **key_path_to_val): self.pack_metadata.set_data(**key_path_to_val) diff --git a/demisto_sdk/commands/common/constants.py b/demisto_sdk/commands/common/constants.py index 2ee801c28aa..ada79976de4 100644 --- a/demisto_sdk/commands/common/constants.py +++ b/demisto_sdk/commands/common/constants.py @@ -109,6 +109,9 @@ XDRC_TEMPLATE_DIR = "XDRCTemplates" LAYOUT_RULES_DIR = "LayoutRules" ASSETS_MODELING_RULES_DIR = "AssetsModelingRules" +CASE_LAYOUT_RULES_DIR = "CaseLayoutRules" +CASE_LAYOUTS_DIR = "CaseLayouts" +CASE_FIELDS_DIR = "CaseFields" # NAMES OF ENTITIES @@ -158,6 +161,9 @@ MARKETPLACE_KEY_PACK_METADATA = "marketplaces" EVENT_COLLECTOR = "EventCollector" ASSETS_MODELING_RULE = "assetsmodelingrule" +CASE_LAYOUT_RULE = "caselayoutrule" +CASE_FIELD = "casefield" +CASE_LAYOUT = "caselayout" # Marketplaces @@ -249,6 +255,9 @@ class FileType(StrEnum): ASSETS_MODELING_RULE_SCHEMA = "assetsmodelingruleschema" ASSETS_MODELING_RULE = "assetsmodelingrule" ASSETS_MODELING_RULE_XIF = "assetsmodelingrulexif" + CASE_LAYOUT_RULE = "caselayoutrule" + CASE_FIELD = "casefield" + CASE_LAYOUT = "caselayout" RN_HEADER_BY_FILE_TYPE = { @@ -287,6 +296,9 @@ class FileType(StrEnum): FileType.XDRC_TEMPLATE: "XDRC Templates", FileType.LAYOUT_RULE: "Layout Rules", FileType.ASSETS_MODELING_RULE: "Assets Modeling Rules", + FileType.CASE_LAYOUT_RULE: "Case Layout Rules", + FileType.CASE_FIELD: "Case Fields", + FileType.CASE_LAYOUT: "Case Layouts", } FILE_TYPE_BY_RN_HEADER = { @@ -329,6 +341,9 @@ class FileType(StrEnum): FileType.OLD_CLASSIFIER.value: CLASSIFIERS_DIR, FileType.LAYOUT_RULE.value: LAYOUT_RULES_DIR, FileType.ASSETS_MODELING_RULE.value: ASSETS_MODELING_RULES_DIR, + FileType.CASE_FIELD.value: CASE_FIELDS_DIR, + FileType.CASE_LAYOUT.value: CASE_LAYOUTS_DIR, + FileType.CASE_LAYOUT_RULE.value: CASE_LAYOUT_RULES_DIR, } SIEM_ONLY_ENTITIES = [ @@ -341,6 +356,9 @@ class FileType(StrEnum): FileType.XDRC_TEMPLATE.value, FileType.LAYOUT_RULE.value, FileType.ASSETS_MODELING_RULE, + FileType.CASE_LAYOUT_RULE.value, + FileType.CASE_FIELD.value, + FileType.CASE_LAYOUT.value, ] CONTENT_FILE_ENDINGS = ["py", "yml", "png", "json", "md"] @@ -387,6 +405,9 @@ class FileType(StrEnum): XSIAM_REPORTS_DIR, TRIGGER_DIR, ASSETS_MODELING_RULES_DIR, + CASE_LAYOUT_RULES_DIR, + CASE_FIELDS_DIR, + CASE_LAYOUTS_DIR, ] CONTENT_ENTITY_UPLOAD_ORDER = [ @@ -905,6 +926,8 @@ class FileType(StrEnum): XDRC_TEMPLATE_PREFIX = "xdrctemplate" LAYOUT_RULE_PREFIX = "layoutrule" ASSETS_MODELING_RULE_ID_SUFFIX = "AssetsModelingRule" +# TODO + # Pack Unique Files PACKS_WHITELIST_FILE_NAME = ".secrets-ignore" @@ -1569,6 +1592,9 @@ class PB_Status: FileType.LAYOUT_RULE: "6.10.0", FileType.XSIAM_DASHBOARD: "6.10.0", FileType.ASSETS_MODELING_RULE: "6.2.1", + FileType.CASE_LAYOUT_RULE: "8.7.0", + FileType.CASE_FIELD: "8.7.0", + FileType.CASE_LAYOUT: "8.7.0", } DEFAULT_PYTHON_VERSION = "3.10" @@ -1858,6 +1884,9 @@ class ContentItems(Enum): XDRC_TEMPLATE = "xdrctemplate" LAYOUT_RULES = "layoutrule" ASSETS_MODELING_RULES = "assetsmodelingrule" + CASE_LAYOUT_RULES = "caselayoutrule" + CASE_FIELDS = "casefield" + CASE_LAYOUTS = "caselayout" CONTENT_ITEMS_DISPLAY_FOLDERS = { @@ -1886,6 +1915,9 @@ class ContentItems(Enum): XDRC_TEMPLATE_DIR, LAYOUT_RULES_DIR, ASSETS_MODELING_RULES_DIR, + CASE_LAYOUT_RULES_DIR, + CASE_FIELDS_DIR, + CASE_LAYOUTS_DIR, } diff --git a/demisto_sdk/commands/common/schemas/casefield.yml b/demisto_sdk/commands/common/schemas/casefield.yml new file mode 100644 index 00000000000..7ef72f7a2cd --- /dev/null +++ b/demisto_sdk/commands/common/schemas/casefield.yml @@ -0,0 +1,130 @@ +type: map +mapping: + fromVersion: + type: str + toVersion: + type: str + id: + type: str + required: true + version: + type: number + modified: + type: str + name: + type: str + required: true + name_x2: + type: str + prettyName: + type: str + required: false + ownerOnly: + type: bool + placeholder: + type: str + description: + type: str + fieldCalcScript: + type: str + cliName: + type: str + required: true + cliName_x2: + type: str + type: + type: str + required: true + closeForm: + type: bool + editForm: + type: bool + required: + type: bool + script: + type: str + neverSetAsRequired: + type: bool + isReadOnly: + type: bool + selectValues: + type: any + validationRegex: + type: str + useAsKpi: + type: bool + locked: + type: bool + system: + type: bool + runScriptAfterIncUpdate: + type: bool + group: + type: number + hidden: + type: bool + columns: + type: any + defaultRows: + type: any + threshold: + type: number + sla: + type: number + caseInsensitive: + type: bool + breachScript: + type: str + associatedTypes: + type: any + systemAssociatedTypes: + type: any + associatedToAll: + type: bool + unmapped: + type: bool + content: + type: bool + unsearchable: + type: bool + extractIndicatorTypesIDs: + type: any + isExtractingSpecificIndicatorTypes: + type: bool + itemVersion: + type: str + propagationLabels: + type: any + toServerVersion: + type: str + openEnded: + type: bool + template: + type: str + marketplaces: + type: seq + sequence: + - type: str + enum: ['marketplacev2'] + Aliases: + type: seq + sequence: + - include: aliases_schema + x2_fields: + type: str + aliasTo: + type: str + +schema;aliases_schema: + type: map + mapping: + cliName: + type: str + required: true + name: + type: str + required: true + type: + type: str + required: true + enum: ['shortText', 'longText', 'boolean', 'singleSelect', 'multiSelect', 'date', 'user', 'role', 'number', 'attachments', 'tagsSelect', 'internal', 'url', 'markdown', 'grid', 'timer', 'html'] diff --git a/demisto_sdk/commands/common/schemas/caselayout.yml b/demisto_sdk/commands/common/schemas/caselayout.yml new file mode 100644 index 00000000000..84879d3cf2b --- /dev/null +++ b/demisto_sdk/commands/common/schemas/caselayout.yml @@ -0,0 +1,102 @@ +type: map +mapping: + typeId: + type: str + required: true + TypeName: + type: str + version: + type: number + kind: + type: str + required: true + fromVersion: + type: str + toVersion: + type: str + required: true + system: + type: bool + description: + type: str + id: + type: str + layout: + type: map + mapping: + id: + type: str + required: true + system: + type: bool + TypeName: + type: str + version: + type: number + kind: + type: str + typeId: + type: str + required: true + modified: + type: str + name: + type: str + tabs: + type: seq + sequence: + - include: tabs_schema + sections: + type: seq + sequence: + - include: section_schema + definitionId: + type: str +schema;tabs_schema: + type: any + +schema;section_schema: + type: map + mapping: + id: + type: str + version: + type: number + modified: + type: str + name: + type: str + type: + type: str + isVisible: + type: bool + readOnly: + type: bool + description: + type: str + query: + type: any + queryType: + type: str + sortValues: + type: str + fields: + type: seq + sequence: + - include: field_schema + +schema;field_schema: + type: map + mapping: + id: + type: str + version: + type: number + modified: + type: str + fieldId: + type: str + isVisible: + type: bool + sortValues: + type: str diff --git a/demisto_sdk/commands/common/schemas/caselayoutrule.yml b/demisto_sdk/commands/common/schemas/caselayoutrule.yml new file mode 100644 index 00000000000..c61e55c619d --- /dev/null +++ b/demisto_sdk/commands/common/schemas/caselayoutrule.yml @@ -0,0 +1,72 @@ +type: map +mapping: + rule_id: + type: str + required: true + rule_name: + type: str + required: true + layout_id: + type: str + required: true + fromVersion: + type: str + required: true + description: + type: str + required: false + incidents_filter: + type: map + mapping: + filter: + type: map + mapping: + OR: + type: seq + required: false + sequence: + - include: filter_schema + - include: or_schema + - include: and_schema + AND: + type: seq + required: false + sequence: + - include: filter_schema + - include: or_schema + - include: and_schema + +schema;filter_schema: + type: map + mapping: + SEARCH_FIELD: + type: str + required: true + SEARCH_TYPE: + type: str + required: true + SEARCH_VALUE: + type: str + required: true + +schema;and_schema: + type: map + mapping: + AND: + type: seq + required: false + sequence: + - include: filter_schema + - include: or_schema + - include: and_schema + +schema;or_schema: + type: map + mapping: + OR: + type: seq + required: false + sequence: + - include: filter_schema + - include: or_schema + - include: and_schema diff --git a/demisto_sdk/commands/common/schemas/layoutscontainer.yml b/demisto_sdk/commands/common/schemas/layoutscontainer.yml index c34dc4045e1..2f3b13c35ce 100644 --- a/demisto_sdk/commands/common/schemas/layoutscontainer.yml +++ b/demisto_sdk/commands/common/schemas/layoutscontainer.yml @@ -6,7 +6,7 @@ mapping: group: type: str required: true - enum: ['incident', 'indicator'] + enum: ['incident', 'indicator', 'case'] definitionId: type: str version: diff --git a/demisto_sdk/commands/common/tools.py b/demisto_sdk/commands/common/tools.py index 980bf3c9832..85513358683 100644 --- a/demisto_sdk/commands/common/tools.py +++ b/demisto_sdk/commands/common/tools.py @@ -1807,6 +1807,9 @@ def find_type( FileType | None: Enum representation of the content file type, None otherwise. """ from demisto_sdk.commands.content_graph.objects import ( + CaseField, + CaseLayout, + CaseLayoutRule, Classifier, CorrelationRule, Dashboard, @@ -1981,6 +1984,15 @@ def find_type( if LayoutRule.match(_dict, Path(path)): return FileType.LAYOUT_RULE + if CaseField.match(_dict, Path(path)): + return FileType.CASE_FIELD + + if CaseLayout.match(_dict, Path(path)): + return FileType.CASE_LAYOUT + + if CaseLayoutRule.match(_dict, Path(path)): + return FileType.CASE_LAYOUT_RULE + if ListObject.match(_dict, Path(path)): return FileType.LISTS diff --git a/demisto_sdk/commands/content_graph/common.py b/demisto_sdk/commands/content_graph/common.py index 44fddd156c4..1fb8861abff 100644 --- a/demisto_sdk/commands/content_graph/common.py +++ b/demisto_sdk/commands/content_graph/common.py @@ -99,6 +99,9 @@ class ContentType(StrEnum): XDRC_TEMPLATE = "XDRCTemplate" LAYOUT_RULE = "LayoutRule" ASSETS_MODELING_RULE = "AssetsModelingRule" + CASE_LAYOUT_RULE = "CaseLayoutRule" + CASE_FIELD = "CaseField" + CASE_LAYOUT = "CaseLayout" @property def labels(self) -> List[str]: @@ -126,7 +129,9 @@ def server_name(self) -> str: return "reputation" elif self == ContentType.INDICATOR_FIELD: return "incidentfield-indicatorfield" - elif self == ContentType.LAYOUT: + elif self == ContentType.CASE_FIELD: + return "casefield" + elif self == ContentType.LAYOUT or self == ContentType.CASE_LAYOUT: return "layoutscontainer" elif self == ContentType.PREPROCESS_RULE: return "preprocessrule" @@ -145,7 +150,7 @@ def metadata_name(self) -> str: return "automation" elif self == ContentType.INDICATOR_TYPE: return "reputation" - elif self == ContentType.LAYOUT: + elif self == ContentType.LAYOUT or self == ContentType.CASE_LAYOUT: return "layoutscontainer" elif self == ContentType.TEST_PLAYBOOK: return ContentType.PLAYBOOK.server_name @@ -161,7 +166,7 @@ def metadata_display_name(self) -> str: return "Reputation" elif self == ContentType.MAPPER: return "Classifier" - elif self == ContentType.LAYOUT: + elif self == ContentType.LAYOUT or self == ContentType.CASE_LAYOUT: return "Layouts Container" else: return re.sub(r"([a-z](?=[A-Z])|[A-Z](?=[A-Z][a-z]))", r"\1 ", self.value) diff --git a/demisto_sdk/commands/content_graph/objects/__init__.py b/demisto_sdk/commands/content_graph/objects/__init__.py index a92c25a8e05..b0522aa935e 100644 --- a/demisto_sdk/commands/content_graph/objects/__init__.py +++ b/demisto_sdk/commands/content_graph/objects/__init__.py @@ -36,12 +36,18 @@ "LayoutRule", "PreProcessRule", "AssetsModelingRule", + "CaseField", + "CaseLayout", + "CaseLayoutRule", ] from demisto_sdk.commands.content_graph.objects.assets_modeling_rule import ( AssetsModelingRule, ) from demisto_sdk.commands.content_graph.objects.base_playbook import BasePlaybook from demisto_sdk.commands.content_graph.objects.base_script import BaseScript +from demisto_sdk.commands.content_graph.objects.case_field import CaseField +from demisto_sdk.commands.content_graph.objects.case_layout import CaseLayout +from demisto_sdk.commands.content_graph.objects.case_layout_rule import CaseLayoutRule from demisto_sdk.commands.content_graph.objects.classifier import Classifier from demisto_sdk.commands.content_graph.objects.correlation_rule import CorrelationRule from demisto_sdk.commands.content_graph.objects.dashboard import Dashboard diff --git a/demisto_sdk/commands/content_graph/objects/case_field.py b/demisto_sdk/commands/content_graph/objects/case_field.py new file mode 100644 index 00000000000..b358d9709b2 --- /dev/null +++ b/demisto_sdk/commands/content_graph/objects/case_field.py @@ -0,0 +1,35 @@ +from pathlib import Path +from typing import Optional + +from pydantic import Field + +from demisto_sdk.commands.common.constants import MarketplaceVersions +from demisto_sdk.commands.common.handlers import JSON_Handler +from demisto_sdk.commands.content_graph.common import ContentType +from demisto_sdk.commands.content_graph.objects.indicator_incident_field import ( + IndicatorIncidentField, +) + +json = JSON_Handler() + + +class CaseField(IndicatorIncidentField, content_type=ContentType.CASE_FIELD): # type: ignore[call-arg] + associated_to_all: bool = Field(False, alias="associatedToAll") + + def summary( + self, + marketplace: Optional[MarketplaceVersions] = None, + incident_to_alert: bool = False, + ) -> dict: + summary = super().summary(marketplace, incident_to_alert) + summary["id"] = f"case_{self.object_id}" + return summary + + @staticmethod + def match(_dict: dict, path: Path) -> bool: + if "id" in _dict: + if isinstance(_dict["id"], str): + _id = _dict["id"].lower() + if _id.startswith("case"): + return True + return False diff --git a/demisto_sdk/commands/content_graph/objects/case_layout.py b/demisto_sdk/commands/content_graph/objects/case_layout.py new file mode 100644 index 00000000000..decb8f3c7d8 --- /dev/null +++ b/demisto_sdk/commands/content_graph/objects/case_layout.py @@ -0,0 +1,14 @@ +from pathlib import Path + +from demisto_sdk.commands.content_graph.common import ContentType +from demisto_sdk.commands.content_graph.objects.layout import Layout + + +class CaseLayout(Layout, content_type=ContentType.CASE_LAYOUT): # type: ignore[call-arg] + @staticmethod + def match(_dict: dict, path: Path) -> bool: + if "group" in _dict and Path(path).suffix == ".json": + if "cliName" not in _dict: + if _dict["group"] == "case": + return True + return False diff --git a/demisto_sdk/commands/content_graph/objects/case_layout_rule.py b/demisto_sdk/commands/content_graph/objects/case_layout_rule.py new file mode 100644 index 00000000000..66f8b600d1b --- /dev/null +++ b/demisto_sdk/commands/content_graph/objects/case_layout_rule.py @@ -0,0 +1,30 @@ +from pathlib import Path +from typing import Set + +from demisto_sdk.commands.content_graph.common import ContentType +from demisto_sdk.commands.content_graph.objects.content_item_xsiam import ( + ContentItemXSIAM, +) + + +class CaseLayoutRule(ContentItemXSIAM, content_type=ContentType.CASE_LAYOUT_RULE): # type: ignore[call-arg] + layout_id: str + + def metadata_fields(self) -> Set[str]: + return ( + super() + .metadata_fields() + .union( + { + "rule_name", + "layout_id", + } + ) + ) + + @staticmethod + def match(_dict: dict, path: Path) -> bool: + if "rule_id" in _dict and path.suffix == ".json": + if "incidents_filter" in _dict: + return True + return False diff --git a/demisto_sdk/commands/content_graph/objects/generic_field.py b/demisto_sdk/commands/content_graph/objects/generic_field.py index a97543f0a04..68aef1d8a89 100644 --- a/demisto_sdk/commands/content_graph/objects/generic_field.py +++ b/demisto_sdk/commands/content_graph/objects/generic_field.py @@ -34,7 +34,8 @@ def match(_dict: dict, path: Path) -> bool: if ( "definitionId" in _dict and _dict["definitionId"] - and _dict["definitionId"].lower() not in ["incident", "indicator"] + and _dict["definitionId"].lower() + not in ["incident", "indicator", "case"] and path.suffix == ".json" ): # we don't want to match the generic type diff --git a/demisto_sdk/commands/content_graph/objects/generic_module.py b/demisto_sdk/commands/content_graph/objects/generic_module.py index 1a890607e08..83e54ab5564 100644 --- a/demisto_sdk/commands/content_graph/objects/generic_module.py +++ b/demisto_sdk/commands/content_graph/objects/generic_module.py @@ -1,5 +1,5 @@ from pathlib import Path -from typing import List, Optional +from typing import List, Optional, Set from pydantic import Field @@ -16,3 +16,14 @@ def match(_dict: dict, path: Path) -> bool: if "definitionIds" in _dict and "views" in _dict and path.suffix == ".json": return True return False + + def metadata_fields(self) -> Set[str]: + return ( + super() + .metadata_fields() + .union( + { + "definitionIds", + } + ) + ) diff --git a/demisto_sdk/commands/content_graph/objects/layout.py b/demisto_sdk/commands/content_graph/objects/layout.py index 8be4b832b81..3dda1ee4866 100644 --- a/demisto_sdk/commands/content_graph/objects/layout.py +++ b/demisto_sdk/commands/content_graph/objects/layout.py @@ -1,5 +1,5 @@ from pathlib import Path -from typing import Callable, List, Optional, Union +from typing import Callable, List, Optional, Set, Union import demisto_client from pydantic import Field @@ -63,6 +63,17 @@ def match(_dict: dict, path: Path) -> bool: return True return False + def metadata_fields(self) -> Set[str]: + return ( + super() + .metadata_fields() + .union( + { + "group", + } + ) + ) + def replace_layout_incident_alert(data: dict) -> dict: """ diff --git a/demisto_sdk/commands/content_graph/objects/layout_rule.py b/demisto_sdk/commands/content_graph/objects/layout_rule.py index 1ed79713c07..a9a37ebc010 100644 --- a/demisto_sdk/commands/content_graph/objects/layout_rule.py +++ b/demisto_sdk/commands/content_graph/objects/layout_rule.py @@ -25,5 +25,6 @@ def metadata_fields(self) -> Set[str]: @staticmethod def match(_dict: dict, path: Path) -> bool: if "rule_id" in _dict and path.suffix == ".json": - return True + if "alerts_filter" in _dict: + return True return False diff --git a/demisto_sdk/commands/content_graph/objects/pack.py b/demisto_sdk/commands/content_graph/objects/pack.py index 68da7bac182..9d19717df5f 100644 --- a/demisto_sdk/commands/content_graph/objects/pack.py +++ b/demisto_sdk/commands/content_graph/objects/pack.py @@ -353,6 +353,10 @@ def dump( ): folder = ContentType.TEST_PLAYBOOK.as_folder + # The content structure is different from the server + if folder == "CaseLayouts": + folder = "Layouts" + content_item.dump( dir=path / folder, marketplace=marketplace, diff --git a/demisto_sdk/commands/content_graph/objects/pack_content_items.py b/demisto_sdk/commands/content_graph/objects/pack_content_items.py index 1449a4bd631..fe7aaf32871 100644 --- a/demisto_sdk/commands/content_graph/objects/pack_content_items.py +++ b/demisto_sdk/commands/content_graph/objects/pack_content_items.py @@ -6,6 +6,9 @@ from demisto_sdk.commands.content_graph.objects.assets_modeling_rule import ( AssetsModelingRule, ) +from demisto_sdk.commands.content_graph.objects.case_field import CaseField +from demisto_sdk.commands.content_graph.objects.case_layout import CaseLayout +from demisto_sdk.commands.content_graph.objects.case_layout_rule import CaseLayoutRule from demisto_sdk.commands.content_graph.objects.classifier import Classifier from demisto_sdk.commands.content_graph.objects.content_item import ContentItem from demisto_sdk.commands.content_graph.objects.correlation_rule import CorrelationRule @@ -44,6 +47,11 @@ class PackContentItems(BaseModel): # The alias is for marshalling purposes + case_field: List[CaseField] = Field([], alias=ContentType.CASE_FIELD.value) + case_layout: List[CaseLayout] = Field([], alias=ContentType.CASE_LAYOUT.value) + case_layout_rule: List[CaseLayoutRule] = Field( + [], alias=ContentType.CASE_LAYOUT_RULE.value + ) classifier: List[Classifier] = Field([], alias=ContentType.CLASSIFIER.value) correlation_rule: List[CorrelationRule] = Field( [], alias=ContentType.CORRELATION_RULE.value diff --git a/demisto_sdk/commands/content_graph/parsers/__init__.py b/demisto_sdk/commands/content_graph/parsers/__init__.py index 800df01be78..34ee1fbbb4b 100644 --- a/demisto_sdk/commands/content_graph/parsers/__init__.py +++ b/demisto_sdk/commands/content_graph/parsers/__init__.py @@ -34,6 +34,9 @@ "LayoutRuleParser", "PreProcessRuleParser", "AssetsModelingRuleParser", + "CaseLayoutRuleParser", + "CaseFieldParser", + "CaseLayoutParser", ] from demisto_sdk.commands.content_graph.parsers.assets_modeling_rule import ( @@ -41,6 +44,11 @@ ) from demisto_sdk.commands.content_graph.parsers.base_playbook import BasePlaybookParser from demisto_sdk.commands.content_graph.parsers.base_script import BaseScriptParser +from demisto_sdk.commands.content_graph.parsers.case_field import CaseFieldParser +from demisto_sdk.commands.content_graph.parsers.case_layout import CaseLayoutParser +from demisto_sdk.commands.content_graph.parsers.case_layout_rule import ( + CaseLayoutRuleParser, +) from demisto_sdk.commands.content_graph.parsers.classifier import ClassifierParser from demisto_sdk.commands.content_graph.parsers.correlation_rule import ( CorrelationRuleParser, diff --git a/demisto_sdk/commands/content_graph/parsers/case_field.py b/demisto_sdk/commands/content_graph/parsers/case_field.py new file mode 100644 index 00000000000..c255a999296 --- /dev/null +++ b/demisto_sdk/commands/content_graph/parsers/case_field.py @@ -0,0 +1,51 @@ +from functools import cached_property +from pathlib import Path +from typing import List, Optional, Set + +from demisto_sdk.commands.common.constants import MarketplaceVersions +from demisto_sdk.commands.common.tools import get_value +from demisto_sdk.commands.content_graph.common import ContentType +from demisto_sdk.commands.content_graph.parsers.json_content_item import ( + JSONContentItemParser, +) + + +class CaseFieldParser(JSONContentItemParser, content_type=ContentType.CASE_FIELD): + def __init__( + self, + path: Path, + pack_marketplaces: List[MarketplaceVersions], + git_sha: Optional[str] = None, + ) -> None: + super().__init__(path, pack_marketplaces, git_sha=git_sha) + self.field_type = self.json_data.get("type") + self.associated_to_all = self.json_data.get("associatedToAll") + self.content = self.json_data.get("content") + self.system = self.json_data.get("system") + self.group = self.json_data.get("group") + + self.connect_to_dependencies() + + @cached_property + def field_mapping(self): + super().field_mapping.update({"object_id": "id", "cli_name": "cliName"}) + return super().field_mapping + + @property + def cli_name(self) -> Optional[str]: + return get_value(self.json_data, self.field_mapping.get("cli_name", "")) + + @property + def object_id(self) -> Optional[str]: + id = get_value(self.json_data, self.field_mapping.get("object_id", "")) + return (id.lower().replace("_", "").replace("-", ""))[len("case") :] + + @property + def supported_marketplaces(self) -> Set[MarketplaceVersions]: + return { + MarketplaceVersions.MarketplaceV2, + } + + def connect_to_dependencies(self) -> None: + """TBD in case we add case types""" + pass diff --git a/demisto_sdk/commands/content_graph/parsers/case_layout.py b/demisto_sdk/commands/content_graph/parsers/case_layout.py new file mode 100644 index 00000000000..82cafddc1e2 --- /dev/null +++ b/demisto_sdk/commands/content_graph/parsers/case_layout.py @@ -0,0 +1,13 @@ +from typing import Set + +from demisto_sdk.commands.common.constants import MarketplaceVersions +from demisto_sdk.commands.content_graph.common import ContentType +from demisto_sdk.commands.content_graph.parsers.layout import ( + LayoutParser, +) + + +class CaseLayoutParser(LayoutParser, content_type=ContentType.CASE_LAYOUT): + @property + def supported_marketplaces(self) -> Set[MarketplaceVersions]: + return {MarketplaceVersions.MarketplaceV2} diff --git a/demisto_sdk/commands/content_graph/parsers/case_layout_rule.py b/demisto_sdk/commands/content_graph/parsers/case_layout_rule.py new file mode 100644 index 00000000000..4cea26a1a4b --- /dev/null +++ b/demisto_sdk/commands/content_graph/parsers/case_layout_rule.py @@ -0,0 +1,9 @@ +from demisto_sdk.commands.content_graph.common import ContentType +from demisto_sdk.commands.content_graph.parsers.layout_rule import LayoutRuleParser + + +class CaseLayoutRuleParser(LayoutRuleParser, content_type=ContentType.CASE_LAYOUT_RULE): + def connect_to_dependencies(self) -> None: + """Collects the playbook used in the trigger as a mandatory dependency.""" + if layout := self.json_data.get("layout_id"): + self.add_dependency_by_id(layout, ContentType.CASE_LAYOUT) diff --git a/demisto_sdk/commands/content_graph/parsers/layout.py b/demisto_sdk/commands/content_graph/parsers/layout.py index b1ffc2d8fbb..2d544ae6640 100644 --- a/demisto_sdk/commands/content_graph/parsers/layout.py +++ b/demisto_sdk/commands/content_graph/parsers/layout.py @@ -57,9 +57,12 @@ def connect_to_dependencies(self) -> None: dependency_field_type = ContentType.INCIDENT_FIELD elif self.group == "indicator": dependency_field_type = ContentType.INDICATOR_FIELD + elif self.group == "case": + dependency_field_type = ContentType.CASE_FIELD + else: raise ValueError( - f'{self.node_id}: Unknown group "{self.group}" - Expected "incident" or "indicator".' + f'{self.node_id}: Unknown group "{self.group}" - Expected "incident", "indicator" or "case".' ) for field in self.get_field_ids_recursively(): @@ -85,7 +88,9 @@ def get_values(current_object): for key, value in current_object.items(): if key == "fieldId" and isinstance(value, str): values.add( - value.replace("incident_", "").replace("indicator_", "") + value.replace("incident_", "") + .replace("indicator_", "") + .replace("case_", "") ) else: get_values(value) diff --git a/demisto_sdk/commands/content_graph/parsers/pack.py b/demisto_sdk/commands/content_graph/parsers/pack.py index 19a77771a3f..34bd062da44 100644 --- a/demisto_sdk/commands/content_graph/parsers/pack.py +++ b/demisto_sdk/commands/content_graph/parsers/pack.py @@ -44,6 +44,11 @@ class PackContentItems: """A class that holds all pack's content items in lists by their types.""" def __init__(self) -> None: + self.case_field = ContentItemsList(content_type=ContentType.CASE_FIELD) + self.case_layout = ContentItemsList(content_type=ContentType.CASE_LAYOUT) + self.case_layout_rule = ContentItemsList( + content_type=ContentType.CASE_LAYOUT_RULE + ) self.classifier = ContentItemsList(content_type=ContentType.CLASSIFIER) self.correlation_rule = ContentItemsList( content_type=ContentType.CORRELATION_RULE diff --git a/demisto_sdk/commands/content_graph/tests/parsers_and_models_test.py b/demisto_sdk/commands/content_graph/tests/parsers_and_models_test.py index 32ecce1070b..768195e805d 100644 --- a/demisto_sdk/commands/content_graph/tests/parsers_and_models_test.py +++ b/demisto_sdk/commands/content_graph/tests/parsers_and_models_test.py @@ -1402,6 +1402,110 @@ def test_layout_rule_parser(self, pack: Pack): expected_fromversion="6.10.0", ) + def test_case_layout_rule_parser(self, pack: Pack): + """ + Given: + - A pack with a case layout rule. + When: + - Creating the content item's parser and model. + Then: + - Verify all relationships of the content item are collected. + - Verify the generic content item properties are parsed correctly. + - Verify the specific properties of the content item are parsed correctly. + """ + from demisto_sdk.commands.content_graph.objects.case_layout_rule import ( + CaseLayoutRule, + ) + from demisto_sdk.commands.content_graph.parsers.case_layout_rule import ( + CaseLayoutRuleParser, + ) + + rule = pack.create_case_layout_rule("case_rule_test") + rule_path = Path(rule.path) + parser = CaseLayoutRuleParser(rule_path, list(MarketplaceVersions)) + RelationshipsVerifier.run( + parser.relationships, + dependency_ids={ + "test_layout": ContentType.CASE_LAYOUT, + }, + ) + model = CaseLayoutRule.from_orm(parser) + ContentItemModelVerifier.run( + model, + expected_id="case_rule_test", + expected_name="case_rule_test", + expected_path=rule_path, + expected_content_type=ContentType.CASE_LAYOUT_RULE, + expected_fromversion="8.7.0", + ) + + def test_case_layout_parser(self, pack: Pack): + """ + Given: + - A pack with a case layout. + When: + - Creating the content item's parser and model. + Then: + - Verify all relationships of the content item are collected. + - Verify the generic content item properties are parsed correctly. + - Verify the specific properties of the content item are parsed correctly. + """ + from demisto_sdk.commands.content_graph.objects import CaseLayout + from demisto_sdk.commands.content_graph.parsers.case_layout import ( + CaseLayoutParser, + ) + + case_layout = pack.create_case_layout("TestCaseLayout") + case_layout_path = Path(case_layout.path) + parser = CaseLayoutParser(case_layout_path, list(MarketplaceVersions)) + model = CaseLayout.from_orm(parser) + + ContentItemModelVerifier.run( + model, + expected_id="TestCaseLayout", + expected_name="TestCaseLayout", + expected_path=case_layout_path, + expected_content_type=ContentType.CASE_LAYOUT, + expected_fromversion="8.7.0", + ) + assert model.group == "case" + + def test_case_field_parser(self, pack: Pack): + """ + Given: + - A pack with a case field. + When: + - Creating the content item's parser and model. + Then: + - Verify all relationships of the content item are collected. + - Verify the generic content item properties are parsed correctly. + - Verify the specific properties of the content item are parsed correctly. + """ + from demisto_sdk.commands.content_graph.objects.case_field import ( + CaseField, + ) + from demisto_sdk.commands.content_graph.parsers.case_field import ( + CaseFieldParser, + ) + + case_field = pack.create_case_field( + "TestCaseField", load_json("case_field.json") + ) + case_field_path = Path(case_field.path) + parser = CaseFieldParser(case_field_path, list(MarketplaceVersions)) + model = CaseField.from_orm(parser) + ContentItemModelVerifier.run( + model, + expected_id="testcasefield", + expected_name="Test case Field", + expected_path=case_field_path, + expected_content_type=ContentType.CASE_FIELD, + expected_fromversion="8.7.0", + ) + assert model.cli_name == "testcasefield" + assert model.field_type == "html" + assert not model.associated_to_all + def test_widget_parser(self, pack: Pack): """ Given: @@ -2548,7 +2652,7 @@ def test_layout_match_fails_on_other_content_types(self, mocker, git_repo): - Ensure ValueError is raised. """ from demisto_sdk.commands.content_graph.common import ContentType - from demisto_sdk.commands.content_graph.objects import Layout + from demisto_sdk.commands.content_graph.objects import CaseLayout, Layout layout_json_str = "layout.json" layout_json_path = git_repo.path / Path(layout_json_str) diff --git a/demisto_sdk/commands/content_graph/tests/test_data/case_field.json b/demisto_sdk/commands/content_graph/tests/test_data/case_field.json new file mode 100644 index 00000000000..e951e09b490 --- /dev/null +++ b/demisto_sdk/commands/content_graph/tests/test_data/case_field.json @@ -0,0 +1,28 @@ +{ + "id": "case_testcasefield", + "version": -1, + "modified": "2024-05-15T11:37:39.117104698Z", + "name": "Test case Field", + "ownerOnly": false, + "cliName": "testcasefield", + "type": "html", + "closeForm": false, + "editForm": true, + "required": false, + "neverSetAsRequired": false, + "isReadOnly": false, + "useAsKpi": false, + "locked": false, + "system": false, + "content": false, + "group": 5, + "hidden": false, + "openEnded": false, + "associatedToAll": false, + "unmapped": false, + "unsearchable": true, + "caseInsensitive": true, + "sla": 0, + "threshold": 72, + "fromVersion": "8.7.0" +} diff --git a/demisto_sdk/commands/format/format_module.py b/demisto_sdk/commands/format/format_module.py index 8a8b3a42379..d1a339552be 100644 --- a/demisto_sdk/commands/format/format_module.py +++ b/demisto_sdk/commands/format/format_module.py @@ -99,6 +99,9 @@ "layoutrule": BaseUpdateJSON, "assetsmodelingrule": BaseUpdateYML, "assetsmodelingruleschema": BaseUpdateJSON, + "casefield": BaseUpdateJSON, + "caselayout": LayoutBaseFormat, + "caselayoutrule": BaseUpdateJSON, } UNFORMATTED_FILES = [ diff --git a/demisto_sdk/commands/format/update_layout.py b/demisto_sdk/commands/format/update_layout.py index bd0b531266a..f947ba85747 100644 --- a/demisto_sdk/commands/format/update_layout.py +++ b/demisto_sdk/commands/format/update_layout.py @@ -189,7 +189,11 @@ def set_layout_key(self): } def set_group_field(self): - if self.data["group"] != "incident" and self.data["group"] != "indicator": + if ( + self.data["group"] != "incident" + and self.data["group"] != "indicator" + and self.data["group"] != "case" + ): logger.info( "[red]No group is specified for this layout, would you like me to update for you? [Y/n][/red]" ) @@ -200,13 +204,15 @@ def set_group_field(self): return logger.info( - "[yellow]Please specify the desired group: incident or indicator[/yellow]" + "[yellow]Please specify the desired group: incident, indicator or case[/yellow]" ) user_desired_group = input() if re.match(r"(^incident$)", user_desired_group, re.IGNORECASE): self.data["group"] = "incident" elif re.match(r"(^indicator$)", user_desired_group, re.IGNORECASE): self.data["group"] = "indicator" + elif re.match(r"(^case$)", user_desired_group, re.IGNORECASE): + self.data["group"] = "case" else: logger.info("[red]Group is not valid[/red]") diff --git a/demisto_sdk/commands/validate/old_validate_manager.py b/demisto_sdk/commands/validate/old_validate_manager.py index b6f665793d6..2994c062691 100644 --- a/demisto_sdk/commands/validate/old_validate_manager.py +++ b/demisto_sdk/commands/validate/old_validate_manager.py @@ -316,6 +316,9 @@ def __init__( FileType.PEM, FileType.METADATA, FileType.VULTURE_WHITELIST, + FileType.CASE_LAYOUT_RULE, + FileType.CASE_LAYOUT, + FileType.CASE_FIELD, ) self.is_external_repo = is_external_repo diff --git a/demisto_sdk/commands/validate/validators/BA_validators/BA106_is_from_version_sufficient_all_items.py b/demisto_sdk/commands/validate/validators/BA_validators/BA106_is_from_version_sufficient_all_items.py index a35d80307ea..c3bd05d0996 100644 --- a/demisto_sdk/commands/validate/validators/BA_validators/BA106_is_from_version_sufficient_all_items.py +++ b/demisto_sdk/commands/validate/validators/BA_validators/BA106_is_from_version_sufficient_all_items.py @@ -8,6 +8,9 @@ from demisto_sdk.commands.content_graph.objects.assets_modeling_rule import ( AssetsModelingRule, ) +from demisto_sdk.commands.content_graph.objects.case_field import CaseField +from demisto_sdk.commands.content_graph.objects.case_layout import CaseLayout +from demisto_sdk.commands.content_graph.objects.case_layout_rule import CaseLayoutRule from demisto_sdk.commands.content_graph.objects.classifier import Classifier from demisto_sdk.commands.content_graph.objects.content_item import ContentItem from demisto_sdk.commands.content_graph.objects.correlation_rule import CorrelationRule @@ -68,6 +71,9 @@ IncidentField, AssetsModelingRule, LayoutRule, + CaseField, + CaseLayout, + CaseLayoutRule, ] FROM_VERSION_DICT: Dict[ContentType, str] = { @@ -96,6 +102,9 @@ ContentType.WIDGET: "5.0.0", ContentType.DASHBOARD: "5.0.0", ContentType.INCIDENT_TYPE: "5.0.0", + ContentType.CASE_FIELD: "8.7.0", + ContentType.CASE_LAYOUT: "8.7.0", + ContentType.CASE_LAYOUT_RULE: "8.7.0", } diff --git a/demisto_sdk/commands/validate/validators/BC_validators/BC105_id_changed.py b/demisto_sdk/commands/validate/validators/BC_validators/BC105_id_changed.py index 57d2388e1d4..45dfe3df4d2 100644 --- a/demisto_sdk/commands/validate/validators/BC_validators/BC105_id_changed.py +++ b/demisto_sdk/commands/validate/validators/BC_validators/BC105_id_changed.py @@ -3,6 +3,9 @@ from typing import Dict, Iterable, List, Union, cast from demisto_sdk.commands.common.constants import GitStatuses +from demisto_sdk.commands.content_graph.objects.case_field import CaseField +from demisto_sdk.commands.content_graph.objects.case_layout import CaseLayout +from demisto_sdk.commands.content_graph.objects.case_layout_rule import CaseLayoutRule from demisto_sdk.commands.content_graph.objects.classifier import Classifier from demisto_sdk.commands.content_graph.objects.correlation_rule import CorrelationRule from demisto_sdk.commands.content_graph.objects.dashboard import Dashboard @@ -69,6 +72,9 @@ GenericType, XSIAMDashboard, XSIAMReport, + CaseField, + CaseLayout, + CaseLayoutRule, ] diff --git a/demisto_sdk/commands/validate/validators/BC_validators/BC106_is_valid_fromversion_on_modified.py b/demisto_sdk/commands/validate/validators/BC_validators/BC106_is_valid_fromversion_on_modified.py index f436ed95289..d4ad813159f 100644 --- a/demisto_sdk/commands/validate/validators/BC_validators/BC106_is_valid_fromversion_on_modified.py +++ b/demisto_sdk/commands/validate/validators/BC_validators/BC106_is_valid_fromversion_on_modified.py @@ -3,6 +3,7 @@ from typing import Iterable, List, Union, cast from demisto_sdk.commands.common.constants import GitStatuses +from demisto_sdk.commands.content_graph.objects.case_field import CaseField from demisto_sdk.commands.content_graph.objects.incident_field import IncidentField from demisto_sdk.commands.content_graph.objects.incident_type import IncidentType from demisto_sdk.commands.content_graph.objects.integration import Integration @@ -13,7 +14,9 @@ ValidationResult, ) -ContentTypes = Union[Integration, Script, Mapper, IncidentType, IncidentField] +ContentTypes = Union[ + Integration, Script, Mapper, IncidentType, IncidentField, CaseField +] class IsValidFromversionOnModifiedValidator(BaseValidator[ContentTypes]): diff --git a/demisto_sdk/commands/validate/validators/IF_validators/IF100_is_valid_name_and_cli_name.py b/demisto_sdk/commands/validate/validators/IF_validators/IF100_is_valid_name_and_cli_name.py index bd059084c05..04423621ebf 100644 --- a/demisto_sdk/commands/validate/validators/IF_validators/IF100_is_valid_name_and_cli_name.py +++ b/demisto_sdk/commands/validate/validators/IF_validators/IF100_is_valid_name_and_cli_name.py @@ -1,14 +1,15 @@ from __future__ import annotations -from typing import Iterable, List +from typing import Iterable, List, Union +from demisto_sdk.commands.content_graph.objects.case_field import CaseField from demisto_sdk.commands.content_graph.objects.incident_field import IncidentField from demisto_sdk.commands.validate.validators.base_validator import ( BaseValidator, ValidationResult, ) -ContentTypes = IncidentField +ContentTypes = Union[CaseField, IncidentField] BAD_WORDS = { "incident", diff --git a/demisto_sdk/commands/validate/validators/IF_validators/IF101_is_valid_content_field.py b/demisto_sdk/commands/validate/validators/IF_validators/IF101_is_valid_content_field.py index 1f7be34d16f..c92332272a6 100644 --- a/demisto_sdk/commands/validate/validators/IF_validators/IF101_is_valid_content_field.py +++ b/demisto_sdk/commands/validate/validators/IF_validators/IF101_is_valid_content_field.py @@ -1,7 +1,8 @@ from __future__ import annotations -from typing import Iterable, List +from typing import Iterable, List, Union +from demisto_sdk.commands.content_graph.objects.case_field import CaseField from demisto_sdk.commands.content_graph.objects.incident_field import IncidentField from demisto_sdk.commands.validate.validators.base_validator import ( BaseValidator, @@ -9,7 +10,7 @@ ValidationResult, ) -ContentTypes = IncidentField +ContentTypes = Union[CaseField, IncidentField] class IsValidContentFieldValidator(BaseValidator[ContentTypes]): diff --git a/demisto_sdk/commands/validate/validators/IF_validators/IF102_is_valid_system_flag.py b/demisto_sdk/commands/validate/validators/IF_validators/IF102_is_valid_system_flag.py index bef22cc4fda..0fb8c91d5b1 100644 --- a/demisto_sdk/commands/validate/validators/IF_validators/IF102_is_valid_system_flag.py +++ b/demisto_sdk/commands/validate/validators/IF_validators/IF102_is_valid_system_flag.py @@ -1,7 +1,8 @@ from __future__ import annotations -from typing import Iterable, List +from typing import Iterable, List, Union +from demisto_sdk.commands.content_graph.objects.case_field import CaseField from demisto_sdk.commands.content_graph.objects.incident_field import IncidentField from demisto_sdk.commands.validate.validators.base_validator import ( BaseValidator, @@ -9,7 +10,7 @@ ValidationResult, ) -ContentTypes = IncidentField +ContentTypes = Union[CaseField, IncidentField] class IsValidSystemFlagValidator(BaseValidator[ContentTypes]): diff --git a/demisto_sdk/commands/validate/validators/IF_validators/IF103_is_valid_field_type.py b/demisto_sdk/commands/validate/validators/IF_validators/IF103_is_valid_field_type.py index ca599af0a8c..e55aa81eba7 100644 --- a/demisto_sdk/commands/validate/validators/IF_validators/IF103_is_valid_field_type.py +++ b/demisto_sdk/commands/validate/validators/IF_validators/IF103_is_valid_field_type.py @@ -1,14 +1,15 @@ from __future__ import annotations -from typing import Iterable, List +from typing import Iterable, List, Union +from demisto_sdk.commands.content_graph.objects.case_field import CaseField from demisto_sdk.commands.content_graph.objects.incident_field import IncidentField from demisto_sdk.commands.validate.validators.base_validator import ( BaseValidator, ValidationResult, ) -ContentTypes = IncidentField +ContentTypes = Union[CaseField, IncidentField] FIELD_TYPES = { "shortText", diff --git a/demisto_sdk/commands/validate/validators/IF_validators/IF105_is_cli_name_field_alphanumeric.py b/demisto_sdk/commands/validate/validators/IF_validators/IF105_is_cli_name_field_alphanumeric.py index 347eec44953..41e4ab98b81 100644 --- a/demisto_sdk/commands/validate/validators/IF_validators/IF105_is_cli_name_field_alphanumeric.py +++ b/demisto_sdk/commands/validate/validators/IF_validators/IF105_is_cli_name_field_alphanumeric.py @@ -1,14 +1,15 @@ from __future__ import annotations -from typing import Iterable, List +from typing import Iterable, List, Union +from demisto_sdk.commands.content_graph.objects.case_field import CaseField from demisto_sdk.commands.content_graph.objects.incident_field import IncidentField from demisto_sdk.commands.validate.validators.base_validator import ( BaseValidator, ValidationResult, ) -ContentTypes = IncidentField +ContentTypes = Union[CaseField, IncidentField] class IsCliNameFieldAlphanumericValidator(BaseValidator[ContentTypes]): diff --git a/demisto_sdk/commands/validate/validators/IF_validators/IF106_is_cli_name_reserved_word.py b/demisto_sdk/commands/validate/validators/IF_validators/IF106_is_cli_name_reserved_word.py index 17b0650e53c..d2c38b8b33f 100644 --- a/demisto_sdk/commands/validate/validators/IF_validators/IF106_is_cli_name_reserved_word.py +++ b/demisto_sdk/commands/validate/validators/IF_validators/IF106_is_cli_name_reserved_word.py @@ -1,14 +1,15 @@ from __future__ import annotations -from typing import Iterable, List +from typing import Iterable, List, Union +from demisto_sdk.commands.content_graph.objects.case_field import CaseField from demisto_sdk.commands.content_graph.objects.incident_field import IncidentField from demisto_sdk.commands.validate.validators.base_validator import ( BaseValidator, ValidationResult, ) -ContentTypes = IncidentField +ContentTypes = Union[CaseField, IncidentField] INCIDENT_PROHIBITED_CLI_NAMES = { "id", diff --git a/demisto_sdk/commands/validate/validators/LO_validators/LO107_is_valid_type.py b/demisto_sdk/commands/validate/validators/LO_validators/LO107_is_valid_type.py index 5ba268ae655..80dfc075af3 100644 --- a/demisto_sdk/commands/validate/validators/LO_validators/LO107_is_valid_type.py +++ b/demisto_sdk/commands/validate/validators/LO_validators/LO107_is_valid_type.py @@ -1,10 +1,11 @@ from __future__ import annotations -from typing import Dict, Iterable, List +from typing import Dict, Iterable, List, Union from ordered_set import OrderedSet from demisto_sdk.commands.common.constants import MarketplaceVersions +from demisto_sdk.commands.content_graph.objects.case_layout import CaseLayout from demisto_sdk.commands.content_graph.objects.layout import Layout from demisto_sdk.commands.content_graph.parsers.related_files import RelatedFileType from demisto_sdk.commands.validate.validators.base_validator import ( @@ -12,7 +13,7 @@ ValidationResult, ) -ContentTypes = Layout +ContentTypes = Union[Layout, CaseLayout] INVALID_SECTIONS: List = [ "evidence", diff --git a/demisto_sdk/scripts/init_validation_script.py b/demisto_sdk/scripts/init_validation_script.py index b6e352043b2..5513d311268 100644 --- a/demisto_sdk/scripts/init_validation_script.py +++ b/demisto_sdk/scripts/init_validation_script.py @@ -148,6 +148,18 @@ "import": "from demisto_sdk.commands.content_graph.objects.xsiam_report import XSIAMReport", "content_type": "XSIAMReport", }, + "30": { + "import": "from demisto_sdk.commands.content_graph.objects.case_field", + "content_type": "CaseField", + }, + "31": { + "import": "from demisto_sdk.commands.content_graph.objects.case_layout", + "content_type": "CaseLayout", + }, + "32": { + "import": "from demisto_sdk.commands.content_graph.objects.case_layout_rule", + "content_type": "CaseLayoutRule", + }, } VALIDATION_TEMPLATE = """