ForcePilot/backend/package/yuxi/external_systems/integrations/servicenow/metadata.py

111 lines
4.2 KiB
Python
Raw Normal View History

"""ServiceNow 厂商集成元数据。
定义 ``IntegrationMetadata(key="servicenow", ...)`` ``__init__.py`` 自注册到
``IntegrationRegistry``元数据为内存态不持久化到数据库
同时定义 ``ServiceNowConnectionConfig`` 连接配置 Schema
``resolve_oauth_endpoints`` OAuth 端点拼接函数 use_cases / 前端在系统创建
与配置阶段调用见设计文档 §3.2 / §3.3
"""
from __future__ import annotations
from pydantic import BaseModel, ConfigDict, Field, model_validator
from yuxi.external_systems.integrations.schemas import IntegrationMetadata
class ServiceNowConnectionConfig(BaseModel):
"""ServiceNow 连接配置 Schema。
``base_url`` 优先级高于 ``instance``支持私有部署场景OAuth 端点由
``resolve_oauth_endpoints`` handler 外动态拼接注入到 ``auth_config``
后传给框架级 OAuth2 插件
"""
model_config = ConfigDict(populate_by_name=True, extra="ignore")
instance: str | None = Field(
default=None,
description="ServiceNow 实例名,如 acme对应 https://acme.service-now.com",
)
base_url: str | None = Field(
default=None,
description="完整 base_url优先级高于 instance用于私有部署或自定义域名",
)
api_version: str | None = Field(
default=None,
description="Table API 版本,如 v1 / v2None 表示非版本化路径 /api/now/table/...",
)
@model_validator(mode="after")
def _ensure_url(self) -> ServiceNowConnectionConfig:
if not self.base_url and not self.instance:
raise ValueError("instance 与 base_url 至少填写一项")
if not self.base_url:
self.base_url = f"https://{self.instance}.service-now.com"
return self
def resolve_oauth_endpoints(base_url: str) -> dict[str, str]:
"""根据 base_url 拼接 ServiceNow 实例级 OAuth 端点。
use_cases / 前端在系统创建与配置阶段调用根据
``connection_config.base_url`` 拼接 ``token_url`` / ``authorize_url``
写入 ``auth_config`` 后持久化handler 执行时 token 已由 use_cases 层通过
``token_manager.get_token`` 预解析并注入到 ``system_config["_resolved_token"]``
handler 不再调用本函数 dynamics365 一致
"""
return {
"authorize_url": f"{base_url}/oauth_auth.do",
"token_url": f"{base_url}/oauth_token.do",
}
SERVICENOW_METADATA = IntegrationMetadata(
key="servicenow",
display_name="ServiceNow",
description="ITSM / 工作流平台,基于 Table REST API 集成",
adapter_type="http",
source_types=["servicenow"],
supported_auth_types=[
"oauth2_authorization_code",
"oauth2_password",
"oauth2_client_credentials",
"basic",
],
tags=["itsm", "workflow", "ticketing"],
icon="servicenow",
docs_url="https://developer.servicenow.com/dev.do#!/learn/learning-plans/yokohama/rest_api",
connection_extra_schema={
"instance": {
"type": "string",
"description": "ServiceNow 实例名,如 acme对应 https://acme.service-now.com",
},
"base_url": {
"type": "string",
"description": "完整 base_url优先级高于 instance用于私有部署或自定义域名",
},
"api_version": {
"type": "string",
"enum": ["v1", "v2"],
"description": "Table API 版本None 表示非版本化路径 /api/now/table/...",
},
},
example_config={
"instance": "acme",
"api_version": None,
"auth_type": "oauth2_client_credentials",
"token_url": "https://acme.service-now.com/oauth_token.do",
"client_id": "example-client-id",
"client_secret": "ref://servicenow/acme/client_secret",
"scope": None,
},
dependency_note=(
"1. 调用账号需对 sys_db_object / sys_dictionary / sys_documentation / sys_choice / sys_glide_object 具备 READ 权限;"
"2. OAuth2 应用需在目标实例中注册并配置回调地址;"
"3. oauth2_client_credentials 需 Washington DC 及以上版本;"
"4. client_secret 建议使用 ref:// 密钥引用。"
),
)