From 944239c65d0e61b27e595d7bff82fd96b3d2136d Mon Sep 17 00:00:00 2001 From: Arseney Zhiltsov Date: Tue, 14 Sep 2021 13:27:22 +0300 Subject: [PATCH] Remove file ext validation (#185) --- botx/clients/clients/processing.py | 2 +- botx/models/files.py | 151 ++++++++++-------- docs/changelog.md | 16 ++ pyproject.toml | 2 +- .../test_models/test_files/test_attributes.py | 18 +-- tests/test_models/test_files/test_errors.py | 10 -- 6 files changed, 104 insertions(+), 95 deletions(-) delete mode 100644 tests/test_models/test_files/test_errors.py diff --git a/botx/clients/clients/processing.py b/botx/clients/clients/processing.py index 82f2ed31..7c98b50d 100644 --- a/botx/clients/clients/processing.py +++ b/botx/clients/clients/processing.py @@ -24,7 +24,7 @@ def build_file(response: HTTPResponse) -> File: Built file from response. """ mimetype = response.headers["content-type"].split(";", 1)[0] - ext = File.get_ext_by_mimetype(mimetype) + ext = File.get_ext_by_mimetype(mimetype) or "" file_name = "document{0}".format(ext) return File.from_file(BytesIO(response.raw_data), file_name) # type: ignore diff --git a/botx/models/files.py b/botx/models/files.py index 1d1694fb..60912bd3 100644 --- a/botx/models/files.py +++ b/botx/models/files.py @@ -9,58 +9,115 @@ from uuid import UUID from base64io import Base64IO -from pydantic import validator from botx.models.base import BotXBaseModel from botx.models.enums import AttachmentsTypes EXTENSIONS_TO_MIMETYPES = MappingProxyType( { - # image_extensions - ".gif": "image/gif", - ".jpeg": "image/jpeg", - ".jpg": "image/jpeg", - ".png": "image/png", - ".svg": "image/svg+xml", - ".tiff": "image/tiff", - # document_extensions - ".csv": "text/csv", + # application + ".7z": "application/x-7z-compressed", + ".abw": "application/x-abiword", + ".ai": "application/postscript", + ".arc": "application/x-freearc", + ".azw": "application/vnd.amazon.ebook", + ".bin": "application/octet-stream", + ".bz": "application/x-bzip", + ".bz2": "application/x-bzip2", + ".cda": "application/x-cdf", + ".csh": "application/x-csh", ".doc": "application/msword", - ".docm": "application/vnd.ms-word.document.macroenabled.12", ".docx": ( "application/vnd.openxmlformats-officedocument.wordprocessingml.document" ), + ".eot": "application/vnd.ms-fontobject", + ".eps": "application/postscript", + ".epub": "application/epub+zip", ".gz": "application/gzip", - ".html": "text/html", + ".jar": "application/java-archive", + ".json-api": "application/vnd.api+json", + ".json-patch": "application/json-patch+json", ".json": "application/json", - ".mp3": "audio/mpeg", - ".mp4": "video/mp4", + ".jsonld": "application/ld+json", + ".mdb": "application/x-msaccess", + ".mpkg": "application/vnd.apple.installer+xml", ".odp": "application/vnd.oasis.opendocument.presentation", ".ods": "application/vnd.oasis.opendocument.spreadsheet", ".odt": "application/vnd.oasis.opendocument.text", + ".ogx": "application/ogg", ".pdf": "application/pdf", + ".php": "application/x-httpd-php", ".ppt": "application/vnd.ms-powerpoint", - ".pptm": "application/vnd.ms-powerpoint.presentation.macroenabled.12", ".pptx": ( "application/vnd.openxmlformats-officedocument.presentationml.presentation" ), - ".psd": "image/vnd.adobe.photoshop", + ".ps": "application/postscript", ".rar": "application/vnd.rar", ".rtf": "application/rtf", - ".sig": "application/pgp-signature", - ".tgz": "application/gzip", - ".txt": "text/plain", + ".sh": "application/x-sh", + ".swf": "application/x-shockwave-flash", + ".tar": "application/x-tar", ".vsd": "application/vnd.visio", - ".vsdx": "application/octet-stream", + ".wasm": "application/wasm", + ".webmanifest": "application/manifest+json", + ".xhtml": "application/xhtml+xml", ".xls": "application/vnd.ms-excel", - ".xlsm": "application/vnd.ms-excel.sheet.macroenabled.12", ".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", - ".xml": "text/xml", + ".xul": "application/vnd.mozilla.xul+xml", ".zip": "application/zip", + # audio + ".aac": "audio/aac", + ".mid": "audio/midi", + ".midi": "audio/midi", + ".mp3": "audio/mpeg", + ".oga": "audio/ogg", + ".opus": "audio/opus", + ".wav": "audio/wav", + ".weba": "audio/webm", + # font + ".otf": "font/otf", + ".ttf": "font/ttf", + ".woff": "font/woff", + ".woff2": "font/woff2", + # image + ".avif": "image/avif", + ".bmp": "image/bmp", + ".gif": "image/gif", + ".ico": "image/vnd.microsoft.icon", + ".jpeg": "image/jpeg", + ".jpg": "image/jpeg", + ".png": "image/png", + ".svg": "image/svg+xml", + ".svgz": "image/svg+xml", + ".tif": "image/tiff", + ".tiff": "image/tiff", + ".webp": "image/webp", + # text + ".css": "text/css", + ".csv": "text/csv", + ".htm": "text/html", + ".html": "text/html", + ".ics": "text/calendar", + ".js": "text/javascript", + ".mjs": "text/javascript", + ".txt": "text/plain", + ".text": "text/plain", + ".xml": "text/xml", + # video + ".3g2": "video/3gpp2", + ".3gp": "video/3gpp", + ".avi": "video/x-msvideo", + ".mov": "video/quicktime", + ".mp4": "video/mp4", + ".mpeg": "video/mpeg", + ".mpg": "video/mpeg", + ".ogv": "video/ogg", + ".ts": "video/mp2t", + ".webm": "video/webm", + ".wmv": "video/x-ms-wmv", }, ) -#: file extensions that can be proceed by BotX API. -BOTX_API_ACCEPTED_EXTENSIONS = EXTENSIONS_TO_MIMETYPES.keys() +DEFAULT_MIMETYPE = "application/octet-stream" class NamedAsyncIterable(AsyncIterable): @@ -81,28 +138,6 @@ class File(BotXBaseModel): # noqa: WPS214 #: text under file. caption: Optional[str] = None - @validator("file_name", always=True) - def check_file_extension(cls, name: str) -> str: # noqa: N805 - """Check that file extension can be handled by BotX API. - - Arguments: - name: file name which will be checked for matching extensions. - - Returns: - Passed name if matching was successful. - - Raises: - ValueError: raised if extension is not supported. - """ - if not cls.has_supported_extension(name): - raise ValueError( - "file {0} has an extensions that is not supported by BotX API".format( - name, - ), - ) - - return name - @classmethod def from_file( # noqa: WPS210 cls, @@ -229,36 +264,20 @@ def media_type(self) -> str: return self._get_mimetype(self.file_name) @classmethod - def has_supported_extension(cls, filename: str) -> bool: - """Check that file extension can be handled by BotX API. - - Arguments: - filename: file name to check. - - Returns: - Matching result. - """ - file_extension = Path(filename).suffix.lower() - return file_extension in BOTX_API_ACCEPTED_EXTENSIONS - - @classmethod - def get_ext_by_mimetype(cls, mimetype: str) -> str: + def get_ext_by_mimetype(cls, mimetype: str) -> Optional[str]: """Get extension by mimetype. Arguments: mimetype: mimetype of file. Returns: - file extension. - - Raises: - ValueError: when mimetype is unsupported. + file extension or none if mimetype not found. """ for ext, m_type in EXTENSIONS_TO_MIMETYPES.items(): if m_type == mimetype: return ext - raise ValueError("`{0}` is unsupported mimetype.".format(mimetype)) + return None @classmethod def _to_rfc2397(cls, media_type: str, encoded_data: str) -> str: @@ -284,7 +303,7 @@ def _get_mimetype(cls, filename: str) -> str: File mimetype. """ file_extension = Path(filename).suffix.lower() - return EXTENSIONS_TO_MIMETYPES[file_extension] + return EXTENSIONS_TO_MIMETYPES.get(file_extension, DEFAULT_MIMETYPE) class MetaFile(BotXBaseModel): diff --git a/docs/changelog.md b/docs/changelog.md index 6f0922db..0b1f8d42 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,3 +1,19 @@ +## 0.24.0 (Sep 14, 2021) + +### Removed + +* File extensions validation. +* `File.has_supported_extension` classmethod. + +### Added + +* Multiple mime-types. + +### Changed + +* `File.get_ext_by_mimetype` now don't raise `ValueError` and returns `None` if mimetype not found. + + ## 0.23.2 (Sep 09, 2021) ### Added diff --git a/pyproject.toml b/pyproject.toml index e2906561..4dcbbb8d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "botx" -version = "0.23.2" +version = "0.24.0" description = "A little python framework for building bots for eXpress" license = "MIT" authors = [ diff --git a/tests/test_models/test_files/test_attributes.py b/tests/test_models/test_files/test_attributes.py index 5d27f7e6..43d8f984 100644 --- a/tests/test_models/test_files/test_attributes.py +++ b/tests/test_models/test_files/test_attributes.py @@ -1,5 +1,3 @@ -import pytest - from botx import File @@ -23,19 +21,5 @@ def test_retrieving_file_size(): assert File.from_string(b"file\ncontents", filename="test.txt").size_in_bytes == 13 -@pytest.mark.parametrize("extension", [".txt", ".TXT"]) -def test_accept_has_supported_extension(extension): - filename = f"test{extension}" - - assert File.has_supported_extension(filename) - - -def test_decline_has_supported_extension(): - bad_filename = "test.bad" - - assert not File.has_supported_extension(bad_filename) - - def test_get_ext_by_unsupported_mimetype(): - with pytest.raises(ValueError): - File.get_ext_by_mimetype("application/javascript") + assert File.get_ext_by_mimetype("application/javascript") is None diff --git a/tests/test_models/test_files/test_errors.py b/tests/test_models/test_files/test_errors.py deleted file mode 100644 index a1d3128d..00000000 --- a/tests/test_models/test_files/test_errors.py +++ /dev/null @@ -1,10 +0,0 @@ -import pytest -from pydantic import ValidationError - -from botx import File - - -@pytest.mark.parametrize("extension", [".py", ".c", ".java"]) -def test_error_with_wrong_extension(extension): - with pytest.raises(ValidationError): - File(file_name="tmp{0}".format(extension), data="")