#json-api #system #user #relationship #model #entity #fields

rabbithole-derive

帮助用户建模数据关系的宏系统

5个不稳定版本

0.3.1 2019年11月17日
0.2.2 2019年11月10日
0.2.1 2019年11月10日
0.2.0 2019年11月10日
0.1.0 2019年11月2日

#9 in #relationship

Download history 17/week @ 2024-03-29 4/week @ 2024-04-05 2/week @ 2024-05-10 1/week @ 2024-05-17 7/week @ 2024-05-24 2/week @ 2024-05-31 55/week @ 2024-07-05 7/week @ 2024-07-12

每月下载量 62
用于 2 crates

MIT 协议

17KB
303

rabbithole-rs 构建状态 crates.io

兔子洞笔直地延伸,像隧道一样,然后突然下沉,如此突然,爱丽丝甚至没有时间思考如何停止自己,就发现自己掉进了一个看起来非常深的井。

-- 《爱丽丝梦游仙境》,路易斯·卡罗尔著

简介

rabbithole-rs 是一个几乎完全类型化的、用户友好的 JSON:API 类型系统,拥有易于使用的宏系统,可以帮助您建模数据。

灵感主要来源于 jsonapi-rust,实际上,所有测试中的示例数据都来自这个包。干得好,michiel!

那么,什么是 JSON:API 呢?

如果您曾与您的团队争论JSON响应的格式,JSON:API 可以成为您的反自行车棚工具。

通过遵循共享约定,您可以提高生产力,利用通用工具,并专注于您真正关心的事情:您的应用程序。

在设计RESTful API时,最令人烦恼的问题是 如何设计数据结构,尤其是如何 设计错误系统。所以,JSON:API为像您这样的人设计了规范,以指定一些规则来帮助您处理设计问题并解放您的时间!

也许规范很长很无聊,就像读教科书一样,但请相信我,您将从中学到很多东西,就像教科书一样。 :)

那么,为什么还需要另一个JSON:API包呢?

需要支持RSQL

这个包的主要原因之一是我需要支持RSQL/FIQL,因为我认为它是一个针对复杂查询的很好定义的查询系统。JSON:API没有提供官方的查询/过滤解决方案,但我认为RSQL/FIQL足够好,可以处理我的项目。

有关RSQL/FIQL的更多信息,请参阅

良好类型化系统

作为一名Scala程序员,我相信良好的类型系统可以避免很多问题。我想知道Rust的ADT系统是否可以处理这种复杂性的问题。实际上,它处理得很好。

用户友好的宏和建模

作为一名Java开发者,我非常喜欢注解系统。幸运的是,Rust使用proc_macro系统为用户提供“完全相同”的体验。

例如,而不是

#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Dog {
    id: String,
    name: String,
    age: i32,
    main_flea: Flea,
    fleas: Vec<Flea>,
}
jsonapi_model!(Dog; "dog"; has one main_flea; has many fleas);

我可以这样建模我的数据结构

#[derive(rbh_derive::EntityDecorator, Serialize, Deserialize, Clone)]
#[entity(type = "dogs")]
pub struct Dog<'a> {
    #[entity(id)]
    pub id: String,
    pub name: String,
    #[entity(to_many)]
    pub fleas: Vec<Flea>,
    #[entity(to_many)]
    pub friends: Vec<Dog<'a>>,
    #[entity(to_one)]
    #[serde(bound(deserialize = "Box<Human<'a>>: Deserialize<'de>"))]
    pub master: Box<Human<'a>>,
    #[entity(to_one)]
    pub best_one: Option<Box<Dog<'a>>>,
}

对我来说,第二种方式更美观。

特性

一些问题

带有关系路径的查询不起作用

请查看详细问题。

在规范中,当使用page查询时

  • meta对象应添加一个totalPage字段
  • links对象应添加prevnextfirstlast链接,但rabbithole无法自动处理,用户应通过在Fetching::vec_to_document中手动实现来添加这些字段。

未来工作

宏系统中的类型检查和错误提示

rabbithole-rs中有很多类型限制。例如

  • [#to_one]装饰器只能用于类型为

    • 一个rabbithole::entity::Entity
    • rabbithole::entity::Entity的包装类,其中包装类是以下之一
      • Option<T>
      • Box<T>
      • &T
      • 另一个包装类内部,如Option<Box<T>>
  • #[to_many]装饰器只能用于具有(所有)的field

    • 一个迭代器
    • 迭代器的内部类型应满足上述限制
    • 没有嵌套列表(讨论中)

现在由于Rust缺少反射,宏现在不能检查类型错误,因此可能需要一些解决方案。

高性能服务器

由于JSON:API的API接口复杂,我认为自己编写所有遵循规范的API接口是冗余且无聊的工作,所以我会为你做这些无聊的事情!

我对这个项目的最终目标

项目的最终目标就像crnkelide一样,可以基于仅仅模型定义自动生成大量的API。在这里,我想展示项目最终会是什么样子。

定义模型

第一步是定义一些API友好型模型。

// This is the derive crate which you can use to generate JSON:API specific traits
extern crate rabbithole_derive as rbh_derive;

#[derive(rbh_derive::EntityDecorator, Serialize, Deserialize, Clone)]
#[entity(type = "people")]
pub struct Human {
    #[entity(id)]
    pub id_code: Uuid,
    pub name: String,
    #[entity(to_many)]
    pub dogs: Vec<Dog>,
}

#[derive(rbh_derive::EntityDecorator, Serialize, Deserialize, Clone)]
#[entity(type = "dogs")]
pub struct Dog {
    #[entity(id)]
    pub id: Uuid,
    pub name: String,
}

编写自己的DAO

rabbithole不与任何特定数据库绑定,这意味着您必须编写自己的DAO。

有关更多详细信息,请参阅rabbithole-endpoint-actix/examples/mock_gen.rs

Fetching特质是什么

Fetching特质是JSON:API中的“获取数据”部分的映射,它定义了几个操作

  • 获取资源
    • 单个资源:GET /articles/1
    • 多个资源:GET /articles
    • 相关资源:GET /articles/1/author,可以是单个或多个
  • 获取关系
    • 关系:GET /articles/1/relationships/comments
  • 带有查询参数的获取
    • 相关资源的包含(include部分)
    • 稀疏字段集(fields[TYPE]部分)
    • 排序(sort部分)
    • 分页(page部分)

这些就是在fetching data部分我们需要了解的。所以这些操作被抽象成Fenching特质。

vec_to_document部分是什么?

如果您想将Vec<SingleEntity>转换为Document,它将执行许多操作,例如排除未包含的资源保留稀疏字段等,当然,我可以在后台帮助您(使用Entity::to_document_automatically)。但是,除了从数据库提取所有字段并在之后丢弃它们之外,为什么不直接将它们留在数据库中呢?所以,这就是重点。如果您不想编写将Vec<SingleEntity>转换为Document的代码,请使用Entity::to_document_automatically,或者,您可以直接从数据库组装Document。

...(其他任何)部分是什么?

  • fetch_collection将被映射为:/<ty>?<query>
  • fetch_single将被映射为:/<ty>/<id>?<query>
  • fetch_relationship将被映射为:/<ty>/<id>/relationships/<related_field>?<query>
  • fetch_related 将被映射为:/<ty>/<id>/<related_field>?<query>
  • type Error 如果可能,将被映射为错误响应
  • type Item 必须是 SingleEntity

依赖

~295–750KB
~17K SLoC