diff --git a/docs/source/changelog.rst b/docs/source/changelog.rst index 79d2680..a76fb91 100644 --- a/docs/source/changelog.rst +++ b/docs/source/changelog.rst @@ -1,5 +1,8 @@ Changelog ========= +- :release:`0.2.0 <6th April 2023>` +- :feature:`6` Setup analyzing + - :release:`0.1.0 <3rd April 2023>` - :feature:`1` Initialize package diff --git a/requirements/requirements-tests.in b/requirements/requirements-tests.in index 83ef1be..ef23890 100644 --- a/requirements/requirements-tests.in +++ b/requirements/requirements-tests.in @@ -3,5 +3,3 @@ pytest pytest-randomly - -httpx # for fastapi.testclient.TestClient diff --git a/requirements/requirements-tests.txt b/requirements/requirements-tests.txt index 7317a50..f0875d8 100644 --- a/requirements/requirements-tests.txt +++ b/requirements/requirements-tests.txt @@ -4,28 +4,6 @@ # # pip-compile requirements/requirements-tests.in # -anyio==4.3.0 - # via - # -c requirements/requirements.txt - # httpx -certifi==2024.2.2 - # via - # -c requirements/requirements.txt - # httpcore - # httpx -h11==0.14.0 - # via - # -c requirements/requirements.txt - # httpcore -httpcore==1.0.5 - # via httpx -httpx==0.27.0 - # via -r requirements/requirements-tests.in -idna==3.6 - # via - # -c requirements/requirements.txt - # anyio - # httpx iniconfig==2.0.0 # via pytest packaging==24.0 @@ -38,8 +16,3 @@ pytest==8.2.0 # pytest-randomly pytest-randomly==3.15.0 # via -r requirements/requirements-tests.in -sniffio==1.3.1 - # via - # -c requirements/requirements.txt - # anyio - # httpx diff --git a/requirements/requirements.in b/requirements/requirements.in index 0be69ca..4e734e8 100644 --- a/requirements/requirements.in +++ b/requirements/requirements.in @@ -6,6 +6,7 @@ uvicorn[standard] fastapi pydantic-settings +httpx # Metrics sentry-sdk[fastapi] diff --git a/requirements/requirements.txt b/requirements/requirements.txt index 65367fc..12cf44b 100644 --- a/requirements/requirements.txt +++ b/requirements/requirements.txt @@ -8,10 +8,14 @@ annotated-types==0.6.0 # via pydantic anyio==4.3.0 # via + # httpx # starlette # watchfiles certifi==2024.2.2 - # via sentry-sdk + # via + # httpcore + # httpx + # sentry-sdk click==8.1.7 # via uvicorn fastapi==0.110.3 @@ -19,11 +23,19 @@ fastapi==0.110.3 # -r requirements/requirements.in # sentry-sdk h11==0.14.0 - # via uvicorn + # via + # httpcore + # uvicorn +httpcore==1.0.5 + # via httpx httptools==0.6.1 # via uvicorn +httpx==0.27.0 + # via -r requirements/requirements.in idna==3.6 - # via anyio + # via + # anyio + # httpx pydantic==2.6.4 # via # fastapi @@ -41,7 +53,9 @@ pyyaml==6.0.1 sentry-sdk[fastapi]==2.0.1 # via -r requirements/requirements.in sniffio==1.3.1 - # via anyio + # via + # anyio + # httpx starlette==0.37.2 # via fastapi typing-extensions==4.10.0 diff --git a/src/zipalyzer/__init__.py b/src/zipalyzer/__init__.py index 0f14d68..142c4e1 100644 --- a/src/zipalyzer/__init__.py +++ b/src/zipalyzer/__init__.py @@ -1,3 +1,3 @@ """An API for analyzing zip files.""" -__version__ = "0.1.0" +__version__ = "0.2.0" diff --git a/src/zipalyzer/files.py b/src/zipalyzer/files.py new file mode 100644 index 0000000..79cbe42 --- /dev/null +++ b/src/zipalyzer/files.py @@ -0,0 +1,30 @@ +"""Handle files and file contents.""" + +from hashlib import sha256 +from io import BytesIO +from urllib.parse import unquote +from zipfile import ZipFile + +import httpx + +from zipalyzer.models.zip_information import File + + +async def get_zip_contents(path: str) -> dict[str, bytes]: + """Get zip contents.""" + async with httpx.AsyncClient() as client: + response = await client.get(url=unquote(path)) + zip_file = ZipFile(BytesIO(response.content)) + return {name: zip_file.read(name) for name in zip_file.namelist()} + + +def process_zip_contents(files: dict[str, bytes]) -> list[File]: + """Process zip file contents.""" + return [ + File( + name=name, + contents=contents, + sha256_hash=sha256(contents).hexdigest(), + ) + for name, contents in files.items() + ] diff --git a/src/zipalyzer/models/zip_information.py b/src/zipalyzer/models/zip_information.py new file mode 100644 index 0000000..5cc34bb --- /dev/null +++ b/src/zipalyzer/models/zip_information.py @@ -0,0 +1,12 @@ +"""Zip information models.""" + +from pydantic.dataclasses import dataclass + + +@dataclass +class File: + """A file in a zip.""" + + name: str + contents: bytes + sha256_hash: str diff --git a/src/zipalyzer/server.py b/src/zipalyzer/server.py index ed82b9d..4d7a84f 100644 --- a/src/zipalyzer/server.py +++ b/src/zipalyzer/server.py @@ -5,6 +5,7 @@ from fastapi.middleware.cors import CORSMiddleware from zipalyzer.metadata.routes import router as router_metadata +from zipalyzer.zips.routes import router as router_zips from . import __version__ from .constants import GIT_SHA, Sentry @@ -32,3 +33,4 @@ ) app.include_router(router_metadata) +app.include_router(router_zips) diff --git a/src/zipalyzer/zips/__init__.py b/src/zipalyzer/zips/__init__.py new file mode 100644 index 0000000..8199e5e --- /dev/null +++ b/src/zipalyzer/zips/__init__.py @@ -0,0 +1 @@ +"""Routes for zip handling.""" diff --git a/src/zipalyzer/zips/routes.py b/src/zipalyzer/zips/routes.py new file mode 100644 index 0000000..728bf55 --- /dev/null +++ b/src/zipalyzer/zips/routes.py @@ -0,0 +1,15 @@ +"""Routes for zip handling.""" + +from fastapi import APIRouter + +from zipalyzer.files import get_zip_contents, process_zip_contents +from zipalyzer.models.zip_information import File + +router = APIRouter(prefix="/zips", tags=["Zips"]) + + +@router.get("/{path:path}", summary="Get zip information") +async def zip_info(path: str) -> list[File]: + """Get zip information.""" + contents = await get_zip_contents(path) + return process_zip_contents(contents) diff --git a/tests/test_server.py b/tests/test_server.py index 8f1375c..d48432e 100644 --- a/tests/test_server.py +++ b/tests/test_server.py @@ -3,7 +3,6 @@ from http import HTTPStatus from fastapi.testclient import TestClient - from zipalyzer import __version__ from zipalyzer.server import app