2025-05-16 15:18:02 +08:00

355 lines
14 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Input Parser Module"""
import json
import os
from typing import Any, Dict, Optional, List, Union
from pydantic import BaseModel # For defining the structure of parsed inputs
from dataclasses import dataclass, field
import logging
logger = logging.getLogger("InputParser")
class ParsedOpenAPISpec(BaseModel):
# Placeholder for OpenAPI spec details relevant to the compliance suite
spec: Dict[str, Any]
info: Dict[str, Any] # Swagger 'info' object with title, version, etc.
paths: Dict[str, Dict[str, Any]] # API paths and their operations
tags: Optional[List[Dict[str, str]]] = None # API tags
basePath: Optional[str] = None # Base path for all APIs
swagger_version: str # Swagger specification version
@dataclass
class YAPIEndpoint:
"""YAPI API端点信息"""
path: str
method: str
title: str = ""
description: str = ""
category_name: str = ""
req_params: List[Dict[str, Any]] = field(default_factory=list)
req_query: List[Dict[str, Any]] = field(default_factory=list)
req_headers: List[Dict[str, Any]] = field(default_factory=list)
req_body_type: str = ""
req_body_other: str = ""
res_body_type: str = ""
res_body: str = ""
@dataclass
class ParsedYAPISpec:
"""解析后的YAPI规范"""
endpoints: List[YAPIEndpoint]
categories: List[Dict[str, Any]]
total_count: int
@dataclass
class SwaggerEndpoint:
"""Swagger API端点信息"""
path: str
method: str
summary: str = ""
description: str = ""
operation_id: str = ""
tags: List[str] = field(default_factory=list)
parameters: List[Dict[str, Any]] = field(default_factory=list)
responses: Dict[str, Any] = field(default_factory=dict)
consumes: List[str] = field(default_factory=list)
produces: List[str] = field(default_factory=list)
request_body: Dict[str, Any] = field(default_factory=dict)
@dataclass
class ParsedSwaggerSpec:
"""解析后的Swagger规范"""
endpoints: List[SwaggerEndpoint]
info: Dict[str, Any]
swagger_version: str
host: str = ""
base_path: str = ""
schemes: List[str] = field(default_factory=list)
tags: List[Dict[str, Any]] = field(default_factory=list)
categories: List[Dict[str, Any]] = field(default_factory=list)
class ParsedBusinessLogic(BaseModel):
# Placeholder for parsed business logic flow
name: str
steps: list # List of steps, each could be another Pydantic model
class InputParser:
"""
Responsible for parsing DDMS supplier's input materials like API specs, etc.
"""
def __init__(self):
pass
def parse_openapi_spec(self, spec_path: str) -> Optional[ParsedOpenAPISpec]:
"""
Parses an OpenAPI specification from a file path.
Args:
spec_path: The file path of the OpenAPI specification.
Returns:
A ParsedOpenAPISpec object containing the parsed specification,
or None if parsing fails.
"""
try:
# Check if file exists
if not os.path.exists(spec_path):
print(f"Error: File not found: {spec_path}")
return None
# Read and parse JSON file
with open(spec_path, 'r', encoding='utf-8') as f:
swagger_data = json.load(f)
# Extract basic information
swagger_version = swagger_data.get('swagger', swagger_data.get('openapi', 'Unknown'))
info = swagger_data.get('info', {})
paths = swagger_data.get('paths', {})
tags = swagger_data.get('tags', [])
base_path = swagger_data.get('basePath', '')
# Create and return ParsedOpenAPISpec
return ParsedOpenAPISpec(
spec=swagger_data,
info=info,
paths=paths,
tags=tags,
basePath=base_path,
swagger_version=swagger_version
)
except FileNotFoundError:
print(f"File not found: {spec_path}")
return None
except json.JSONDecodeError as e:
print(f"Error parsing JSON from {spec_path}: {e}")
return None
except Exception as e:
print(f"Error parsing OpenAPI spec from {spec_path}: {e}")
return None
def parse_yapi_spec(self, file_path: str) -> Optional[ParsedYAPISpec]:
"""
解析YAPI规范文件
Args:
file_path: YAPI JSON文件路径
Returns:
Optional[ParsedYAPISpec]: 解析后的YAPI规范如果解析失败则返回None
"""
if not os.path.isfile(file_path):
logger.error(f"文件不存在: {file_path}")
return None
try:
with open(file_path, 'r', encoding='utf-8') as f:
yapi_data = json.load(f)
if not isinstance(yapi_data, list):
logger.error(f"无效的YAPI文件格式: 顶层元素应该是数组")
return None
endpoints = []
categories = []
# 处理分类
for category_data in yapi_data:
if not isinstance(category_data, dict):
logger.warning(f"YAPI 分类条目格式不正确,应为字典类型,已跳过: {category_data}")
continue
category_name = category_data.get('name', '')
category_desc = category_data.get('desc', '')
# 添加到分类列表
categories.append({
'name': category_name,
'desc': category_desc
})
# 处理API接口
api_list = category_data.get('list', [])
if not isinstance(api_list, list):
logger.warning(f"分类 '{category_name}' 中的 API列表 (list) 格式不正确,应为数组类型,已跳过。")
continue
for api_item in api_list:
if not isinstance(api_item, dict):
logger.warning(f"分类 '{category_name}' 中的 API条目格式不正确应为字典类型已跳过: {api_item}")
continue
# 提取API信息
path = api_item.get('path', '')
if not path:
logger.info(f"分类 '{category_name}' 中的 API条目缺少 'path',使用空字符串。 API: {api_item.get('title', '未命名')}")
method = api_item.get('method', 'GET')
if api_item.get('method') is None: # 仅当原始数据中完全没有 method 字段时记录
logger.info(f"分类 '{category_name}' 中的 API条目 '{path}' 缺少 'method',使用默认值 'GET'")
title = api_item.get('title', '')
if not title:
logger.info(f"分类 '{category_name}' 中的 API条目 '{path}' ({method}) 缺少 'title',使用空字符串。")
description = api_item.get('desc', '')
# 提取请求参数
req_params = api_item.get('req_params', [])
req_query = api_item.get('req_query', [])
req_headers = api_item.get('req_headers', [])
# 提取请求体信息
req_body_type = api_item.get('req_body_type', '')
req_body_other = api_item.get('req_body_other', '')
# 提取响应体信息
res_body_type = api_item.get('res_body_type', '')
res_body = api_item.get('res_body', '')
# 创建端点对象
endpoint = YAPIEndpoint(
path=path,
method=method,
title=title,
description=description,
category_name=category_name,
req_params=req_params,
req_query=req_query,
req_headers=req_headers,
req_body_type=req_body_type,
req_body_other=req_body_other,
res_body_type=res_body_type,
res_body=res_body
)
endpoints.append(endpoint)
return ParsedYAPISpec(
endpoints=endpoints,
categories=categories,
total_count=len(endpoints)
)
except Exception as e:
logger.error(f"解析YAPI文件时出错: {str(e)}")
return None
def parse_swagger_spec(self, file_path: str) -> Optional[ParsedSwaggerSpec]:
"""
解析Swagger规范文件
Args:
file_path: Swagger JSON文件路径
Returns:
Optional[ParsedSwaggerSpec]: 解析后的Swagger规范如果解析失败则返回None
"""
if not os.path.isfile(file_path):
logger.error(f"文件不存在: {file_path}")
return None
try:
with open(file_path, 'r', encoding='utf-8') as f:
swagger_data = json.load(f)
if not isinstance(swagger_data, dict):
logger.error(f"无效的Swagger文件格式: 顶层元素应该是对象")
return None
# 提取基本信息
swagger_version = swagger_data.get('swagger', swagger_data.get('openapi', ''))
info = swagger_data.get('info', {})
host = swagger_data.get('host', '')
base_path = swagger_data.get('basePath', '')
schemes = swagger_data.get('schemes', [])
tags = swagger_data.get('tags', [])
# 创建分类列表
categories = []
for tag in tags:
categories.append({
'name': tag.get('name', ''),
'desc': tag.get('description', '')
})
# 处理API路径
paths = swagger_data.get('paths', {})
endpoints = []
for path, path_item in paths.items():
if not isinstance(path_item, dict):
continue
# 处理每个HTTP方法 (GET, POST, PUT, DELETE等)
for method, operation in path_item.items():
if method in ['get', 'post', 'put', 'delete', 'patch', 'options', 'head', 'trace']:
if not isinstance(operation, dict):
continue
# 提取操作信息
summary = operation.get('summary', '')
description = operation.get('description', '')
operation_id = operation.get('operationId', '')
operation_tags = operation.get('tags', [])
# 提取参数信息
parameters = operation.get('parameters', [])
# 提取响应信息
responses = operation.get('responses', {})
# 提取请求和响应的内容类型
consumes = operation.get('consumes', swagger_data.get('consumes', []))
produces = operation.get('produces', swagger_data.get('produces', []))
# 提取请求体信息 (OpenAPI 3.0 格式)
request_body = operation.get('requestBody', {})
# 创建端点对象
endpoint = SwaggerEndpoint(
path=path,
method=method.upper(),
summary=summary,
description=description,
operation_id=operation_id,
tags=operation_tags,
parameters=parameters,
responses=responses,
consumes=consumes,
produces=produces,
request_body=request_body
)
endpoints.append(endpoint)
# 创建返回对象
return ParsedSwaggerSpec(
endpoints=endpoints,
info=info,
swagger_version=swagger_version,
host=host,
base_path=base_path,
schemes=schemes,
tags=tags,
categories=categories
)
except Exception as e:
logger.error(f"解析Swagger文件时出错: {str(e)}")
return None
def parse_business_logic_flow(self, flow_description: str) -> Optional[ParsedBusinessLogic]:
"""
Parses a business logic flow description.
The format of this description is TBD and this parser would need to be built accordingly.
Args:
flow_description: The string content describing the business logic flow.
Returns:
A ParsedBusinessLogic object or None if parsing fails.
"""
print(f"[InputParser] Placeholder: Parsing business logic flow. Content: {flow_description[:100]}...")
# Placeholder: Actual parsing logic will depend on the defined format.
return ParsedBusinessLogic(name="Example Flow", steps=["Step 1 API call", "Step 2 Validate Response"])
# Add other parsers as needed (e.g., for data object definitions)