"""Dump tools for AsusRouter. This module contains all needed to dump the raw data from the router for the following analysis and testing. """ from __future__ import annotations import asyncio from datetime import UTC, datetime import json import os from types import TracebackType from typing import Any, Self import zipfile from asusrouter.modules.endpoint import Endpoint class AsusRouterDump: """AsusRouter Dumper.""" _endpoint: Endpoint _datetime: str def __init__( self, output_folder: str, full_dump: bool = False, archive: bool = True ) -> None: """Initialize.""" self.log: dict[str, str] = {} self._output_folder = output_folder self.full_dump = full_dump self.zip = archive self._init_datetime = datetime.now(UTC) if self.zip: self._zipfile = zipfile.ZipFile( os.path.join( # noqa: PTH118 self._output_folder, ( "AsusRouter-" f"{self._init_datetime.isoformat().replace(':', '-')}" ".zip" ), ), "w", ) else: # Create subfolder for the dump self._output_folder = os.path.join( # noqa: PTH118 output_folder, f"AsusRouter-" f"{self._init_datetime.isoformat().replace(':', '-')}", ) os.makedirs(self._output_folder) # noqa: PTH103 def __enter__(self) -> Self: """Enter the runtime context related to this object.""" return self def __exit__( self, exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: TracebackType | None, ) -> None: """Exit the runtime context and save the log.""" if self.zip: # Write the log data to the zip file self._zipfile.writestr( "log.json", json.dumps(self.log, default=str) ) self._zipfile.close() else: log_filename = os.path.join(self._output_folder, "log.json") # noqa: PTH118 with open(log_filename, "w", encoding="utf-8") as f: # noqa: PTH123 json.dump(self.log, f, default=str) async def dump( self, endpoint: Endpoint, payload: dict[str, Any], resp_status: int, resp_headers: dict[str, Any], resp_content: bytes, ) -> None: """Dump the data.""" self._endpoint = endpoint self._datetime = datetime.now(UTC).isoformat().replace(":", "-") self.log[self._datetime] = f"Endpoint.{endpoint.name}" # Write the response content to a file loop = asyncio.get_running_loop() await loop.run_in_executor(None, self._write_content, resp_content) # Write the metadata to a file metadata = { "endpoint": endpoint, "payload": payload, "resp_status": resp_status, "resp_headers": resp_headers, } await loop.run_in_executor(None, self._write_metadata, metadata) def _write_content(self, content: str) -> None: """Write the content to a file.""" if self.full_dump: filename = f"{self._datetime}-{self._endpoint}.content" if self.zip: self._zipfile.writestr(filename, content) else: with open( # noqa: PTH123 os.path.join(self._output_folder, filename), # noqa: PTH118 "w", encoding="utf-8", ) as f: f.write(content) def _write_metadata(self, metadata: dict[str, Any]) -> None: """Write the metadata to a file.""" if self.full_dump: filename = f"{self._datetime}-{self._endpoint}.json" if self.zip: self._zipfile.writestr( filename, json.dumps(metadata, default=str) ) else: with open( # noqa: PTH123 os.path.join(self._output_folder, filename), # noqa: PTH118 "w", encoding="utf-8", ) as f: json.dump(metadata, f, default=str)