diff --git a/apps/routers/service.py b/apps/routers/service.py index 0feae74a6082aa4b6b52b7f102e88c8e768d4217..57ae3892be8f82aab66dc0dcf3bc372ffdbf08d0 100644 --- a/apps/routers/service.py +++ b/apps/routers/service.py @@ -117,7 +117,7 @@ async def get_service_list( # noqa: PLR0913 @router.post("", response_model=UpdateServiceRsp, dependencies=[Depends(verify_csrf_token)]) -async def update_service( +async def update_service( # noqa: PLR0911 user_sub: Annotated[str, Depends(get_user)], data: Annotated[UpdateServiceRequest, Body(..., description="上传 YAML 文本对应数据对象")], ) -> JSONResponse: @@ -125,6 +125,15 @@ async def update_service( if not data.service_id: try: service_id = await ServiceCenterManager.create_service(user_sub, data.data) + except ValueError as e: + return JSONResponse( + status_code=status.HTTP_400_BAD_REQUEST, + content=ResponseData( + code=status.HTTP_400_BAD_REQUEST, + message=str(e), + result={}, + ).model_dump(exclude_none=True, by_alias=True), + ) except Exception as e: LOGGER.error(f"[ServiceCenter] Create service failed: {e}") return JSONResponse( @@ -138,7 +147,16 @@ async def update_service( else: try: service_id = await ServiceCenterManager.update_service(user_sub, data.service_id, data.data) - except ValueError: + except ValueError as e: + if str(e).startswith("Endpoint error"): + return JSONResponse( + status_code=status.HTTP_400_BAD_REQUEST, + content=ResponseData( + code=status.HTTP_400_BAD_REQUEST, + message=str(e), + result={}, + ).model_dump(exclude_none=True, by_alias=True), + ) return JSONResponse( status_code=status.HTTP_400_BAD_REQUEST, content=ResponseData( diff --git a/apps/scheduler/openapi.py b/apps/scheduler/openapi.py index 187bf424242a6fe45952a471edde4e47f23a27f4..af0fe621c1187daa4bf61f033e6628c2a5118e57 100644 --- a/apps/scheduler/openapi.py +++ b/apps/scheduler/openapi.py @@ -2,6 +2,7 @@ Copyright (c) Huawei Technologies Co., Ltd. 2023-2024. All rights reserved. """ + from collections.abc import Sequence from copy import deepcopy from typing import Any, Optional @@ -32,10 +33,7 @@ def _retrieve_ref(path: str, schema: dict) -> dict: """从OpenAPI文档中找到$ref对应的schema""" components = path.split("/") if components[0] != "#": - msg = ( - "ref paths are expected to be URI fragments, meaning they should start " - "with #." - ) + msg = "ref paths are expected to be URI fragments, meaning they should start with #." raise ValueError(msg) out = schema for component in components[1:]: @@ -69,30 +67,25 @@ def _dereference_refs_helper( continue processed_refs.add(v) ref = _retrieve_ref(v, full_schema) - full_ref = _dereference_refs_helper( - ref, full_schema, skip_keys, processed_refs, - ) + full_ref = _dereference_refs_helper(ref, full_schema, skip_keys, processed_refs) processed_refs.remove(v) return full_ref elif isinstance(v, (list, dict)): - obj_out[k] = _dereference_refs_helper( - v, full_schema, skip_keys, processed_refs, - ) + obj_out[k] = _dereference_refs_helper(v, full_schema, skip_keys, processed_refs) else: obj_out[k] = v return obj_out if isinstance(obj, list): - return [ - _dereference_refs_helper(el, full_schema, skip_keys, processed_refs) - for el in obj - ] + return [_dereference_refs_helper(el, full_schema, skip_keys, processed_refs) for el in obj] return obj def _infer_skip_keys( - obj: Any, full_schema: dict, processed_refs: Optional[set[str]] = None, # noqa: ANN401 + obj: Any, + full_schema: dict, + processed_refs: Optional[set[str]] = None, ) -> list[str]: """推断需要跳过的OpenAPI文档中的键""" if processed_refs is None: @@ -133,11 +126,7 @@ def reduce_endpoint_docs(docs: dict) -> dict: if docs.get("description"): out["description"] = docs.get("description") if docs.get("parameters"): - out["parameters"] = [ - parameter - for parameter in docs.get("parameters", []) - if parameter.get("required") - ] + out["parameters"] = [parameter for parameter in docs.get("parameters", []) if parameter.get("required")] if "200" in docs["responses"]: out["responses"] = docs["responses"]["200"] if docs.get("requestBody"): @@ -148,19 +137,31 @@ def reduce_endpoint_docs(docs: dict) -> dict: def reduce_openapi_spec(spec: dict) -> ReducedOpenAPISpec: """解析和处理OpenAPI文档""" # 只支持get, post, patch, put, delete API;强制去除ref;提取关键字段 - endpoints = [ - ReducedOpenAPIEndpoint( - id=docs.get("operationId", None), - uri=route, - method=operation_name, - name=docs.get("summary"), - description=docs.get("description"), - spec=reduce_endpoint_docs(dereference_refs(docs, full_schema=spec)), - ) - for route, operation in spec["paths"].items() - for operation_name, docs in operation.items() - if operation_name in ["get", "post", "patch", "put", "delete"] and (not hasattr(docs, "deprecated") or not docs.deprecated) - ] + endpoints = [] + for route, operation in spec["paths"].items(): + for operation_name, docs in operation.items(): + if operation_name in ["get", "post", "patch", "put", "delete"] and ( + not hasattr(docs, "deprecated") or not docs.deprecated + ): + name = docs.get("summary") + description = docs.get("description") + missing_fields = [] + if not name: + missing_fields.append("summary") + if not description: + missing_fields.append("description") + if missing_fields: + msg = f'Endpoint error at "{operation_name.upper()} {route}": missing {", ".join(missing_fields)}.' + raise ValueError(msg) + endpoint = ReducedOpenAPIEndpoint( + id=docs.get("operationId", None), + uri=route, + method=operation_name, + name=name, + description=description, + spec=reduce_endpoint_docs(dereference_refs(docs, full_schema=spec)), + ) + endpoints.append(endpoint) return ReducedOpenAPISpec( id=spec["info"]["title"],