fiuai-sdk-python 0.3.0__tar.gz → 0.3.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. {fiuai_sdk_python-0.3.0 → fiuai_sdk_python-0.3.2}/PKG-INFO +2 -1
  2. {fiuai_sdk_python-0.3.0 → fiuai_sdk_python-0.3.2}/pyproject.toml +2 -1
  3. {fiuai_sdk_python-0.3.0 → fiuai_sdk_python-0.3.2}/src/fiuai_sdk_python/__init__.py +0 -2
  4. fiuai_sdk_python-0.3.2/src/fiuai_sdk_python/auth/__init__.py +25 -0
  5. fiuai_sdk_python-0.3.2/src/fiuai_sdk_python/auth/header.py +90 -0
  6. fiuai_sdk_python-0.3.2/src/fiuai_sdk_python/auth/helper.py +161 -0
  7. fiuai_sdk_python-0.3.2/src/fiuai_sdk_python/auth/test_auth.py +273 -0
  8. fiuai_sdk_python-0.3.2/src/fiuai_sdk_python/auth/type.py +31 -0
  9. {fiuai_sdk_python-0.3.0 → fiuai_sdk_python-0.3.2}/src/fiuai_sdk_python/bank.py +6 -6
  10. fiuai_sdk_python-0.3.2/src/fiuai_sdk_python/client.py +263 -0
  11. {fiuai_sdk_python-0.3.0 → fiuai_sdk_python-0.3.2}/src/fiuai_sdk_python/company.py +3 -3
  12. {fiuai_sdk_python-0.3.0 → fiuai_sdk_python-0.3.2}/src/fiuai_sdk_python/setup.py +4 -4
  13. fiuai_sdk_python-0.3.2/src/fiuai_sdk_python/test_all.py +73 -0
  14. {fiuai_sdk_python-0.3.0 → fiuai_sdk_python-0.3.2}/src/fiuai_sdk_python/type.py +0 -3
  15. {fiuai_sdk_python-0.3.0 → fiuai_sdk_python-0.3.2}/src/fiuai_sdk_python/util.py +6 -17
  16. fiuai_sdk_python-0.3.0/src/fiuai_sdk_python/client.py +0 -416
  17. fiuai_sdk_python-0.3.0/src/fiuai_sdk_python/token.py +0 -84
  18. {fiuai_sdk_python-0.3.0 → fiuai_sdk_python-0.3.2}/.gitignore +0 -0
  19. {fiuai_sdk_python-0.3.0 → fiuai_sdk_python-0.3.2}/CHANGELOG.md +0 -0
  20. {fiuai_sdk_python-0.3.0 → fiuai_sdk_python-0.3.2}/LICENSE +0 -0
  21. {fiuai_sdk_python-0.3.0 → fiuai_sdk_python-0.3.2}/README.md +0 -0
  22. {fiuai_sdk_python-0.3.0 → fiuai_sdk_python-0.3.2}/src/fiuai_sdk_python/const.py +0 -0
  23. {fiuai_sdk_python-0.3.0 → fiuai_sdk_python-0.3.2}/src/fiuai_sdk_python/error.py +0 -0
  24. {fiuai_sdk_python-0.3.0 → fiuai_sdk_python-0.3.2}/src/fiuai_sdk_python/item.py +0 -0
  25. {fiuai_sdk_python-0.3.0 → fiuai_sdk_python-0.3.2}/src/fiuai_sdk_python/perm.py +0 -0
  26. {fiuai_sdk_python-0.3.0 → fiuai_sdk_python-0.3.2}/src/fiuai_sdk_python/profile.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fiuai_sdk_python
3
- Version: 0.3.0
3
+ Version: 0.3.2
4
4
  Summary: FiuAI Python SDK - 企业级AI服务集成开发工具包
5
5
  Project-URL: Homepage, https://github.com/fiuai/fiuai-sdk-python
6
6
  Project-URL: Documentation, https://github.com/fiuai/fiuai-sdk-python#readme
@@ -22,6 +22,7 @@ Classifier: Programming Language :: Python :: 3.13
22
22
  Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
23
23
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
24
  Requires-Python: >=3.12
25
+ Requires-Dist: fastapi>=0.118.2
25
26
  Requires-Dist: httpx>=0.28.1
26
27
  Requires-Dist: pydantic>=2.11.7
27
28
  Requires-Dist: pytest>=8.4.1
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "fiuai_sdk_python"
3
- version = "0.3.0"
3
+ version = "0.3.2"
4
4
  description = "FiuAI Python SDK - 企业级AI服务集成开发工具包"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -24,6 +24,7 @@ classifiers = [
24
24
  "Topic :: Software Development :: Libraries :: Application Frameworks",
25
25
  ]
26
26
  dependencies = [
27
+ "fastapi>=0.118.2",
27
28
  "httpx>=0.28.1",
28
29
  "pydantic>=2.11.7",
29
30
  "pytest>=8.4.1",
@@ -1,12 +1,10 @@
1
1
  from .client import FiuaiSDK, get_client
2
- from .token import TokenConfig
3
2
  from .util import init_fiuai
4
3
  from .profile import UserProfileInfo
5
4
  from .type import UserProfile
6
5
 
7
6
  __all__ = [
8
7
  'FiuaiSDK',
9
- 'TokenConfig',
10
8
  'init_fiuai',
11
9
  'get_client',
12
10
  'UserProfileInfo',
@@ -0,0 +1,25 @@
1
+ from .header import parse_auth_headers, extract_auth_from_request
2
+ from .type import AuthData, AuthHeader
3
+ from .helper import (
4
+ get_auth_data,
5
+ get_current_user_id,
6
+ get_current_tenant_id,
7
+ get_current_company,
8
+ get_company_unique_no,
9
+ get_impersonation,
10
+ is_impersonating
11
+ )
12
+
13
+ __all__ = [
14
+ "parse_auth_headers",
15
+ "extract_auth_from_request",
16
+ "AuthData",
17
+ "AuthHeader",
18
+ "get_auth_data",
19
+ "get_current_user_id",
20
+ "get_current_tenant_id",
21
+ "get_current_company",
22
+ "get_company_unique_no",
23
+ "get_impersonation",
24
+ "is_impersonating",
25
+ ]
@@ -0,0 +1,90 @@
1
+ # -- coding: utf-8 --
2
+ # Project: auth
3
+ # Created Date: 2025-01-09
4
+ # Author: liming
5
+ # Email: lmlala@aliyun.com
6
+ # Copyright (c) 2025 FiuAI
7
+
8
+ from typing import Dict, Optional, Union, Literal
9
+ from .type import AuthData
10
+ from fastapi import Request
11
+
12
+
13
+ def parse_auth_headers(headers: Dict[str, str]) -> Optional[AuthData]:
14
+ """
15
+ 从 HTTP headers 中解析认证信息并转换为 AuthData
16
+
17
+ Args:
18
+ headers: HTTP headers 字典
19
+
20
+ Returns:
21
+ AuthData 对象,如果解析失败则返回 None
22
+
23
+ Raises:
24
+ ValueError: 当必需的头信息缺失或格式错误时
25
+ """
26
+ try:
27
+ # 获取必需的头信息(注意:HTTP headers 会自动转换为小写)
28
+ user_id = headers.get("x-fiuai-user")
29
+ auth_tenant_id = headers.get("x-fiuai-auth-tenant-id")
30
+ current_company_str = headers.get("x-fiuai-current-company", "")
31
+ impersonation = headers.get("x-fiuai-impersonation", "")
32
+
33
+ # 验证必需字段
34
+ if not user_id:
35
+ raise ValueError("Missing required header: x-fiuai-user")
36
+ if not auth_tenant_id:
37
+ raise ValueError("Missing required header: x-fiuai-auth-tenant-id")
38
+
39
+ # 解析当前公司列表(逗号分隔)
40
+ current_company = []
41
+ if current_company_str:
42
+ current_company = [company.strip() for company in current_company_str.split(",") if company.strip()]
43
+
44
+ # 检查是否有有效的公司信息
45
+ if not current_company:
46
+ raise ValueError("Missing required header: x-fiuai-current-company")
47
+
48
+ return AuthData(
49
+ user_id=user_id,
50
+ auth_tenant_id=auth_tenant_id,
51
+ current_company=current_company[0],
52
+ impersonation=impersonation,
53
+ company_unique_no=current_company[0]
54
+ )
55
+
56
+ except Exception as e:
57
+ # 记录错误但不抛出异常,返回 None 表示解析失败
58
+
59
+ raise
60
+
61
+
62
+ def extract_auth_from_request(
63
+ request: Union[Request, Dict[str, str]],
64
+ engine: Literal["fastapi", "dict"] = "fastapi"
65
+ ) -> Optional[AuthData]:
66
+ """
67
+ 从请求对象中提取认证信息
68
+
69
+ Args:
70
+ request: FastAPI Request 对象或原生 headers 字典
71
+ engine: 请求引擎类型,默认为 "fastapi"
72
+
73
+ Returns:
74
+ AuthData 对象,如果解析失败则返回 None, 或抛出异常
75
+
76
+ Raises:
77
+ TypeError: 当 request 类型与 engine 不匹配时
78
+ """
79
+ if engine == "fastapi":
80
+ if not isinstance(request, Request):
81
+ raise TypeError("request must be a FastAPI Request object when engine='fastapi'")
82
+ headers = dict(request.headers)
83
+ elif engine == "dict":
84
+ if not isinstance(request, dict):
85
+ raise TypeError("request must be a dict when engine='dict'")
86
+ headers = request
87
+ else:
88
+ raise ValueError("engine must be either 'fastapi' or 'dict'")
89
+
90
+ return parse_auth_headers(headers)
@@ -0,0 +1,161 @@
1
+ # -- coding: utf-8 --
2
+ # Project: auth
3
+ # Created Date: 2025-01-09
4
+ # Author: liming
5
+ # Email: lmlala@aliyun.com
6
+ # Copyright (c) 2025 FiuAI
7
+
8
+ from fastapi import Request, HTTPException
9
+ from typing import Optional, Union, Dict, Literal
10
+ from .type import AuthData
11
+
12
+
13
+ def get_auth_data(
14
+ request: Union[Request, Dict[str, str]],
15
+ engine: Literal["fastapi", "dict"] = "fastapi"
16
+ ) -> AuthData:
17
+ """
18
+ 从请求中获取认证数据
19
+
20
+ Args:
21
+ request: FastAPI Request 对象或包含认证数据的字典
22
+ engine: 请求引擎类型,默认为 "fastapi"
23
+
24
+ Returns:
25
+ AuthData: 认证数据
26
+
27
+ Raises:
28
+ HTTPException: 当认证数据不存在时抛出 401 错误(仅限 FastAPI)
29
+ ValueError: 当使用 dict 引擎且认证数据不存在时
30
+ """
31
+ if engine == "fastapi":
32
+ if not isinstance(request, Request):
33
+ raise TypeError("request must be a FastAPI Request object when engine='fastapi'")
34
+ auth_data = getattr(request.state, 'auth_data', None)
35
+ if not auth_data:
36
+ raise HTTPException(
37
+ status_code=401,
38
+ detail={
39
+ "error": "Unauthorized",
40
+ "message": "Authentication data not found",
41
+ "code": "AUTH_NOT_FOUND"
42
+ }
43
+ )
44
+ return auth_data
45
+ elif engine == "dict":
46
+ if not isinstance(request, dict):
47
+ raise TypeError("request must be a dict when engine='dict'")
48
+ auth_data = request.get('auth_data')
49
+ if not auth_data:
50
+ raise ValueError("Authentication data not found in request dict")
51
+ return auth_data
52
+ else:
53
+ raise ValueError("engine must be either 'fastapi' or 'dict'")
54
+
55
+
56
+ def get_current_user_id(
57
+ request: Union[Request, Dict[str, str]],
58
+ engine: Literal["fastapi", "dict"] = "fastapi"
59
+ ) -> str:
60
+ """
61
+ 获取当前用户ID
62
+
63
+ Args:
64
+ request: FastAPI Request 对象或包含认证数据的字典
65
+ engine: 请求引擎类型,默认为 "fastapi"
66
+
67
+ Returns:
68
+ str: 用户ID
69
+ """
70
+ auth_data = get_auth_data(request, engine)
71
+ return auth_data.user_id
72
+
73
+
74
+ def get_current_tenant_id(
75
+ request: Union[Request, Dict[str, str]],
76
+ engine: Literal["fastapi", "dict"] = "fastapi"
77
+ ) -> str:
78
+ """
79
+ 获取当前租户ID
80
+
81
+ Args:
82
+ request: FastAPI Request 对象或包含认证数据的字典
83
+ engine: 请求引擎类型,默认为 "fastapi"
84
+
85
+ Returns:
86
+ str: 租户ID
87
+ """
88
+ auth_data = get_auth_data(request, engine)
89
+ return auth_data.auth_tenant_id
90
+
91
+
92
+ def get_current_company(
93
+ request: Union[Request, Dict[str, str]],
94
+ engine: Literal["fastapi", "dict"] = "fastapi"
95
+ ) -> str:
96
+ """
97
+ 获取当前公司ID
98
+
99
+ Args:
100
+ request: FastAPI Request 对象或包含认证数据的字典
101
+ engine: 请求引擎类型,默认为 "fastapi"
102
+
103
+ Returns:
104
+ str: 公司ID
105
+ """
106
+ auth_data = get_auth_data(request, engine)
107
+ return auth_data.current_company
108
+
109
+
110
+ def get_company_unique_no(
111
+ request: Union[Request, Dict[str, str]],
112
+ engine: Literal["fastapi", "dict"] = "fastapi"
113
+ ) -> str:
114
+ """
115
+ 获取当前公司唯一编号
116
+
117
+ Args:
118
+ request: FastAPI Request 对象或包含认证数据的字典
119
+ engine: 请求引擎类型,默认为 "fastapi"
120
+
121
+ Returns:
122
+ str: 公司唯一编号
123
+ """
124
+ auth_data = get_auth_data(request, engine)
125
+ return auth_data.company_unique_no
126
+
127
+
128
+ def get_impersonation(
129
+ request: Union[Request, Dict[str, str]],
130
+ engine: Literal["fastapi", "dict"] = "fastapi"
131
+ ) -> str:
132
+ """
133
+ 获取当前代表的租户ID
134
+
135
+ Args:
136
+ request: FastAPI Request 对象或包含认证数据的字典
137
+ engine: 请求引擎类型,默认为 "fastapi"
138
+
139
+ Returns:
140
+ str: 代表的租户ID
141
+ """
142
+ auth_data = get_auth_data(request, engine)
143
+ return auth_data.impersonation
144
+
145
+
146
+ def is_impersonating(
147
+ request: Union[Request, Dict[str, str]],
148
+ engine: Literal["fastapi", "dict"] = "fastapi"
149
+ ) -> bool:
150
+ """
151
+ 检查是否正在代表其他租户
152
+
153
+ Args:
154
+ request: FastAPI Request 对象或包含认证数据的字典
155
+ engine: 请求引擎类型,默认为 "fastapi"
156
+
157
+ Returns:
158
+ bool: 是否正在代表其他租户
159
+ """
160
+ auth_data = get_auth_data(request, engine)
161
+ return bool(auth_data.impersonation and auth_data.impersonation != auth_data.auth_tenant_id)
@@ -0,0 +1,273 @@
1
+ # -- coding: utf-8 --
2
+ # Project: auth
3
+ # Created Date: 2025-01-09
4
+ # Author: liming
5
+ # Email: lmlala@aliyun.com
6
+ # Copyright (c) 2025 FiuAI
7
+
8
+ """
9
+ 认证功能测试
10
+ """
11
+
12
+ import sys
13
+ import os
14
+ sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
15
+
16
+ from .header import parse_auth_headers, extract_auth_from_request
17
+ from .helper import (
18
+ get_auth_data,
19
+ get_current_user_id,
20
+ get_current_tenant_id,
21
+ get_current_company,
22
+ get_company_unique_no,
23
+ get_impersonation,
24
+ is_impersonating
25
+ )
26
+ from .type import AuthData
27
+ from unittest.mock import Mock
28
+
29
+
30
+ def test_parse_auth_headers_success():
31
+ """测试成功解析认证头信息"""
32
+ headers = {
33
+ "x-fiuai-user": "user123",
34
+ "x-fiuai-auth-tenant-id": "tenant456",
35
+ "x-fiuai-current-company": "company1,company2",
36
+ "x-fiuai-impersonation": "imp_tenant789"
37
+ }
38
+
39
+ auth_data = parse_auth_headers(headers)
40
+
41
+ assert auth_data is not None
42
+ assert auth_data.user_id == "user123"
43
+ assert auth_data.auth_tenant_id == "tenant456"
44
+ assert auth_data.current_company == "company1"
45
+ assert auth_data.company_unique_no == "company1"
46
+ assert auth_data.impersonation == "imp_tenant789"
47
+
48
+
49
+ def test_parse_auth_headers_missing_required():
50
+ """测试缺少必需头信息的情况"""
51
+ headers = {
52
+ "x-fiuai-user": "user123",
53
+ # 缺少 x-fiuai-auth-tenant-id
54
+ "x-fiuai-current-company": "company1",
55
+ }
56
+
57
+ try:
58
+ auth_data = parse_auth_headers(headers)
59
+ assert False, "应该抛出 ValueError"
60
+ except ValueError as e:
61
+ assert "Missing required header: x-fiuai-auth-tenant-id" in str(e)
62
+
63
+
64
+ def test_parse_auth_headers_empty_company():
65
+ """测试空公司列表的情况"""
66
+ headers = {
67
+ "x-fiuai-user": "user123",
68
+ "x-fiuai-auth-tenant-id": "tenant456",
69
+ "x-fiuai-current-company": "", # 空字符串
70
+ "x-fiuai-impersonation": ""
71
+ }
72
+
73
+ try:
74
+ auth_data = parse_auth_headers(headers)
75
+ assert False, "应该抛出 ValueError"
76
+ except ValueError as e:
77
+ assert "Missing required header: x-fiuai-current-company" in str(e)
78
+
79
+
80
+ def test_parse_auth_headers_multiple_companies():
81
+ """测试多个公司的情况"""
82
+ headers = {
83
+ "x-fiuai-user": "user123",
84
+ "x-fiuai-auth-tenant-id": "tenant456",
85
+ "x-fiuai-current-company": "company1,company2,company3",
86
+ "x-fiuai-impersonation": ""
87
+ }
88
+
89
+ auth_data = parse_auth_headers(headers)
90
+
91
+ assert auth_data is not None
92
+ assert auth_data.current_company == "company1" # 取第一个
93
+ assert auth_data.company_unique_no == "company1"
94
+
95
+
96
+ def test_parse_auth_headers_with_spaces():
97
+ """测试包含空格的公司列表"""
98
+ headers = {
99
+ "x-fiuai-user": "user123",
100
+ "x-fiuai-auth-tenant-id": "tenant456",
101
+ "x-fiuai-current-company": " company1 , company2 , company3 ",
102
+ "x-fiuai-impersonation": ""
103
+ }
104
+
105
+ auth_data = parse_auth_headers(headers)
106
+
107
+ assert auth_data is not None
108
+ assert auth_data.current_company == "company1" # 取第一个,已去除空格
109
+ assert auth_data.company_unique_no == "company1"
110
+
111
+
112
+ def test_extract_auth_from_request_fastapi():
113
+ """测试从 FastAPI Request 对象提取认证信息"""
114
+ # 模拟 FastAPI Request 对象
115
+ from fastapi import Request
116
+ mock_request = Mock(spec=Request)
117
+ mock_request.headers = {
118
+ "x-fiuai-user": "user123",
119
+ "x-fiuai-auth-tenant-id": "tenant456",
120
+ "x-fiuai-current-company": "company1,company2",
121
+ "x-fiuai-impersonation": "imp_tenant789"
122
+ }
123
+
124
+ auth_data = extract_auth_from_request(mock_request, engine="fastapi")
125
+
126
+ assert auth_data is not None
127
+ assert auth_data.user_id == "user123"
128
+ assert auth_data.auth_tenant_id == "tenant456"
129
+ assert auth_data.current_company == "company1"
130
+ assert auth_data.company_unique_no == "company1"
131
+ assert auth_data.impersonation == "imp_tenant789"
132
+
133
+
134
+ def test_extract_auth_from_request_dict():
135
+ """测试从原生字典提取认证信息"""
136
+ headers_dict = {
137
+ "x-fiuai-user": "user123",
138
+ "x-fiuai-auth-tenant-id": "tenant456",
139
+ "x-fiuai-current-company": "company1,company2",
140
+ "x-fiuai-impersonation": "imp_tenant789"
141
+ }
142
+
143
+ auth_data = extract_auth_from_request(headers_dict, engine="dict")
144
+
145
+ assert auth_data is not None
146
+ assert auth_data.user_id == "user123"
147
+ assert auth_data.auth_tenant_id == "tenant456"
148
+ assert auth_data.current_company == "company1"
149
+ assert auth_data.company_unique_no == "company1"
150
+ assert auth_data.impersonation == "imp_tenant789"
151
+
152
+
153
+ def test_extract_auth_from_request_wrong_type():
154
+ """测试类型错误的情况"""
155
+ # 测试 FastAPI 引擎但传入字典
156
+ try:
157
+ extract_auth_from_request({"test": "value"}, engine="fastapi")
158
+ assert False, "应该抛出 TypeError"
159
+ except TypeError as e:
160
+ assert "request must be a FastAPI Request object" in str(e)
161
+
162
+ # 测试 dict 引擎但传入 Request 对象
163
+ from fastapi import Request
164
+ mock_request = Mock(spec=Request)
165
+ try:
166
+ extract_auth_from_request(mock_request, engine="dict")
167
+ assert False, "应该抛出 TypeError"
168
+ except TypeError as e:
169
+ assert "request must be a dict" in str(e)
170
+
171
+
172
+ def test_helper_functions_fastapi():
173
+ """测试 FastAPI 模式下的辅助函数"""
174
+ # 模拟 FastAPI Request 对象,包含认证数据
175
+ from fastapi import Request
176
+ mock_request = Mock(spec=Request)
177
+ auth_data = AuthData(
178
+ user_id="user123",
179
+ auth_tenant_id="tenant456",
180
+ current_company="company1",
181
+ impersonation="imp_tenant789",
182
+ company_unique_no="company1"
183
+ )
184
+ mock_request.state.auth_data = auth_data
185
+
186
+ # 测试各种辅助函数
187
+ assert get_current_user_id(mock_request, engine="fastapi") == "user123"
188
+ assert get_current_tenant_id(mock_request, engine="fastapi") == "tenant456"
189
+ assert get_current_company(mock_request, engine="fastapi") == "company1"
190
+ assert get_company_unique_no(mock_request, engine="fastapi") == "company1"
191
+ assert get_impersonation(mock_request, engine="fastapi") == "imp_tenant789"
192
+ assert is_impersonating(mock_request, engine="fastapi") == True
193
+
194
+
195
+ def test_helper_functions_dict():
196
+ """测试 dict 模式下的辅助函数"""
197
+ # 模拟包含认证数据的字典
198
+ auth_data = AuthData(
199
+ user_id="user123",
200
+ auth_tenant_id="tenant456",
201
+ current_company="company1",
202
+ impersonation="imp_tenant789",
203
+ company_unique_no="company1"
204
+ )
205
+ request_dict = {"auth_data": auth_data}
206
+
207
+ # 测试各种辅助函数
208
+ assert get_current_user_id(request_dict, engine="dict") == "user123"
209
+ assert get_current_tenant_id(request_dict, engine="dict") == "tenant456"
210
+ assert get_current_company(request_dict, engine="dict") == "company1"
211
+ assert get_company_unique_no(request_dict, engine="dict") == "company1"
212
+ assert get_impersonation(request_dict, engine="dict") == "imp_tenant789"
213
+ assert is_impersonating(request_dict, engine="dict") == True
214
+
215
+
216
+ def test_helper_functions_missing_auth_data():
217
+ """测试缺少认证数据的情况"""
218
+ # 测试 FastAPI 模式
219
+ from fastapi import Request
220
+ mock_request = Mock(spec=Request)
221
+ mock_request.state.auth_data = None
222
+
223
+ try:
224
+ get_auth_data(mock_request, engine="fastapi")
225
+ assert False, "应该抛出 HTTPException"
226
+ except Exception as e:
227
+ assert "Authentication data not found" in str(e)
228
+
229
+ # 测试 dict 模式
230
+ request_dict = {}
231
+ try:
232
+ get_auth_data(request_dict, engine="dict")
233
+ assert False, "应该抛出 ValueError"
234
+ except ValueError as e:
235
+ assert "Authentication data not found" in str(e)
236
+
237
+
238
+ def test_is_impersonating_false():
239
+ """测试非代表模式的情况"""
240
+ auth_data = AuthData(
241
+ user_id="user123",
242
+ auth_tenant_id="tenant456",
243
+ current_company="company1",
244
+ impersonation="", # 空字符串
245
+ company_unique_no="company1"
246
+ )
247
+ request_dict = {"auth_data": auth_data}
248
+
249
+ assert is_impersonating(request_dict, engine="dict") == False
250
+
251
+ # 测试 impersonation 等于 auth_tenant_id 的情况
252
+ auth_data.impersonation = "tenant456"
253
+ assert is_impersonating(request_dict, engine="dict") == False
254
+
255
+
256
+ if __name__ == "__main__":
257
+ # 运行原有测试
258
+ test_parse_auth_headers_success()
259
+ test_parse_auth_headers_missing_required()
260
+ test_parse_auth_headers_empty_company()
261
+ test_parse_auth_headers_multiple_companies()
262
+ test_parse_auth_headers_with_spaces()
263
+
264
+ # 运行新增的测试
265
+ test_extract_auth_from_request_fastapi()
266
+ test_extract_auth_from_request_dict()
267
+ test_extract_auth_from_request_wrong_type()
268
+ test_helper_functions_fastapi()
269
+ test_helper_functions_dict()
270
+ test_helper_functions_missing_auth_data()
271
+ test_is_impersonating_false()
272
+
273
+ print("所有测试通过!")
@@ -0,0 +1,31 @@
1
+ # -- coding: utf-8 --
2
+ # Project: auth
3
+ # Created Date: 2025 09 Th
4
+ # Author: liming
5
+ # Email: lmlala@aliyun.com
6
+ # Copyright (c) 2025 FiuAI
7
+
8
+
9
+ from pydantic import BaseModel, Field
10
+ from typing import List
11
+
12
+ class AuthData(BaseModel):
13
+ user_id: str = Field(description="用户ID")
14
+ auth_tenant_id: str = Field(description="租户ID")
15
+ current_company: str = Field(description="当前公司ID列表")
16
+ impersonation: str = Field(description="当前代表的租户ID", default="")
17
+ company_unique_no: str = Field(description="当前公司唯一编号,正常情况等于current_company")
18
+
19
+
20
+
21
+ # edoc
22
+
23
+ class AuthHeader(BaseModel):
24
+ """HTTP 认证头信息模型"""
25
+ x_fiuai_user: str = Field(alias="x-fiuai-user", description="用户ID")
26
+ x_fiuai_auth_tenant_id: str = Field(alias="x-fiuai-auth-tenant-id", description="租户ID")
27
+ x_fiuai_current_company: str = Field(alias="x-fiuai-current-company", default="", description="当前公司ID列表(逗号分隔)")
28
+ x_fiuai_impersonation: str = Field(alias="x-fiuai-impersonation", default="", description="当前代表的租户ID")
29
+
30
+ class Config:
31
+ populate_by_name = True
@@ -46,7 +46,7 @@ def get_bank_account(client: FiuaiSDK, bank_account_no: str) -> Optional[Company
46
46
  logger.error(f"bank_account_no is empty, return None")
47
47
  return None
48
48
 
49
- _bank_account = client.get_doc(
49
+ _bank_account = client.internal_get(
50
50
  doctype="Bank Account",
51
51
  name=bank_account_no,
52
52
  fields=["name","bank_account_no", "account_name", "bank", "opening_bank_branch"]
@@ -58,7 +58,7 @@ def get_bank_account(client: FiuaiSDK, bank_account_no: str) -> Optional[Company
58
58
 
59
59
 
60
60
  # 获取银行信息
61
- _bank_detail = client.get_doc(
61
+ _bank_detail = client.internal_get(
62
62
  doctype="Bank",
63
63
  name=_bank_account["bank"],
64
64
  fields=["name", "bank_name"]
@@ -68,7 +68,7 @@ def get_bank_account(client: FiuaiSDK, bank_account_no: str) -> Optional[Company
68
68
  return CompanyBankAccountSearchResult(exists=False)
69
69
 
70
70
  # 获取支行信息
71
- _bank_branch = client.get_doc(
71
+ _bank_branch = client.internal_get(
72
72
  doctype="Bank Branch",
73
73
  name=_bank_account["opening_bank_branch"],
74
74
  fields=["name", "bank_branch_name"]
@@ -93,7 +93,7 @@ def get_bank_account_by_bank_id(client: FiuaiSDK, bank_id: str) -> BankAccount|N
93
93
  """
94
94
  根据银行账号id获取银行账号信息
95
95
  """
96
- resp = client.get_doc(
96
+ resp = client.internal_get(
97
97
  doctype="Bank Account",
98
98
  filters=[["name", "=", bank_id]],
99
99
  fields=[
@@ -111,7 +111,7 @@ def get_bank_account_by_bank_id(client: FiuaiSDK, bank_id: str) -> BankAccount|N
111
111
  # 获取支行信息
112
112
  bank_branch = ""
113
113
  if resp["opening_bank_branch"] != "":
114
- _bank_branch = client.get_doc(
114
+ _bank_branch = client.internal_get(
115
115
  doctype="Bank Branch",
116
116
  name=resp["opening_bank_branch"],
117
117
  fields=["name", "bank_branch_name"]
@@ -123,7 +123,7 @@ def get_bank_account_by_bank_id(client: FiuaiSDK, bank_id: str) -> BankAccount|N
123
123
  bank_branch = _bank_branch["bank_branch_name"]
124
124
 
125
125
  # 获取银行信息
126
- _bank_detail = client.get_doc(
126
+ _bank_detail = client.internal_get(
127
127
  doctype="Bank",
128
128
  name=resp["bank"],
129
129
  fields=["name", "bank_name"]