Metadata-Version: 2.4
Name: socar-api
Version: 0.6.0
Summary: 基于 FastAPI + JSON:API 规范的 RESTful 接口框架
Author-email: "so.car" <socar@so.car>
License-Expression: MIT
Requires-Python: >=3.12
Description-Content-Type: text/markdown
Requires-Dist: fastapi>=0.136.3
Requires-Dist: pydantic>=2.13.4
Requires-Dist: jquery-unparam>=2.0.0
Requires-Dist: pyyaml>=6.0.3
Requires-Dist: treelib>=1.8.0
Requires-Dist: bitarray>=3.8.1
Requires-Dist: python-multipart>=0.0.32
Requires-Dist: python-dateutil>=2.9.0.post0
Requires-Dist: typing-extensions>=4.15.0
Provides-Extra: dev
Requires-Dist: pytest>=9.0.3; extra == "dev"
Requires-Dist: pytest-asyncio>=1.4.0; extra == "dev"
Requires-Dist: httpx>=0.28.1; extra == "dev"
Requires-Dist: pytest-cov>=7.1.0; extra == "dev"
Requires-Dist: ruff>=0.15.16; extra == "dev"
Requires-Dist: mypy>=2.1.0; extra == "dev"
Requires-Dist: pre-commit>=4.6.0; extra == "dev"

# api_frame

基于 FastAPI + JSON:API 规范的 RESTful 接口框架。

[![version](https://img.shields.io/badge/version-0.6.0-blue.svg)](https://gitee.com/socar/api_frame)

## 简介

api_frame 提供了从资源声明 → 路由生成 → 请求处理 → 响应序列化的完整链路，让开发者**专注业务逻辑**，无需重复实现接口规范。

框架提供两种资源基类：
- **`BaseResource`** — 完整基类，支持 `connect_data()` session 管理和子资源嵌套
- **`Resource`**（推荐） — 简化基类，内置 JSON:API 序列化管线，可直接访问 `self.db`

```python
from api_frame import Resource, SchemaBase, Field, Relationship

class ArticleModel(SchemaBase):
    title: str = Field(None)
    content: str = Field(None)
    author_id: int = Field(None, isrel=True)

class Article(Resource):
    model = ArticleModel

    class Meta:
        link = '/articles'
        type_ = 'articles'

    class RelResources:
        author = Relationship(
            rel_resource='Author',
            mapping_field='author_id',
            has_api=False,
        )

    async def get_many(self):
        return self.db.query(ArticleModel).all()

    async def post(self):
        body = self.parse_body()
        obj = ArticleModel(**body.model_dump())
        self.db.add(obj)
        self.db.commit()
        return self.model.model_validate(obj)
```

一行 `Article.register_routes(app)` 即生成完整 JSON:API 接口。

## 核心特性

- **JSON:API 规范** — 请求/响应格式、资源关系、稀疏字段、include 复合文档、分页
- **资源路由** — 类声明式路由，自动生成标准 CRUD 端点
- **关系管理** — 一对一/一对多关系，include 深层级联（最多支持 3 层）
- **权限系统** — 基于 scope 的 OAuth2 认证，API + 关系双层权限；支持 `@scope` 声明式配置
- **原子操作** — JSON:API Atomic 扩展支持，批量操作 + 事务回滚
- **文件上传/下载** — 增强的 `UploadFileResource` / `DownloadFileResource` 基类，文件类型验证、关系校验、可替换存储后端
- **响应缓存** — `@cached()` 装饰器一行启用接口缓存，Cache-Aside 模式，可替换后端
- **筛选过滤** — Deep Object 格式，40+ 操作符覆盖字符串/数值/列表/时间/布尔
- **自动模型** — 根据资源自动生成请求/响应模型
- **资源版本** — 内建 API 版本管理
- **自动文档** — 与 FastAPI OpenAPI 集成

## 安装

> **要求**: Python 3.12+

```bash
pip install git+https://gitee.com/socar/api_frame
```

## 快速开始

```python
from fastapi import FastAPI
from api_frame import BaseResource, SchemaBase, Field
from api_frame.util import register_jsonapi_exception_handlers


class BookModel(SchemaBase):
    id: str = Field(None)
    title: str = Field(None)
    author: str = Field(None)


class Book(BaseResource):
    model = BookModel
    methods = {'GET', 'GETS', 'POST', 'PATCH', 'DELETE'}

    class Meta:
        link = '/books'
        type_ = 'books'


app = FastAPI()
register_jsonapi_exception_handlers(app)
Book.register_routes(app)
```

启动后自动生成：

| 方法 | 路径 | 说明 |
|------|------|------|
| `GET` | `/books` | 资源列表 |
| `GET` | `/books/{id}` | 单个资源 |
| `POST` | `/books` | 新增资源 |
| `PATCH` | `/books/{id}` | 更新资源 |
| `DELETE` | `/books/{id}` | 删除资源 |
| `POST` | `/atomic` | 原子操作（`register_routes()` 自动注册） |

## 文档

- [框架概述 & 核心概念](docs/index.md)
- [快速开始指南](docs/guide.md)
- [API 参考](docs/api.md)
- [原子操作设计文档](docs/atomic.md)

## 安装指定版本

```bash
pip install git+https://gitee.com/socar/api_frame.git@v0.6.0
```

## 版本历史

| 版本 | 说明 |
|------|------|
| v0.6.0 | 声明式 scope 配置 (`@scope`)、增强文件上传/下载基类 (`filebase.py`)、统一缓存装饰器 (`@cached`)、SecurityConfig strict 模式。405 tests |
| v0.5.11 | `create_filter_model` 处理 `typing.Union`（`Optional[X]`）；`page[limit]`/`page[offset]` 解析后转为 `int`。330 tests |
| v0.5.10 | `relapi=False` 时不生成 relationship links；`has_self_link` 控制 `data.links.self`；顶层 `links.self` 保留原始 request URL 的全部 query params；分页导航 `first/last/prev/next` 保留 `filter/sort/include` 等非分页参数；`get_host()` 不再硬编码 HTTPS，保留实际请求协议。325 tests |
| v0.5.9 | UploadFileBaseResource `examples` list 格式 + `e.raw_errors→e.errors()`（Pydantic v2 兼容）；include 兄弟节点 `asyncio.gather` 并行；Schema cache double-lookup 优化（13 处）；`list[tuple]→Union` 修复（Pydantic v2 homogeneous list 退化）；`_build_response` / `_jsonapi` 单资源 `links.self` 含 ID（含 BaseResource 路径修复）；NumberFilter/FloatFilter 增加 `aeq`；306 tests |
| v0.5.8 | JSON:API v1.1 合规全闭环（POST Location 头、PATCH body.id 409 校验、422 JSON Pointer `source.pointer`、顶层 `links.self` 含 ID、include 保留连字符、查询参数命名约定、分页 `links.first/last/prev/next` URL）；`lid` 支持（ResourceIdentifier/RelationshipModel/ApiDataModelRequest）；filter NumberFilter/FloatFilter 增加 `aeq` 操作符；query.py dict 类型检测兼容 Python 3.12（`__origin__ is dict`）；`list[tuple(...)]` → Union（Pydantic v2 退化为 homogeneous list 修复）；exception.py 空 parts JSON Pointer 边界保护；`_build_response` 单资源 `links.self` 正确提取 ID；303 tests |
| v0.5.7 | resource.py _jsonapi 新增 `many` 参数，空结果根据 many 返回 `[]` 或 `null`；handlers.py make_related_handler 对 to-one 404 自动转换为 `data: null`（修复 `/models/{id}/measure` 等 related 端点 500）；292 tests |
| v0.5.6 | filter.py: UnionType (`int\|None`) + GenericAlias (`list[str]`) 支持；serializer.py: None to-one 关系产出 null 标识符；query.py: Filters/ArgsModel 的 get_field_value/get_filter/pop_and_filter 递归遍历 FilterOr；移除根目录残留 `__init__.py`；292 tests |
| v0.5.4 | JSON:API v1.1 合规收紧（ErrorModel.status str 强制 + field_validator 自动 int→str；ResourceIdentifier.id/type 必填不可空；ErrorResponse model_dump 默认 exclude_none）；RefRes.id 统一 str；status 校验增加 bool 拒绝 + HTTP 100-599 区间；ResourceIdentifier validator mode=before + whitespace strip 自动规范化；新增边界测试覆盖空白 id、status 区间边界；292 tests |
| v0.5.3 | JSON:API v1.1 合规改进（ErrorModel/ErrorResponse/LinksSelfModel 扩展 v1.1 字段）；related_cond/serializer request 传递 bug 修复；ApiDataModelResponse.id 类型收紧；get_field_value 类型注解修正 + ArgsModel 多值一致性；jsonapi.version → 1.1；LinksRelatedModel 补全 href/meta；OpenAPI summary 自然中文描述 + 资源类型前缀；清除废弃注释代码和构建残留；415/406 Content-Type/Accept 校验中间件 JSONAPIContentNegotiationMiddleware；移除废弃 Pydantic v1 兼容层（Meta.dict → model_dump、__fields__ → model_fields）；RdentifierMeta → IdentifierMeta 正名；过期 warning 升级为 error；StrEnum / PEP 695 语法迁移；examples/ 全面清理；filterwarnings 移除；mypy 严格模式；requires-python ≥3.12；新增 31 合规补集测试（共 289 pass） |
| v0.5.0 | Pydantic v2 原生迁移；resource.py 拆分为 router/serializer/handlers/atomic 四模块；新增简化基类 Resource；Python 3.10+ 语法（`int | str` 替代 `Union`）；FastAPI 0.136；移除 clean.py、build/；OpenAPI summary 优化 |
| v0.4.0 | 原子操作 Atomic 支持、权限三态模型（未配 scope = 放行）、docs 文档 |
| v0.3.30 | 兼容 map 类型的 get_field_value |
| v0.3.29 | 依赖版本更改 |
| v0.3.28 | str 支持，语义化搜索/向量搜索 |
| v0.3.27 | 支持 dict 类型过滤 |
| v0.3.26 | 权限配置缺失时提醒 |
| v0.3.25 | query 验证数组逻辑更改 |
| v0.3.24 | 权限验证绑定到关系的关系资源 |
| v0.3.23 | Filter 类增加 get_field_value 方法 |
| v0.3.22 | 判断接口是否有权限方法更改 |
| v0.3.21 | 验证关系权限前判断关系是否存在 |
| v0.3.20 | 更改 url 参数解析，使用 urllib.parse |
| v0.3.19 | route 命唯一名 |
| v0.3.0 | 调整 fastapi==0.92.0 |
