use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Ticket {
pub id: u64,
#[serde(default)]
pub number: Option<String>,
#[serde(default)]
pub title: Option<String>,
#[serde(default)]
pub group_id: Option<u64>,
#[serde(default)]
pub state_id: Option<u64>,
#[serde(default)]
pub priority_id: Option<u64>,
#[serde(default)]
pub customer_id: Option<u64>,
#[serde(default)]
pub owner_id: Option<u64>,
#[serde(default)]
pub organization_id: Option<u64>,
#[serde(default)]
pub note: Option<String>,
#[serde(default)]
pub created_at: Option<DateTime<Utc>>,
#[serde(default)]
pub updated_at: Option<DateTime<Utc>>,
#[serde(flatten)]
pub extra: Map<String, Value>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct TicketExpanded {
pub id: u64,
#[serde(default)]
pub number: Option<String>,
#[serde(default)]
pub title: Option<String>,
#[serde(default)]
pub group_id: Option<u64>,
#[serde(default)]
pub group: Option<String>,
#[serde(default)]
pub state_id: Option<u64>,
#[serde(default)]
pub state: Option<String>,
#[serde(default)]
pub priority_id: Option<u64>,
#[serde(default)]
pub priority: Option<String>,
#[serde(default)]
pub customer_id: Option<u64>,
#[serde(default)]
pub customer: Option<String>,
#[serde(default)]
pub owner_id: Option<u64>,
#[serde(default)]
pub owner: Option<String>,
#[serde(default)]
pub organization_id: Option<u64>,
#[serde(default)]
pub organization: Option<String>,
#[serde(default)]
pub note: Option<String>,
#[serde(default)]
pub created_at: Option<DateTime<Utc>>,
#[serde(default)]
pub updated_at: Option<DateTime<Utc>>,
#[serde(flatten)]
pub extra: Map<String, Value>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct User {
pub id: u64,
#[serde(default)]
pub login: Option<String>,
#[serde(default)]
pub firstname: Option<String>,
#[serde(default)]
pub lastname: Option<String>,
#[serde(default)]
pub email: Option<String>,
#[serde(default)]
pub web: Option<String>,
#[serde(default)]
pub phone: Option<String>,
#[serde(default)]
pub mobile: Option<String>,
#[serde(default)]
pub fax: Option<String>,
#[serde(default)]
pub note: Option<String>,
#[serde(default)]
pub organization_id: Option<u64>,
#[serde(default)]
pub active: Option<bool>,
#[serde(default)]
pub verified: Option<bool>,
#[serde(default)]
pub created_at: Option<DateTime<Utc>>,
#[serde(default)]
pub updated_at: Option<DateTime<Utc>>,
#[serde(flatten)]
pub extra: Map<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct NewUser {
#[serde(skip_serializing_if = "Option::is_none")]
pub firstname: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub lastname: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub email: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub login: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub phone: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mobile: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub organization: Option<String>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub roles: Vec<String>,
#[serde(flatten)]
pub extra: Map<String, Value>,
}
impl NewUser {
pub fn with_email(email: impl Into<String>) -> Self {
Self {
email: Some(email.into()),
..Self::default()
}
}
pub fn firstname(mut self, s: impl Into<String>) -> Self {
self.firstname = Some(s.into());
self
}
pub fn lastname(mut self, s: impl Into<String>) -> Self {
self.lastname = Some(s.into());
self
}
pub fn phone(mut self, s: impl Into<String>) -> Self {
self.phone = Some(s.into());
self
}
pub fn role(mut self, s: impl Into<String>) -> Self {
self.roles.push(s.into());
self
}
pub fn field(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
self.extra.insert(key.into(), value.into());
self
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Article {
pub id: u64,
#[serde(default)]
pub ticket_id: Option<u64>,
#[serde(default)]
pub subject: Option<String>,
#[serde(default)]
pub body: Option<String>,
#[serde(default)]
pub from: Option<String>,
#[serde(default)]
pub to: Option<String>,
#[serde(default)]
pub cc: Option<String>,
#[serde(default)]
pub reply_to: Option<String>,
#[serde(default)]
pub content_type: Option<String>,
#[serde(rename = "type", default)]
pub article_type: Option<String>,
#[serde(default)]
pub sender: Option<String>,
#[serde(default)]
pub internal: Option<bool>,
#[serde(default)]
pub created_at: Option<DateTime<Utc>>,
#[serde(default)]
pub updated_at: Option<DateTime<Utc>>,
#[serde(default)]
pub attachments: Vec<Attachment>,
#[serde(flatten)]
pub extra: Map<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct NewArticle {
#[serde(skip_serializing_if = "Option::is_none")]
pub subject: Option<String>,
pub body: String,
#[serde(rename = "type", skip_serializing_if = "Option::is_none")]
pub article_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub internal: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub content_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sender: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub from: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub to: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cc: Option<String>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub attachments: Vec<NewAttachment>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ticket_id: Option<u64>,
}
impl NewArticle {
pub fn new(body: impl Into<String>) -> Self {
Self {
body: body.into(),
..Self::default()
}
}
pub fn subject(mut self, s: impl Into<String>) -> Self {
self.subject = Some(s.into());
self
}
pub fn article_type(mut self, s: impl Into<String>) -> Self {
self.article_type = Some(s.into());
self
}
pub fn content_type(mut self, s: impl Into<String>) -> Self {
self.content_type = Some(s.into());
self
}
pub fn internal(mut self, b: bool) -> Self {
self.internal = Some(b);
self
}
pub fn sender(mut self, s: impl Into<String>) -> Self {
self.sender = Some(s.into());
self
}
pub fn from(mut self, s: impl Into<String>) -> Self {
self.from = Some(s.into());
self
}
pub fn to(mut self, s: impl Into<String>) -> Self {
self.to = Some(s.into());
self
}
pub fn cc(mut self, s: impl Into<String>) -> Self {
self.cc = Some(s.into());
self
}
pub fn attach(mut self, a: NewAttachment) -> Self {
self.attachments.push(a);
self
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Attachment {
pub id: u64,
#[serde(default)]
pub filename: Option<String>,
#[serde(default)]
pub size: Option<String>,
#[serde(default)]
pub preferences: Option<Value>,
}
#[derive(Debug, Clone, Serialize)]
pub struct NewAttachment {
pub filename: String,
pub data: String,
#[serde(rename = "mime-type")]
pub mime_type: String,
}
impl NewAttachment {
pub fn from_bytes(
filename: impl Into<String>,
mime_type: impl Into<String>,
bytes: &[u8],
) -> Self {
use base64::Engine;
Self {
filename: filename.into(),
mime_type: mime_type.into(),
data: base64::engine::general_purpose::STANDARD.encode(bytes),
}
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Organization {
pub id: u64,
#[serde(default)]
pub name: Option<String>,
#[serde(default)]
pub shared: Option<bool>,
#[serde(default)]
pub domain: Option<String>,
#[serde(default)]
pub domain_assignment: Option<bool>,
#[serde(default)]
pub active: Option<bool>,
#[serde(default)]
pub note: Option<String>,
#[serde(default)]
pub created_at: Option<DateTime<Utc>>,
#[serde(default)]
pub updated_at: Option<DateTime<Utc>>,
#[serde(flatten)]
pub extra: Map<String, Value>,
}
#[derive(Debug, Clone, Default, Serialize)]
pub struct NewOrganization {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub domain: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub shared: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub note: Option<String>,
#[serde(flatten)]
pub extra: Map<String, Value>,
}
impl NewOrganization {
pub fn new(name: impl Into<String>) -> Self {
Self {
name: name.into(),
..Self::default()
}
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Group {
pub id: u64,
#[serde(default)]
pub name: Option<String>,
#[serde(default)]
pub signature_id: Option<u64>,
#[serde(default)]
pub email_address_id: Option<u64>,
#[serde(default)]
pub follow_up_possible: Option<String>,
#[serde(default)]
pub follow_up_assignment: Option<bool>,
#[serde(default)]
pub active: Option<bool>,
#[serde(default)]
pub note: Option<String>,
#[serde(flatten)]
pub extra: Map<String, Value>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct TicketState {
pub id: u64,
#[serde(default)]
pub name: Option<String>,
#[serde(default)]
pub state_type_id: Option<u64>,
#[serde(default)]
pub ignore_escalation: Option<bool>,
#[serde(default)]
pub default_create: Option<bool>,
#[serde(default)]
pub default_follow_up: Option<bool>,
#[serde(default)]
pub active: Option<bool>,
#[serde(default)]
pub note: Option<String>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct TicketPriority {
pub id: u64,
#[serde(default)]
pub name: Option<String>,
#[serde(default)]
pub default_create: Option<bool>,
#[serde(default)]
pub ui_icon: Option<String>,
#[serde(default)]
pub ui_color: Option<String>,
#[serde(default)]
pub active: Option<bool>,
#[serde(default)]
pub note: Option<String>,
}
#[cfg(test)]
mod tests {
use super::NewAttachment;
#[test]
fn attachment_encodes_bytes_as_base64() {
let a = NewAttachment::from_bytes("hi.txt", "text/plain", b"foobar");
assert_eq!(a.data, "Zm9vYmFy");
}
}