1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
//! 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)
}