"""
Data models for OpenAPI specifications and operations.
"""
from typing import Any, Dict, List, Optional, Union
from dataclasses import dataclass, field
from enum import Enum
class HTTPMethod(str, Enum):
"""Supported HTTP methods."""
GET = "get"
POST = "post"
PUT = "put"
DELETE = "delete"
PATCH = "patch"
HEAD = "head"
OPTIONS = "options"
@dataclass
class Parameter:
"""Represents an OpenAPI parameter."""
name: str
param_type: str # query, path, header, etc.
required: bool = False
schema: Dict[str, Any] = field(default_factory=dict)
description: Optional[str] = None
example: Optional[Any] = None
@dataclass
class RequestBody:
"""Represents an OpenAPI request body."""
content_type: str
schema: Dict[str, Any]
required: bool = False
description: Optional[str] = None
example: Optional[Any] = None
@dataclass
class Response:
"""Represents an OpenAPI response."""
status_code: str
description: Optional[str] = None
content_type: Optional[str] = None
schema: Dict[str, Any] = field(default_factory=dict)
@dataclass
class APIOperation:
"""Represents a single API operation (endpoint + method)."""
# Basic information
operation_id: str
method: HTTPMethod
path: str
summary: Optional[str] = None
description: Optional[str] = None
tags: List[str] = field(default_factory=list)
# Parameters and body
parameters: List[Parameter] = field(default_factory=list)
request_body: Optional[RequestBody] = None
# Responses
responses: Dict[str, Response] = field(default_factory=dict)
# Security
security: List[Dict[str, List[str]]] = field(default_factory=list)
# Additional metadata
deprecated: bool = False
external_docs: Optional[Dict[str, str]] = None
@dataclass
class SecurityScheme:
"""Represents an OpenAPI security scheme."""
type: str
description: Optional[str] = None
name: Optional[str] = None # For API key
location: Optional[str] = None # header, query, cookie
scheme: Optional[str] = None # For HTTP auth
bearer_format: Optional[str] = None
flows: Optional[Dict[str, Any]] = None # For OAuth2
@dataclass
class ServerInfo:
"""Represents an OpenAPI server."""
url: str
description: Optional[str] = None
variables: Dict[str, Dict[str, Any]] = field(default_factory=dict)
@dataclass
class ContactInfo:
"""Represents contact information."""
name: Optional[str] = None
url: Optional[str] = None
email: Optional[str] = None
@dataclass
class LicenseInfo:
"""Represents license information."""
name: str
url: Optional[str] = None
@dataclass
class InfoObject:
"""Represents the info object from OpenAPI spec."""
title: str
version: str
description: Optional[str] = None
terms_of_service: Optional[str] = None
contact: Optional[ContactInfo] = None
license: Optional[LicenseInfo] = None
@dataclass
class OpenAPISpec:
"""Represents a parsed OpenAPI specification."""
# Basic info
openapi_version: str
info: InfoObject
servers: List[ServerInfo] = field(default_factory=list)
# Operations
operations: List[APIOperation] = field(default_factory=list)
# Security
security_schemes: Dict[str, SecurityScheme] = field(default_factory=dict)
security: List[Dict[str, List[str]]] = field(default_factory=list)
# Schema definitions
components: Dict[str, Any] = field(default_factory=dict)
# Tags
tags: List[Dict[str, str]] = field(default_factory=list)
# External docs
external_docs: Optional[Dict[str, str]] = None
def get_operation_by_id(self, operation_id: str) -> Optional[APIOperation]:
"""Get an operation by its operation ID."""
for operation in self.operations:
if operation.operation_id == operation_id:
return operation
return None
def get_operations_by_tag(self, tag: str) -> List[APIOperation]:
"""Get all operations with a specific tag."""
return [op for op in self.operations if tag in op.tags]
def get_base_url(self) -> Optional[str]:
"""Get the first server URL as base URL."""
if self.servers:
return self.servers[0].url
return None