dwyeapi 0.9.4__tar.gz → 0.9.5__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 (52) hide show
  1. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/PKG-INFO +1 -1
  2. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/pyproject.toml +1 -1
  3. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/__init__.py +1 -1
  4. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/providers/email/base.py +8 -2
  5. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/providers/email/factory.py +3 -0
  6. dwyeapi-0.9.5/src/dwyeapi/tasks/schema.py +42 -0
  7. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi.egg-info/PKG-INFO +1 -1
  8. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/tests/test_tasks.py +24 -0
  9. dwyeapi-0.9.4/src/dwyeapi/tasks/schema.py +0 -31
  10. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/README.md +0 -0
  11. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/setup.cfg +0 -0
  12. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/cache.py +0 -0
  13. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/config.py +0 -0
  14. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/database.py +0 -0
  15. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/dependencies.py +0 -0
  16. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/dt.py +0 -0
  17. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/exceptions.py +0 -0
  18. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/health.py +0 -0
  19. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/logger.py +0 -0
  20. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/masking.py +0 -0
  21. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/pagination.py +0 -0
  22. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/providers/__init__.py +0 -0
  23. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/providers/email/__init__.py +0 -0
  24. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/providers/email/config.py +0 -0
  25. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/providers/email/protocol.py +0 -0
  26. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/providers/email/resend.py +0 -0
  27. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/response.py +0 -0
  28. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/security.py +0 -0
  29. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/tasks/__init__.py +0 -0
  30. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/tasks/context.py +0 -0
  31. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/tasks/model.py +0 -0
  32. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/tasks/pool.py +0 -0
  33. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/tasks/registry.py +0 -0
  34. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/tasks/router.py +0 -0
  35. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/tasks/service.py +0 -0
  36. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi/tasks/worker.py +0 -0
  37. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi.egg-info/SOURCES.txt +0 -0
  38. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi.egg-info/dependency_links.txt +0 -0
  39. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi.egg-info/requires.txt +0 -0
  40. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/src/dwyeapi.egg-info/top_level.txt +0 -0
  41. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/tests/test_cache.py +0 -0
  42. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/tests/test_config.py +0 -0
  43. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/tests/test_database.py +0 -0
  44. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/tests/test_dependencies.py +0 -0
  45. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/tests/test_dt.py +0 -0
  46. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/tests/test_exceptions.py +0 -0
  47. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/tests/test_health.py +0 -0
  48. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/tests/test_logger.py +0 -0
  49. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/tests/test_masking.py +0 -0
  50. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/tests/test_pagination.py +0 -0
  51. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/tests/test_response.py +0 -0
  52. {dwyeapi-0.9.4 → dwyeapi-0.9.5}/tests/test_security.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dwyeapi
3
- Version: 0.9.4
3
+ Version: 0.9.5
4
4
  Summary: Lightweight FastAPI infrastructure — exceptions, database, config, security, cache, pagination, logger
5
5
  License: MIT
6
6
  Project-URL: Repository, https://github.com/danweiyuancircle/dwy-ai-experience
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "dwyeapi"
3
- version = "0.9.4"
3
+ version = "0.9.5"
4
4
  description = "Lightweight FastAPI infrastructure — exceptions, database, config, security, cache, pagination, logger"
5
5
  requires-python = ">=3.11"
6
6
  license = {text = "MIT"}
@@ -32,4 +32,4 @@ __all__ = [
32
32
  "is_prod",
33
33
  "logger",
34
34
  ]
35
- __version__ = "0.9.4"
35
+ __version__ = "0.9.5"
@@ -63,11 +63,17 @@ class EmailProviderBase(ABC):
63
63
  return "".join(secrets.choice("0123456789") for _ in range(self._length))
64
64
 
65
65
  async def send_code(self, target: str) -> bool:
66
- """生成验证码存 Redis 并调用 `_send()` 发送。"""
66
+ """生成验证码:先发送成功再写入 Redis。
67
+
68
+ 顺序约束:``_send`` 失败时不落库,避免用户未收到邮件却有可校验码。
69
+ 发送成功后写 Redis;若写库失败则返回 False(邮件已发出属极端边界,业务可重发覆盖)。
70
+ """
67
71
  code = self._generate_code()
72
+ if not await self._send(target, code):
73
+ return False
68
74
  redis = await self._get_redis()
69
75
  await redis.set(f"{CODE_KEY_PREFIX}{target}", code, ex=self._ttl)
70
- return await self._send(target, code)
76
+ return True
71
77
 
72
78
  async def verify_code(self, target: str, code: str) -> bool:
73
79
  """从 Redis 读取存储的验证码比对,成功则删除 key(一次性)。"""
@@ -64,6 +64,9 @@ def make_email_provider(settings: EmailSettings) -> EmailProvider:
64
64
 
65
65
  if not settings.resend.api_key:
66
66
  raise ValueError("EMAIL__RESEND__API_KEY 未配置")
67
+ # from_email 启用时必填:缺省会导致 resend 运行时发信失败,尽早在工厂拦截
68
+ if not settings.resend.from_email:
69
+ raise ValueError("EMAIL__RESEND__FROM_EMAIL 未配置")
67
70
  return ResendEmailProvider(
68
71
  api_key=settings.resend.api_key,
69
72
  from_email=settings.resend.from_email,
@@ -0,0 +1,42 @@
1
+ """任务 API 请求/响应的 Pydantic schema。"""
2
+
3
+ from datetime import datetime
4
+ from typing import Any
5
+
6
+ from pydantic import BaseModel, ConfigDict, Field
7
+
8
+ from dwyeapi.tasks.model import TaskStatus
9
+
10
+ # 与 Task ORM 列 String(50) 对齐;params 键数/体积限制防超大 body
11
+ _TASK_TYPE_MAX_LEN = 50
12
+ _PARAMS_MAX_KEYS = 50
13
+
14
+
15
+ class TaskCreate(BaseModel):
16
+ """提交新任务的请求体。
17
+
18
+ 约束与 ORM / 输入限长规则对齐:``task_type`` 最长 50;``params`` 最多 50 个键。
19
+ """
20
+
21
+ task_type: str = Field(min_length=1, max_length=_TASK_TYPE_MAX_LEN, description="已注册的任务类型名")
22
+ params: dict[str, Any] = Field(
23
+ default_factory=dict,
24
+ description="任务参数;键数量上限 50,避免超大 JSON 入库",
25
+ max_length=_PARAMS_MAX_KEYS,
26
+ )
27
+
28
+
29
+ class TaskResponse(BaseModel):
30
+ """任务详情响应体,覆盖状态、进度、日志与结果等字段。"""
31
+
32
+ id: str
33
+ task_type: str
34
+ status: TaskStatus
35
+ params: dict[str, Any]
36
+ result: dict[str, Any] | None
37
+ progress: int
38
+ logs: str
39
+ created_at: datetime
40
+ updated_at: datetime
41
+
42
+ model_config = ConfigDict(from_attributes=True)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dwyeapi
3
- Version: 0.9.4
3
+ Version: 0.9.5
4
4
  Summary: Lightweight FastAPI infrastructure — exceptions, database, config, security, cache, pagination, logger
5
5
  License: MIT
6
6
  Project-URL: Repository, https://github.com/danweiyuancircle/dwy-ai-experience
@@ -127,6 +127,30 @@ class TestSchemas:
127
127
  assert tc.task_type == "export"
128
128
  assert tc.params == {"format": "csv"}
129
129
 
130
+ def test_task_create_rejects_empty_task_type(self):
131
+ """task_type 最短 1 字符。"""
132
+ import pytest
133
+ from pydantic import ValidationError
134
+
135
+ with pytest.raises(ValidationError):
136
+ TaskCreate(task_type="", params={})
137
+
138
+ def test_task_create_rejects_long_task_type(self):
139
+ """task_type 最长 50,对齐 ORM String(50)。"""
140
+ import pytest
141
+ from pydantic import ValidationError
142
+
143
+ with pytest.raises(ValidationError):
144
+ TaskCreate(task_type="x" * 51, params={})
145
+
146
+ def test_task_create_rejects_too_many_params_keys(self):
147
+ """params 键数上限 50。"""
148
+ import pytest
149
+ from pydantic import ValidationError
150
+
151
+ with pytest.raises(ValidationError):
152
+ TaskCreate(task_type="export", params={f"k{i}": i for i in range(51)})
153
+
130
154
  async def test_task_response_from_attributes(self, session):
131
155
  """TaskResponse should be constructible from a persisted Task ORM object."""
132
156
  task = Task(
@@ -1,31 +0,0 @@
1
- """任务 API 请求/响应的 Pydantic schema。"""
2
-
3
- from datetime import datetime
4
- from typing import Any
5
-
6
- from pydantic import BaseModel, ConfigDict
7
-
8
- from dwyeapi.tasks.model import TaskStatus
9
-
10
-
11
- class TaskCreate(BaseModel):
12
- """提交新任务的请求体。"""
13
-
14
- task_type: str
15
- params: dict[str, Any]
16
-
17
-
18
- class TaskResponse(BaseModel):
19
- """任务详情响应体,覆盖状态、进度、日志与结果等字段。"""
20
-
21
- id: str
22
- task_type: str
23
- status: TaskStatus
24
- params: dict[str, Any]
25
- result: dict[str, Any] | None
26
- progress: int
27
- logs: str
28
- created_at: datetime
29
- updated_at: datetime
30
-
31
- model_config = ConfigDict(from_attributes=True)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes