bella-openapi 1.0.2__py3-none-any.whl → 1.0.2.2__py3-none-any.whl

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.
@@ -1,108 +1,108 @@
1
- import uuid
2
-
3
- import werkzeug
4
- from werkzeug.routing import Map, Rule
5
- from starlette.datastructures import URL
6
- from starlette.middleware.base import BaseHTTPMiddleware
7
- from starlette.responses import JSONResponse
8
-
9
- from bella_openapi import caller_id_context, request_url_context, trace_id_context
10
- from bella_openapi import validate_token
11
- from bella_openapi.exception import AuthorizationException
12
- from urllib.parse import parse_qs
13
-
14
-
15
- class WebSocketHttpContextMiddleware:
16
- def __init__(self, app, *, exclude_url: list[str] = None):
17
- self.app = app
18
- self.exclude_url = exclude_url or []
19
-
20
- async def __call__(self, scope, receive, send):
21
- if scope["type"] != "websocket":
22
- return await self.app(scope, receive, send)
23
-
24
- url = URL(scope=scope)
25
- if match_url(self.exclude_url, url.path):
26
- return await self.app(scope, receive, send)
27
-
28
- query_params = parse_qs(url.query)
29
- if not (query_params.get('token')) or query_params.get('token') == '':
30
- # send token required error
31
- await send({
32
- "type": "websocket.close",
33
- "code": 1006,
34
- "reason": "token required",
35
- })
36
- return
37
- try:
38
- token = query_params.get('token')
39
- caller = validate_token(token[0])
40
- except AuthorizationException:
41
- await send({
42
- "type": "websocket.close",
43
- "code": 1006,
44
- "reason": "token invalid",
45
- })
46
- return
47
- else:
48
- caller_context_token = caller_id_context.set(caller)
49
- trace_id_context_token = trace_id_context.set(str(uuid.uuid4()))
50
- request_url_context_token = request_url_context.set(url.path)
51
- await self.app(scope, receive, send)
52
- caller_id_context.reset(caller_context_token)
53
- trace_id_context.reset(trace_id_context_token)
54
- request_url_context.reset(request_url_context_token)
55
-
56
-
57
- def match_url(patterns, url):
58
- if patterns is None:
59
- return False
60
- # 创建 URL 规则
61
- rules = [Rule(pattern) for pattern in patterns]
62
- # 匹配 URL
63
- adapter = Map(rules).bind('')
64
- try:
65
- adapter.match(url)
66
- return True
67
- except werkzeug.exceptions.NotFound:
68
- return False
69
-
70
-
71
- class HttpContextMiddleware(BaseHTTPMiddleware):
72
- def __init__(self, app, *, exclude_url: list[str] = None, ):
73
- """
74
- :param app
75
- :param exclude_url: 不需要验证token的url,
76
- 根据https://werkzeug.palletsprojects.com/en/2.2.x/routing/ 规则进行配置
77
- """
78
- super().__init__(app)
79
- self.exclude_url = exclude_url
80
-
81
- async def dispatch(self, request, call_next):
82
- if match_url(self.exclude_url, request.url.path):
83
- return await call_next(request)
84
-
85
- if request.url.path.startswith("/v1/actuator/health"):
86
- return await call_next(request)
87
- authorization = request.headers.get("Authorization")
88
- if authorization is None:
89
- return JSONResponse(status_code=401, content={"message": "empty Authorization header"})
90
-
91
- try:
92
- caller = validate_token(authorization)
93
- except AuthorizationException as e:
94
- return JSONResponse(status_code=401, content={"message": e.message})
95
-
96
- caller_context_token = caller_id_context.set(caller)
97
- trace_id_context_token = trace_id_context.set(str(uuid.uuid4()))
98
- request_url_context_token = request_url_context.set(request.url.path)
99
-
100
- # 继续处理请求
101
- response = await call_next(request)
102
-
103
- # 重置contextvars上下文
104
- caller_id_context.reset(caller_context_token)
105
- trace_id_context.reset(trace_id_context_token)
106
- request_url_context.reset(request_url_context_token)
107
-
108
- return response
1
+ import uuid
2
+
3
+ import werkzeug
4
+ from werkzeug.routing import Map, Rule
5
+ from starlette.datastructures import URL
6
+ from starlette.middleware.base import BaseHTTPMiddleware
7
+ from starlette.responses import JSONResponse
8
+
9
+ from bella_openapi import caller_id_context, request_url_context, trace_id_context
10
+ from bella_openapi import validate_token
11
+ from bella_openapi.exception import AuthorizationException
12
+ from urllib.parse import parse_qs
13
+
14
+
15
+ class WebSocketHttpContextMiddleware:
16
+ def __init__(self, app, *, exclude_url: list[str] = None):
17
+ self.app = app
18
+ self.exclude_url = exclude_url or []
19
+
20
+ async def __call__(self, scope, receive, send):
21
+ if scope["type"] != "websocket":
22
+ return await self.app(scope, receive, send)
23
+
24
+ url = URL(scope=scope)
25
+ if match_url(self.exclude_url, url.path):
26
+ return await self.app(scope, receive, send)
27
+
28
+ query_params = parse_qs(url.query)
29
+ if not (query_params.get('token')) or query_params.get('token') == '':
30
+ # send token required error
31
+ await send({
32
+ "type": "websocket.close",
33
+ "code": 1006,
34
+ "reason": "token required",
35
+ })
36
+ return
37
+ try:
38
+ token = query_params.get('token')
39
+ caller = validate_token(token[0])
40
+ except AuthorizationException:
41
+ await send({
42
+ "type": "websocket.close",
43
+ "code": 1006,
44
+ "reason": "token invalid",
45
+ })
46
+ return
47
+ else:
48
+ caller_context_token = caller_id_context.set(caller)
49
+ trace_id_context_token = trace_id_context.set(str(uuid.uuid4()))
50
+ request_url_context_token = request_url_context.set(url.path)
51
+ await self.app(scope, receive, send)
52
+ caller_id_context.reset(caller_context_token)
53
+ trace_id_context.reset(trace_id_context_token)
54
+ request_url_context.reset(request_url_context_token)
55
+
56
+
57
+ def match_url(patterns, url):
58
+ if patterns is None:
59
+ return False
60
+ # 创建 URL 规则
61
+ rules = [Rule(pattern) for pattern in patterns]
62
+ # 匹配 URL
63
+ adapter = Map(rules).bind('')
64
+ try:
65
+ adapter.match(url)
66
+ return True
67
+ except werkzeug.exceptions.NotFound:
68
+ return False
69
+
70
+
71
+ class HttpContextMiddleware(BaseHTTPMiddleware):
72
+ def __init__(self, app, *, exclude_url: list[str] = None, ):
73
+ """
74
+ :param app
75
+ :param exclude_url: 不需要验证token的url,
76
+ 根据https://werkzeug.palletsprojects.com/en/2.2.x/routing/ 规则进行配置
77
+ """
78
+ super().__init__(app)
79
+ self.exclude_url = exclude_url
80
+
81
+ async def dispatch(self, request, call_next):
82
+ if match_url(self.exclude_url, request.url.path):
83
+ return await call_next(request)
84
+
85
+ if request.url.path.startswith("/v1/actuator/health"):
86
+ return await call_next(request)
87
+ authorization = request.headers.get("Authorization")
88
+ if authorization is None:
89
+ return JSONResponse(status_code=401, content={"message": "empty Authorization header"})
90
+
91
+ try:
92
+ caller = validate_token(authorization)
93
+ except AuthorizationException as e:
94
+ return JSONResponse(status_code=401, content={"message": e.message})
95
+
96
+ caller_context_token = caller_id_context.set(caller)
97
+ trace_id_context_token = trace_id_context.set(str(uuid.uuid4()))
98
+ request_url_context_token = request_url_context.set(request.url.path)
99
+
100
+ # 继续处理请求
101
+ response = await call_next(request)
102
+
103
+ # 重置contextvars上下文
104
+ caller_id_context.reset(caller_context_token)
105
+ trace_id_context.reset(trace_id_context_token)
106
+ request_url_context.reset(request_url_context_token)
107
+
108
+ return response
@@ -1,6 +1,6 @@
1
- from contextvars import ContextVar
2
- from typing import Optional
3
-
4
- trace_id_context: ContextVar[Optional[str]] = ContextVar("trace_id", default=None)
5
- caller_id_context: ContextVar[Optional[str]] = ContextVar("caller_id", default=None)
6
- request_url_context: ContextVar[Optional[str]] = ContextVar("request_url", default=None)
1
+ from contextvars import ContextVar
2
+ from typing import Optional
3
+
4
+ trace_id_context: ContextVar[Optional[str]] = ContextVar("trace_id", default=None)
5
+ caller_id_context: ContextVar[Optional[str]] = ContextVar("caller_id", default=None)
6
+ request_url_context: ContextVar[Optional[str]] = ContextVar("request_url", default=None)
bella_openapi/schema.py CHANGED
@@ -1,63 +1,63 @@
1
- from datetime import datetime
2
- from typing import Optional
3
-
4
- import uuid
5
-
6
- import pydantic
7
- from pydantic import BaseModel, Field
8
- from .openapi_contexvar import trace_id_context, caller_id_context, request_url_context
9
-
10
-
11
- class BaseOperationLog(BaseModel):
12
- uuid: str = Field(default_factory=lambda: str(uuid.uuid4()))
13
- request_id: Optional[str] = Field(default_factory=lambda: trace_id_context.get(),
14
- alias='requestId')
15
- caller_id: Optional[str] = Field(default_factory=lambda: caller_id_context.get(),
16
- alias='callerId')
17
- request_url: Optional[str] = Field(default_factory=lambda: request_url_context.get(),
18
- alias='requestUrl')
19
- op_log_type: str = Field(alias='opLogType')
20
- op_type: str = Field(alias='opType')
21
- is_cost_log: bool = Field(default=False, alias='isCostLog')
22
- operation_status: str = Field(alias='operationStatus')
23
- start_time_millis: int = Field(default_factory=lambda: int(datetime.now().timestamp() * 1000),
24
- alias='startTimeMillis')
25
- duration_millis: int = Field(default=0, alias='durationMillis')
26
- request: object
27
- response: object = Field(default=None)
28
- err_msg: Optional[str] = Field(default=None, alias='errMsg')
29
- extra_info: dict = Field(default={}, alias='extraInfo')
30
- ucid: str = Field(default='ucid')
31
-
32
- @staticmethod
33
- def validate(values):
34
- if 'request_id' not in values or values['request_id'] is None:
35
- raise ValueError('request_id is required, please set trace_id_context')
36
- if 'caller_id' not in values or values['caller_id'] is None:
37
- raise ValueError('caller_id is required, please set caller_id_context')
38
- if 'request_url' not in values or values['request_url'] is None:
39
- raise ValueError('request_url is required, please set request_url_context')
40
- return values
41
-
42
-
43
- if pydantic.version.VERSION.startswith('1.'):
44
- from pydantic import root_validator
45
-
46
-
47
- class OperationLog(BaseOperationLog):
48
- @root_validator
49
- def validate_duration_millis(cls, values):
50
- super().validate(values)
51
- return values
52
-
53
-
54
- else:
55
- from pydantic import model_validator
56
-
57
-
58
- class OperationLog(BaseOperationLog):
59
-
60
- @model_validator(mode='after')
61
- def validate_duration_millis(cls, values):
62
- super().validate(values.dict())
63
- return values
1
+ from datetime import datetime
2
+ from typing import Optional
3
+
4
+ import uuid
5
+
6
+ import pydantic
7
+ from pydantic import BaseModel, Field
8
+ from .openapi_contexvar import trace_id_context, caller_id_context, request_url_context
9
+
10
+
11
+ class BaseOperationLog(BaseModel):
12
+ uuid: str = Field(default_factory=lambda: str(uuid.uuid4()))
13
+ request_id: Optional[str] = Field(default_factory=lambda: trace_id_context.get(),
14
+ alias='requestId')
15
+ caller_id: Optional[str] = Field(default_factory=lambda: caller_id_context.get(),
16
+ alias='callerId')
17
+ request_url: Optional[str] = Field(default_factory=lambda: request_url_context.get(),
18
+ alias='requestUrl')
19
+ op_log_type: str = Field(alias='opLogType')
20
+ op_type: str = Field(alias='opType')
21
+ is_cost_log: bool = Field(default=False, alias='isCostLog')
22
+ operation_status: str = Field(alias='operationStatus')
23
+ start_time_millis: int = Field(default_factory=lambda: int(datetime.now().timestamp() * 1000),
24
+ alias='startTimeMillis')
25
+ duration_millis: int = Field(default=0, alias='durationMillis')
26
+ request: object
27
+ response: object = Field(default=None)
28
+ err_msg: Optional[str] = Field(default=None, alias='errMsg')
29
+ extra_info: dict = Field(default={}, alias='extraInfo')
30
+ ucid: str = Field(default='ucid')
31
+
32
+ @staticmethod
33
+ def validate(values):
34
+ if 'request_id' not in values or values['request_id'] is None:
35
+ raise ValueError('request_id is required, please set trace_id_context')
36
+ if 'caller_id' not in values or values['caller_id'] is None:
37
+ raise ValueError('caller_id is required, please set caller_id_context')
38
+ if 'request_url' not in values or values['request_url'] is None:
39
+ raise ValueError('request_url is required, please set request_url_context')
40
+ return values
41
+
42
+
43
+ if pydantic.version.VERSION.startswith('1.'):
44
+ from pydantic import root_validator
45
+
46
+
47
+ class OperationLog(BaseOperationLog):
48
+ @root_validator
49
+ def validate_duration_millis(cls, values):
50
+ super().validate(values)
51
+ return values
52
+
53
+
54
+ else:
55
+ from pydantic import model_validator
56
+
57
+
58
+ class OperationLog(BaseOperationLog):
59
+
60
+ @model_validator(mode='after')
61
+ def validate_duration_millis(cls, values):
62
+ super().validate(values.dict())
63
+ return values