25 releases

0.5.0-a.2 Aug 20, 2024
0.5.0-a.1 Mar 19, 2024
0.4.0 Mar 12, 2024
0.3.4 Feb 16, 2023
0.0.0 Mar 24, 2022

#242 in HTTP client


Used in chrs

MIT license

100KB
2.5K SLoC

ChRIS Client Library for Rust

crates.io version codecov

Client library for ChRIS built on reqwest.

https://docs.rs/chris/latest/chris/


lib.rs:

Client library for ChRIS built on reqwest.

Introduction

ChRIS is a platform for scientific and medical compute. In ChRIS, objects known as ChRIS plugins represent data processing software. A user can run a plugin by creating a plugin instance. Chains of plugin instances are organized as feeds.

Most of the ChRIS API, also known as CUBE, requires a user account. A subset of API endpoints can be accessed anonymously in read-only mode. For example, anyone can list the plugins of a CUBE. However, a user account is required to create plugin instances and browse non-public feeds.

Pro-tip: read the integration test code for code examples.

Authentication

Typically, you start off with a CUBE URL, username, and password. First, obtain a token with Account::get_token, then call ChrisClient::build.

For anonymous access, simply call AnonChrisClient::build.

Access Modes

All client structs are generic over [Access], being either [RwAccess] or [RoAccess]. In many cases, methods are only available to an object when it is of type [RwAccess]. e.g. PluginRw::create_instance is defined for the type Plugin<RwAccess>, however no such method exists for Plugin<RoAccess>.

In situations where either [RwAccess] or [RoAccess] works, implement your function to be generic over [Access]. For example,

use chris::{Access, BaseChrisClient, Plugin};

async fn generic_example<A: Access, C: BaseChrisClient<A>>(chris: &C) -> Plugin<A> {
    todo!()
}

Types which are generic with [RoAccess] typically have a subset of the methods of the same type but generic with [RwAccess]. If [RoAccess] is good enough for you, consider [EitherClient]:

use chris::{BaseChrisClient, EitherClient, PluginRo};

async fn either_client_example(chris: &EitherClient) -> PluginRo {
    chris.plugin().name("pl-dircopy").search().get_only().await.unwrap()
}

It can be convenient to convert objects from T<Access> to T<RoAccess> by calling AuthedChrisClient::into_ro, EitherClient::into_ro, search::Search::into_ro, search::QueryBuilder::into_ro, or LinkedModel::from.

Searching Collections

The ChRIS API is designed around the idea of objects and collections. For example, the plugin collection API is api/v1/plugins/, while specific plugins are retrieved from api/v1/plugins/1/, api/v1/plugins/2/, and so on. Collections usually also have a search API, e.g. api/v1/plugins/search/?name=pl-example.

Many functions return a search::Search. For example, suppose you want a [Plugin]. You need to search for it like this:

use chris::{Access. BaseChrisClient, Plugin};

async fn get_plugin_example<A: Access, C: BaseChrisClient<A>>(chris: &C) -> Plugin<A> {
    chris
        .plugin()
        .name("pl-example")
        .search()
        .get_first()
        .await
        .unwrap()
        .expect("plugin not found")
}

You can either use search::Search::get_first if you want Option<T>, or [search::Search::get_only()] to get just T.

Response Data, Linked v.s. Unlinked Structs

Structs which represent JSON API response data as-is are defined in src/models/data.rs and follow the naming convention *Response, e.g. [PluginResponse]. These response data structs, in combination with an [Access] and a [request::Client, are the generic parameters of [LinkedModel]. Specific methods are defined for generic type combinations. For example, you can get the note of a feed when you have RW access. Hence, the method FeedRw::note is defined for [LinkedModel<FeedResponse, RwAccess>].

In some situations, the API response data contains only a link to the object (e.g. "https://example.org/api/v1/5/" instead of the full object (e.g. {"id": 5, "name": "Example feed", "url": "https://example.org/api/v1/5/", ...}). It would be costly to make an additional HTTP GET to create the full [LinkedModel]. Instead, a [LazyLinkedModel] is returned, which works the same as a [LinkedModel] but is missing the actual object data. The object data can be obtained by calling LazyLinkedModel::get.

Dependencies

~16–31MB
~531K SLoC