17 个版本

0.2.5 2024 年 7 月 22 日
0.2.3 2024 年 3 月 21 日
0.1.10 2023 年 12 月 27 日
0.1.9 2022 年 10 月 21 日
0.1.1 2022 年 2 月 26 日

网页编程 中排名第 181

Download history 9/week @ 2024-05-26 114/week @ 2024-06-09 17/week @ 2024-06-16 1/week @ 2024-06-23 146/week @ 2024-07-21 16/week @ 2024-07-28

每月下载量 162

MIT/Apache

265KB
5.5K SLoC

redmine-api

Redmine 问题跟踪器的 API

此 crate 的状态

目前它只支持同步请求,但如果需要,异步支持应该不需要太多努力就能实现。

大多数 API 端点都得到了支持,并且每个支持的 API 端点都至少通过一个单元测试进行了测试。不过,在这个早期阶段,我并不保证与可能需要略微修改以适应我遗漏的 API 端点变体(例如,我未知的不同过滤器)相关的参数的稳定性。(例如,Redmine wiki 上的文档远非完整)。

尽管如此,在更新此 crate 时更改客户端时,此类更改不应花费太多精力,现有的参数值很可能只需包装在一个新的 Enum 中。

如何使用此 crate

本 README 文件中的所有示例都假设在环境或 .env 文件中设置了 REDMINE_API_KEY 和 REDMINE_URL(如果您不使用 .env 文件,则可以跳过 dotenv 行)。

调用 API 端点主要有四种方式

  • 忽略响应体
  • 返回 JSON 响应体
  • 返回支持分页的查询的单页
  • 返回支持分页的查询的所有页

端点 trait

每个 API 端点都由实现 Endpoint trait 的对象表示。

它们每个都有一个构建器来设置任何潜在的参数。为了统一,即使没有参数也使用此模式。

大多数端点都位于与 Redmine wiki 上文档的 API 端点对应的 api 模块中。

例外是 FileUpload 端点及其相关测试,它们位于 api::uploads 中。

端点是我们在下面的调用中未明确指定的泛型参数。

包装类型

许多 API 响应返回实际响应包装在或需要请求包装在额外的 JSON 对象中,该对象有一个以实体单数或复数命名的键。我已提供泛型包装类型,例如 IssueWrapper,以允许此 crate 的用户尽快丢弃它们。

我决定不对非分页查询完全隐藏它们在API后面,因为这可能会给用户带来更少的灵活性。

使用自己的返回类型

除了提供的返回类型,可以使用自己的类型,甚至像 serde_json::Value 这样的东西。这允许您将所需字段直接提取到自己的类型中,例如,只需Id。

基本类型

在API响应中,我们经常以最小形式找到其他实体的列表,例如,只是id和名称。我已将这些类型命名为主要实体后跟Essentials,例如 IssueEssentials

忽略响应体

这在实际中主要用于没有响应体(例如删除查询)或具有副作用(如创建问题)的查询,我们不在乎响应体。

为了说明目的,我这里使用的是 ListIssues 端点,主要是为了避免在生产Redmine实例上运行从此处复制的删除示例时意外丢失数据。

use redmine_api::api::Redmine;
use redmine_api::api::issues::{Issue, IssuesWrapper, ListIssues};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    dotenv::dotenv()?;
    let redmine = Redmine::from_env()?;
    let endpoint = ListIssues::builder().build()?;
    redmine.ignore_response_body::<_>(&endpoint)?;
    Ok(())
}

返回JSON响应

这用于大多数API端点,它要求端点实现 ReturnsJsonResponse 特性,因此不能意外地用于始终返回空响应体的端点。

如果它在支持分页的端点上使用,它将仅返回第一页。这是Redmine的行为,不是由这个crate实现的。

use redmine_api::api::Redmine;
use redmine_api::api::issues::{Issue, IssuesWrapper, ListIssues};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    dotenv::dotenv()?;
    let redmine = Redmine::from_env()?;
    let endpoint = ListIssues::builder().build()?;
    let IssuesWrapper { issues } =
        redmine.json_response_body::<_, IssuesWrapper<Issue>>(&endpoint)?;
    for issue in issues {
        println!("Issue found:\n{:#?}", issue);
    }
    Ok(())
}

分页(单页)

这用于少数几个可以返回大量结果的API端点。

它要求端点实现 Pageable 特性,因此不能意外地用于不支持分页的端点。

由于分页查询始终有一个包含分页键(total_count、offset、limit)的外部包装对象,这些键必须解析,因此不需要在分页查询中显式使用包装对象。

但是,调用确实需要偏移量和限制参数,并且响应被包装在 ResponsePage 结构中以返回这些值。

use redmine_api::api::{Redmine,ResponsePage};
use redmine_api::api::issues::{Issue, ListIssues};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    dotenv::dotenv()?;
    let redmine = Redmine::from_env()?;
    let endpoint = ListIssues::builder().build()?;
    let ResponsePage { values: issues, total_count, offset, limit} =
        redmine.json_response_body_page::<_, Issue>(&endpoint, 3, 25)?;
    println!("Total count: {}", total_count);
    println!("Offset: {}", offset);
    println!("Limit: {}", limit);
    for issue in issues {
        println!("Issue found:\n{:#?}", issue);
    }
    Ok(())
}

分页(所有页面)

上一节中所述的大部分内容也适用于此处。

由于我们请求所有页面,因此不需要偏移量或限制参数,结果也不会包装在额外的对象中。

use redmine_api::api::Redmine;
use redmine_api::api::issues::{Issue, ListIssues};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    dotenv::dotenv()?;
    let redmine = Redmine::from_env()?;
    let endpoint = ListIssues::builder().build()?;
    let issues =
        redmine.json_response_body_all_pages::<_, Issue>(&endpoint)?;
    for issue in issues {
        println!("Issue found:\n{:#?}", issue);
    }
    Ok(())
}

依赖关系

~6–17MB
~248K SLoC