dregs 1.0.0

The official Rust client for Dregs, which scores the users of your application for fraud and abuse.
Documentation
//! Dregs: fraud and abuse scoring for the users of your application.
//!
//! Send events from your backend, read back the scores and the observations behind them:
//!
//! ```no_run
//! use dregs::{Client, Event};
//! use serde_json::json;
//!
//! # async fn hold_for_review(_: &str) {}
//! # async fn run() -> dregs::Result<()> {
//! let client = Client::from_env()?;
//!
//! client
//!     .track(
//!         Event::new("user.signup", "user_12345")
//!             .data(json!({ "plan": "pro" }))
//!             .identity_data(json!({ "email": "ada@example.com" })),
//!     )
//!     .await?;
//!
//! let scores = client.identities().scores("user_12345").await?;
//!
//! if scores.authenticity().is_some_and(|authenticity| authenticity < 40) {
//!     hold_for_review("user_12345").await;
//! }
//! # Ok(())
//! # }
//! ```
//!
//! Scoring is asynchronous, so scores appear moments after the events that move them rather than
//! in the same breath. See <https://dregs.com/manual/api/> for the API this wraps.
//!
//! The client is async and runs on [Tokio](https://tokio.rs), through
//! [reqwest](https://docs.rs/reqwest).
//!
//! # Groups on track
//!
//! Pass the organizations, teams, or workspaces the user is acting in, and Dregs makes the
//! identity a member of each. An event carries at most one group of each type:
//!
//! ```no_run
//! # async fn run(client: dregs::Client) -> dregs::Result<()> {
//! use dregs::{Event, Group};
//! use serde_json::json;
//!
//! client
//!     .track(
//!         Event::new("user.login", "user_12345")
//!             .group(Group::organization("org_678").data(json!({ "name": "Acme Inc" })))
//!             .group(Group::new("team", "team_42")),
//!     )
//!     .await?;
//! # Ok(())
//! # }
//! ```
//!
//! # History, links, and past cycles
//!
//! ```no_run
//! # async fn run(client: dregs::Client) -> dregs::Result<()> {
//! let history = client.identities().history("user_12345", Some(10)).await?;
//!
//! if let Some(cycle_id) = history.last().and_then(|entry| entry.cycle_id) {
//!     let older = client.identities().analysis_cycle("user_12345", cycle_id).await?;
//! }
//!
//! for link in client.identities().links("user_12345").await? {
//!     println!("{:?} {:?}", link.identity_id, link.link_type);
//! }
//! # Ok(())
//! # }
//! ```
//!
//! # Disregarding an identity
//!
//! Leave your own staff and test accounts out of scoring and escalations:
//!
//! ```no_run
//! # async fn run(client: dregs::Client) -> dregs::Result<()> {
//! use dregs::IdentityUpdate;
//!
//! client
//!     .identities()
//!     .update("qa_bot_7", IdentityUpdate::new().disregarded(true))
//!     .await?;
//! # Ok(())
//! # }
//! ```
//!
//! # Asking for a review
//!
//! Dregs's AI reviewer looks over an identity's analysis in the background; read the result back
//! later with [`Identities::reviews`].
//!
//! ```no_run
//! # async fn run(client: dregs::Client) -> dregs::Result<()> {
//! let review = client
//!     .identities()
//!     .review("user_12345", Some("This is a long-standing customer."))
//!     .await?;
//! # Ok(())
//! # }
//! ```
//!
//! # Listing and paging
//!
//! Every namespace with a `list()` returns one [`Page`] at a time, numbered from 0, and a
//! `list_all()` that walks every page as a stream. Filters you leave out are not sent.
//!
//! ```no_run
//! # async fn run(client: dregs::Client) -> dregs::Result<()> {
//! use dregs::{ListIdentities, Order};
//! use futures_util::TryStreamExt;
//!
//! let page = client
//!     .identities()
//!     .list(ListIdentities::new().term("example.com").page_size(50))
//!     .await?;
//!
//! println!("{} match", page.total_elements);
//!
//! let mut identities = client.identities().list_all(
//!     ListIdentities::new().group("org_678").sort("humanityScore").order(Order::Asc),
//! );
//!
//! while let Some(identity) = identities.try_next().await? {
//!     println!("{:?}", identity.id);
//! }
//! # Ok(())
//! # }
//! ```
//!
//! # Groups
//!
//! [`Client::groups`] reads groups back, and keeps Dregs in step when something changes outside an
//! event:
//!
//! ```no_run
//! # async fn run(client: dregs::Client) -> dregs::Result<()> {
//! use dregs::Group;
//! use serde_json::json;
//!
//! client
//!     .groups()
//!     .create(Group::organization("org_678").data(json!({ "name": "Acme Inc" })))
//!     .await?;
//!
//! client.groups().add_member("team", "team_42", "user_12345").await?;
//! client.groups().remove_member("team", "team_42", "user_12345").await?;
//! # Ok(())
//! # }
//! ```
//!
//! # Events, devices, and escalations
//!
//! ```no_run
//! # async fn run(client: dregs::Client) -> dregs::Result<()> {
//! use dregs::{EscalationStatus, ListEscalations, ListEvents};
//! use futures_util::TryStreamExt;
//!
//! let events = client.events().list(ListEvents::new().identity("user_12345")).await?;
//!
//! client.devices().update("a1b2c3d4e5", true).await?;
//!
//! let mut open = client
//!     .escalations()
//!     .list_all(ListEscalations::new().status(EscalationStatus::Open));
//!
//! while let Some(escalation) = open.try_next().await? {
//!     if let Some(id) = escalation.id {
//!         client.escalations().update(id, EscalationStatus::Acknowledged).await?;
//!     }
//! }
//!
//! let summary = client.escalations().summary().await?;
//! # Ok(())
//! # }
//! ```
//!
//! # Features
//!
//! - `rustls` (default): TLS through rustls, verifying certificates with the platform's verifier.
//! - `native-tls`: TLS through the operating system's library instead. Turn off default features
//!   to drop rustls.

#![warn(missing_docs)]

mod client;
mod devices;
mod error;
mod escalations;
mod event;
mod events;
mod groups;
mod identities;
mod models;
mod paging;
pub mod webhooks;

pub use client::{
    BASE_URL_ENV, Client, ClientBuilder, DEFAULT_BASE_URL, DEFAULT_MAX_RETRIES, DEFAULT_TIMEOUT,
    SECRET_KEY_ENV,
};
pub use devices::{Devices, ListDevices};
pub use error::{ApiError, ApiErrorKind, Error, Result};
pub use escalations::{Escalations, ListEscalations};
pub use event::{DEFAULT_GROUP_TYPE, Event, Group};
pub use events::{Events, ListEvents};
pub use groups::{Groups, ListGroups};
pub use identities::{Identities, IdentityUpdate, ListIdentities};
pub use models::{
    Analysis, Badge, Category, Device, Escalation, EscalationSeverity, EscalationStatus,
    EscalationSummary, GroupReference, Identity, IdentityReview, LinkedIdentity, Observation,
    Reference, Score, ScoreHistoryEntry, Scores, TrackResult, TrackedEvent, TrackedGroup,
};
pub use paging::{ItemStream, Order, Page};