bunny-api 0.0.5

Alpha API client for Bunny.net
Documentation
//! Types and API functions related to the Support APIs.

use crate::{Client, APIResult, user::User, error::error_from_response, NoSpecificError};

use reqwest::Method;
use serde::{Deserialize, Serialize};

/// Information about a support ticket.
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "PascalCase")]
pub struct Ticket {
    /// The ticket id.
    pub id: i64,
    /// The current status of the ticket.
    pub status: Option<String>,
    /// The subject line of the ticket.
    pub subject: Option<String>,
    /// A list of comments on the ticket.
    #[serde(default)]
    pub comments: Vec<TicketComment>,
}

/// Information about a comment on a support ticket.
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "PascalCase")]
pub struct TicketComment {
    /// The comment id.
    id: i64,
    /// The type of comment.
    #[serde(rename = "type")]
    typ: Option<String>,
    /// The body of the comment as plain text.
    body: Option<String>,
    /// The body of the comment as HTML.
    html_body: String,
    /// Whether the comment is publicly visible.
    public: bool,
    /// The user id of the commenter.
    author_id: i64,
    /// When the comment was submitted.
    created_at: time::OffsetDateTime,
    /// The [`User`] that submitted the comment.
    user: User,
}

/// The response returned by the [`list_tickets`] endpoint.
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "PascalCase")]
pub struct ListTickets {
    /// All of the tickets on this page.
    pub items: Vec<Ticket>,
    /// Which page was returned, starting at 1.
    pub current_page: i32,
    total_items: i32,
    /// Whether there are more tickets after this.
    pub has_more_items: bool,
}

async fn inner_list_tickets(client: &Client, page: i32, per_page: i32) -> APIResult<ListTickets, NoSpecificError> {
    let url = format!("{}/support/ticket/list?page={}&perPage={}", crate::API_BASE_URL, page, per_page);

    let req = client
        .get(&url)
        .header("accept", "application/json")
        .build()
        .map_err(crate::Error::map_request_err(
            Method::GET,
            url.clone(),
        ))?;

    let resp = client
        .send_logged(req)
        .await
        .map_err(crate::Error::map_request_err(
            Method::GET,
            url.clone(),
        ))?;

    let resp = error_from_response(resp)
        .await
        .map_err(crate::Error::map_response_err(
            Method::GET,
            url.clone(),
        ))?;

    resp.json()
        .await
        .map_err(crate::Error::map_json_err(
            Method::GET,
            url.clone(),
        ))
}

/// List the tickets on a given `page`, with the given amount `per_page`.
///
/// # Defaults
///
/// - `page`: 1
/// - `per_page`: 5 (the minimum)
///
/// # Errors
///
/// Any error that occurs during the API call.
pub async fn list_tickets(client: &Client, page: Option<i32>, per_page: Option<i32>) -> APIResult<ListTickets, NoSpecificError> {
    let page = page.unwrap_or(1);
    let per_page = per_page.unwrap_or(5);
    inner_list_tickets(client, page, per_page).await
}

/// List all tickets for the authenticated user.
///
/// Internally, this calls [`list_tickets`] repeatedly until there are no more tickets returned.
///
/// # Errors
///
/// See [`list_tickets`].
pub async fn list_all_tickets(client: &Client) -> APIResult<Vec<Ticket>, NoSpecificError> {
    let mut page = 1;
    let per_page = 100;
    let mut tickets = Vec::new();

    loop {
        let resp = inner_list_tickets(client, page, per_page).await?;
        let ListTickets { items, has_more_items, .. } = resp;
        tickets.extend(items);

        if has_more_items {
            page += 1;
        } else {
            break;
        }
    }

    Ok(tickets)
}