diff --git a/demisto_sdk/commands/content_graph/parsers/related_files.py b/demisto_sdk/commands/content_graph/parsers/related_files.py index 461955972af..5d7c07c6379 100644 --- a/demisto_sdk/commands/content_graph/parsers/related_files.py +++ b/demisto_sdk/commands/content_graph/parsers/related_files.py @@ -1,3 +1,4 @@ +import base64 from abc import ABC from enum import Enum from pathlib import Path @@ -198,14 +199,30 @@ def get_file_size(self): def get_file_dimensions(self): raise NotImplementedError + def load_image(self): + raise NotImplementedError + class PNGFiles(ImageFiles): def get_file_size(self): return self.file_path.stat() + def load_image(self) -> Union[str, bytearray, memoryview]: + encoded_image = "" + with open(self.file_path, "rb") as image: + image_data = image.read() + encoded_image = base64.b64encode(image_data) # type: ignore + if isinstance(encoded_image, bytes): + encoded_image = encoded_image.decode("utf-8") + return encoded_image + class SVGFiles(ImageFiles): - pass + def load_image(self) -> bytes: + encoded_image = b"" + with open(self.file_path, "rb") as image_file: + encoded_image = image_file.read() # type: ignore + return encoded_image class DarkSVGRelatedFile(RelatedFile): diff --git a/demisto_sdk/commands/validate/sdk_validation_config.toml b/demisto_sdk/commands/validate/sdk_validation_config.toml index fc2ae7c5b3a..f59f9e77199 100644 --- a/demisto_sdk/commands/validate/sdk_validation_config.toml +++ b/demisto_sdk/commands/validate/sdk_validation_config.toml @@ -36,7 +36,7 @@ select = [ "CL100", "GF100", "GF101", "GF102", "IF100", "IF101", "IF102", "IF103", "IF104", "IF105", "IF106", - "IM100", "IM108", "IM109", + "IM100", "IM108", "IM109", "IM106", "RP101", "BC106", "BC107" ] warning = [] diff --git a/demisto_sdk/commands/validate/tests/IM_validators_test.py b/demisto_sdk/commands/validate/tests/IM_validators_test.py index 3f8c2936f13..be6e0fa25de 100644 --- a/demisto_sdk/commands/validate/tests/IM_validators_test.py +++ b/demisto_sdk/commands/validate/tests/IM_validators_test.py @@ -1,5 +1,9 @@ import pytest +from pytest_mock import MockerFixture +from demisto_sdk.commands.content_graph.parsers.related_files import ( + ImageRelatedFile, +) from demisto_sdk.commands.validate.tests.test_tools import ( create_integration_object, create_pack_object, @@ -7,12 +11,16 @@ from demisto_sdk.commands.validate.validators.IM_validators.IM100_image_exists_validation import ( ImageExistsValidator, ) +from demisto_sdk.commands.validate.validators.IM_validators.IM106_default_image_validator import ( + DefaultImageValidator, +) from demisto_sdk.commands.validate.validators.IM_validators.IM108_author_image_is_empty import ( AuthorImageIsEmptyValidator, ) from demisto_sdk.commands.validate.validators.IM_validators.IM109_author_image_exists_validation import ( AuthorImageExistsValidator, ) +from demisto_sdk.tests.constants_test import DEFAULT_IMAGE def test_ImageExistsValidator_is_valid_image_path(): @@ -112,3 +120,40 @@ def test_AuthorImageIsEmptyValidator_is_valid( for result, expected_msg in zip(results, expected_msgs) ] ) + + +def test_DefaultImageValidator_is_valid(mocker: MockerFixture): + """ + Given: + - First integration with a default image. + - Second integration with a sample image + + When: + - Calling the DefaultImageValidator is_valid function. + + Then: + - Make sure the right amount of integration image validation failed, and that the right error message is returned. + - Case 1: Should fail. + - Case 2: Shouldn't fail. + """ + from pathlib import Path + + default_image = ImageRelatedFile(main_file_path=Path(DEFAULT_IMAGE)) + sample_image = ImageRelatedFile( + main_file_path=Path("TestSuite/assets/default_integration/sample_image.png") + ) + + content_items = [create_integration_object(), create_integration_object()] + mocker.patch.object( + content_items[0].image, "load_image", return_value=default_image.load_image() + ) + mocker.patch.object( + content_items[1].image, "load_image", return_value=sample_image.load_image() + ) + results = DefaultImageValidator().is_valid(content_items) + assert len(results) == 1 + assert results[ + 0 + ].message == "The integration is using the default image at {0}, please change to the integration image.".format( + DEFAULT_IMAGE + ) diff --git a/demisto_sdk/commands/validate/validators/IM_validators/IM106_default_image_validator.py b/demisto_sdk/commands/validate/validators/IM_validators/IM106_default_image_validator.py new file mode 100644 index 00000000000..7b4778a4e90 --- /dev/null +++ b/demisto_sdk/commands/validate/validators/IM_validators/IM106_default_image_validator.py @@ -0,0 +1,46 @@ +from __future__ import annotations + +from typing import Iterable, List + +from demisto_sdk.commands.common.constants import ( + DEFAULT_DBOT_IMAGE_BASE64, + DEFAULT_IMAGE_BASE64, +) +from demisto_sdk.commands.content_graph.objects.integration import Integration +from demisto_sdk.commands.content_graph.parsers.related_files import RelatedFileType +from demisto_sdk.commands.validate.validators.base_validator import ( + BaseValidator, + ValidationResult, +) +from demisto_sdk.tests.constants_test import DEFAULT_IMAGE + +ContentTypes = Integration + + +class DefaultImageValidator(BaseValidator[ContentTypes]): + error_code = "IM106" + description = "Checks if the integration has an image other than the default ones." + rationale = "If an image is provided, it must not be the default ones." + error_message = "The integration is using the default image at {0}, please change to the integration image." + related_field = "image" + is_auto_fixable = False + related_file_type = [RelatedFileType.IMAGE] + + def is_valid(self, content_items: Iterable[ContentTypes]) -> List[ValidationResult]: + return [ + ValidationResult( + validator=self, + message=self.error_message.format( + DEFAULT_IMAGE, + ), + content_object=content_item, + ) + for content_item in content_items + if ( + content_item.image.load_image() + in [ + DEFAULT_IMAGE_BASE64, + DEFAULT_DBOT_IMAGE_BASE64, + ] + ) + ]