Initial upload: mockapi - OpenAPI Mock Server Generator
This commit is contained in:
93
src/mockapi/core/spec_loader.py
Normal file
93
src/mockapi/core/spec_loader.py
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
"""OpenAPI Specification Loader."""
|
||||||
|
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any, Dict, Optional
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
|
||||||
|
class SpecLoaderError(Exception):
|
||||||
|
"""Raised when spec loading fails."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class SpecLoader:
|
||||||
|
"""Loads and parses OpenAPI specifications from YAML or JSON files."""
|
||||||
|
|
||||||
|
SUPPORTED_EXTENSIONS = {".yaml", ".yml", ".json"}
|
||||||
|
|
||||||
|
def __init__(self, spec_path: str, fmt: Optional[str] = None):
|
||||||
|
"""Initialize the spec loader.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
spec_path: Path to the OpenAPI spec file
|
||||||
|
fmt: Force format (yaml or json), auto-detected if None
|
||||||
|
"""
|
||||||
|
self.spec_path = Path(spec_path)
|
||||||
|
self.fmt = fmt or self._detect_format()
|
||||||
|
|
||||||
|
if not self.spec_path.exists():
|
||||||
|
raise SpecLoaderError(f"Spec file not found: {spec_path}")
|
||||||
|
|
||||||
|
if self.spec_path.suffix not in self.SUPPORTED_EXTENSIONS:
|
||||||
|
raise SpecLoaderError(
|
||||||
|
f"Unsupported file format: {self.spec_path.suffix}. "
|
||||||
|
f"Supported: {', '.join(self.SUPPORTED_EXTENSIONS)}"
|
||||||
|
)
|
||||||
|
|
||||||
|
def _detect_format(self) -> str:
|
||||||
|
"""Detect the spec format from file extension."""
|
||||||
|
ext = self.spec_path.suffix.lower()
|
||||||
|
if ext in {".yaml", ".yml"}:
|
||||||
|
return "yaml"
|
||||||
|
elif ext == ".json":
|
||||||
|
return "json"
|
||||||
|
return "yaml"
|
||||||
|
|
||||||
|
def load(self) -> Dict[str, Any]:
|
||||||
|
"""Load and parse the OpenAPI specification.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dictionary containing the parsed spec
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
SpecLoaderError: If loading or parsing fails
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
with open(self.spec_path, "r", encoding="utf-8") as f:
|
||||||
|
if self.fmt == "yaml":
|
||||||
|
spec = yaml.safe_load(f)
|
||||||
|
else:
|
||||||
|
spec = json.load(f)
|
||||||
|
|
||||||
|
if not isinstance(spec, dict):
|
||||||
|
raise SpecLoaderError("Invalid spec: root must be an object")
|
||||||
|
|
||||||
|
return spec
|
||||||
|
|
||||||
|
except yaml.YAMLError as e:
|
||||||
|
raise SpecLoaderError(f"YAML parsing error: {e}")
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
raise SpecLoaderError(f"JSON parsing error: {e}")
|
||||||
|
except Exception as e:
|
||||||
|
raise SpecLoaderError(f"Failed to load spec: {e}")
|
||||||
|
|
||||||
|
def get_paths(self) -> Dict[str, Any]:
|
||||||
|
"""Get all paths from the spec."""
|
||||||
|
return self.load().get("paths", {})
|
||||||
|
|
||||||
|
def get_schemas(self) -> Dict[str, Any]:
|
||||||
|
"""Get all schemas from the spec."""
|
||||||
|
return self.load().get("components", {}).get("schemas", {})
|
||||||
|
|
||||||
|
def get_info(self) -> Dict[str, Any]:
|
||||||
|
"""Get the info section from the spec."""
|
||||||
|
return self.load().get("info", {})
|
||||||
|
|
||||||
|
def get_server_url(self) -> Optional[str]:
|
||||||
|
"""Get the server URL from the spec."""
|
||||||
|
servers = self.load().get("servers", [])
|
||||||
|
if servers:
|
||||||
|
return servers[0].get("url", "http://localhost:8080")
|
||||||
|
return None
|
||||||
Reference in New Issue
Block a user