1use chrono::{DateTime, Utc};
14use serde::{Deserialize, Serialize};
15use serde_json::{Map, Value};
16
17#[derive(Debug, Clone, Deserialize, Serialize)]
19pub struct Ticket {
20 pub id: u64,
22 #[serde(default)]
24 pub number: Option<String>,
25 #[serde(default)]
27 pub title: Option<String>,
28 #[serde(default)]
30 pub group_id: Option<u64>,
31 #[serde(default)]
33 pub state_id: Option<u64>,
34 #[serde(default)]
36 pub priority_id: Option<u64>,
37 #[serde(default)]
39 pub customer_id: Option<u64>,
40 #[serde(default)]
42 pub owner_id: Option<u64>,
43 #[serde(default)]
45 pub organization_id: Option<u64>,
46 #[serde(default)]
48 pub note: Option<String>,
49 #[serde(default)]
51 pub created_at: Option<DateTime<Utc>>,
52 #[serde(default)]
54 pub updated_at: Option<DateTime<Utc>>,
55 #[serde(flatten)]
58 pub extra: Map<String, Value>,
59}
60
61#[derive(Debug, Clone, Deserialize, Serialize)]
67pub struct TicketExpanded {
68 pub id: u64,
70 #[serde(default)]
72 pub number: Option<String>,
73 #[serde(default)]
75 pub title: Option<String>,
76 #[serde(default)]
78 pub group_id: Option<u64>,
79 #[serde(default)]
81 pub group: Option<String>,
82 #[serde(default)]
84 pub state_id: Option<u64>,
85 #[serde(default)]
87 pub state: Option<String>,
88 #[serde(default)]
90 pub priority_id: Option<u64>,
91 #[serde(default)]
93 pub priority: Option<String>,
94 #[serde(default)]
96 pub customer_id: Option<u64>,
97 #[serde(default)]
99 pub customer: Option<String>,
100 #[serde(default)]
102 pub owner_id: Option<u64>,
103 #[serde(default)]
105 pub owner: Option<String>,
106 #[serde(default)]
108 pub organization_id: Option<u64>,
109 #[serde(default)]
111 pub organization: Option<String>,
112 #[serde(default)]
114 pub note: Option<String>,
115 #[serde(default)]
117 pub created_at: Option<DateTime<Utc>>,
118 #[serde(default)]
120 pub updated_at: Option<DateTime<Utc>>,
121 #[serde(flatten)]
123 pub extra: Map<String, Value>,
124}
125
126#[derive(Debug, Clone, Deserialize, Serialize)]
128pub struct User {
129 pub id: u64,
131 #[serde(default)]
133 pub login: Option<String>,
134 #[serde(default)]
136 pub firstname: Option<String>,
137 #[serde(default)]
139 pub lastname: Option<String>,
140 #[serde(default)]
142 pub email: Option<String>,
143 #[serde(default)]
145 pub web: Option<String>,
146 #[serde(default)]
148 pub phone: Option<String>,
149 #[serde(default)]
151 pub mobile: Option<String>,
152 #[serde(default)]
154 pub fax: Option<String>,
155 #[serde(default)]
157 pub note: Option<String>,
158 #[serde(default)]
160 pub organization_id: Option<u64>,
161 #[serde(default)]
163 pub active: Option<bool>,
164 #[serde(default)]
166 pub verified: Option<bool>,
167 #[serde(default)]
169 pub created_at: Option<DateTime<Utc>>,
170 #[serde(default)]
172 pub updated_at: Option<DateTime<Utc>>,
173 #[serde(flatten)]
175 pub extra: Map<String, Value>,
176}
177
178#[derive(Debug, Clone, Default, Serialize)]
180pub struct NewUser {
181 #[serde(skip_serializing_if = "Option::is_none")]
183 pub firstname: Option<String>,
184 #[serde(skip_serializing_if = "Option::is_none")]
186 pub lastname: Option<String>,
187 #[serde(skip_serializing_if = "Option::is_none")]
189 pub email: Option<String>,
190 #[serde(skip_serializing_if = "Option::is_none")]
192 pub login: Option<String>,
193 #[serde(skip_serializing_if = "Option::is_none")]
195 pub phone: Option<String>,
196 #[serde(skip_serializing_if = "Option::is_none")]
198 pub mobile: Option<String>,
199 #[serde(skip_serializing_if = "Option::is_none")]
201 pub organization: Option<String>,
202 #[serde(skip_serializing_if = "Vec::is_empty", default)]
204 pub roles: Vec<String>,
205 #[serde(flatten)]
207 pub extra: Map<String, Value>,
208}
209
210impl NewUser {
211 pub fn with_email(email: impl Into<String>) -> Self {
213 Self {
214 email: Some(email.into()),
215 ..Self::default()
216 }
217 }
218
219 pub fn firstname(mut self, s: impl Into<String>) -> Self {
221 self.firstname = Some(s.into());
222 self
223 }
224 pub fn lastname(mut self, s: impl Into<String>) -> Self {
226 self.lastname = Some(s.into());
227 self
228 }
229 pub fn phone(mut self, s: impl Into<String>) -> Self {
231 self.phone = Some(s.into());
232 self
233 }
234 pub fn role(mut self, s: impl Into<String>) -> Self {
236 self.roles.push(s.into());
237 self
238 }
239 pub fn field(mut self, key: impl Into<String>, value: impl Into<Value>) -> Self {
241 self.extra.insert(key.into(), value.into());
242 self
243 }
244}
245
246#[derive(Debug, Clone, Deserialize, Serialize)]
248pub struct Article {
249 pub id: u64,
251 #[serde(default)]
253 pub ticket_id: Option<u64>,
254 #[serde(default)]
256 pub subject: Option<String>,
257 #[serde(default)]
259 pub body: Option<String>,
260 #[serde(default)]
262 pub from: Option<String>,
263 #[serde(default)]
265 pub to: Option<String>,
266 #[serde(default)]
268 pub cc: Option<String>,
269 #[serde(default)]
271 pub reply_to: Option<String>,
272 #[serde(default)]
274 pub content_type: Option<String>,
275 #[serde(rename = "type", default)]
277 pub article_type: Option<String>,
278 #[serde(default)]
280 pub sender: Option<String>,
281 #[serde(default)]
283 pub internal: Option<bool>,
284 #[serde(default)]
286 pub created_at: Option<DateTime<Utc>>,
287 #[serde(default)]
289 pub updated_at: Option<DateTime<Utc>>,
290 #[serde(default)]
292 pub attachments: Vec<Attachment>,
293 #[serde(flatten)]
295 pub extra: Map<String, Value>,
296}
297
298#[derive(Debug, Clone, Default, Serialize)]
300pub struct NewArticle {
301 #[serde(skip_serializing_if = "Option::is_none")]
303 pub subject: Option<String>,
304 pub body: String,
306 #[serde(rename = "type", skip_serializing_if = "Option::is_none")]
308 pub article_type: Option<String>,
309 #[serde(skip_serializing_if = "Option::is_none")]
311 pub internal: Option<bool>,
312 #[serde(skip_serializing_if = "Option::is_none")]
314 pub content_type: Option<String>,
315 #[serde(skip_serializing_if = "Option::is_none")]
317 pub sender: Option<String>,
318 #[serde(skip_serializing_if = "Option::is_none")]
320 pub from: Option<String>,
321 #[serde(skip_serializing_if = "Option::is_none")]
323 pub to: Option<String>,
324 #[serde(skip_serializing_if = "Option::is_none")]
326 pub cc: Option<String>,
327 #[serde(skip_serializing_if = "Vec::is_empty", default)]
329 pub attachments: Vec<NewAttachment>,
330 #[serde(skip_serializing_if = "Option::is_none")]
333 pub ticket_id: Option<u64>,
334}
335
336impl NewArticle {
337 pub fn new(body: impl Into<String>) -> Self {
340 Self {
341 body: body.into(),
342 ..Self::default()
343 }
344 }
345
346 pub fn subject(mut self, s: impl Into<String>) -> Self {
348 self.subject = Some(s.into());
349 self
350 }
351 pub fn article_type(mut self, s: impl Into<String>) -> Self {
353 self.article_type = Some(s.into());
354 self
355 }
356 pub fn content_type(mut self, s: impl Into<String>) -> Self {
358 self.content_type = Some(s.into());
359 self
360 }
361 pub fn internal(mut self, b: bool) -> Self {
363 self.internal = Some(b);
364 self
365 }
366 pub fn sender(mut self, s: impl Into<String>) -> Self {
368 self.sender = Some(s.into());
369 self
370 }
371 pub fn from(mut self, s: impl Into<String>) -> Self {
373 self.from = Some(s.into());
374 self
375 }
376 pub fn to(mut self, s: impl Into<String>) -> Self {
378 self.to = Some(s.into());
379 self
380 }
381 pub fn cc(mut self, s: impl Into<String>) -> Self {
383 self.cc = Some(s.into());
384 self
385 }
386 pub fn attach(mut self, a: NewAttachment) -> Self {
388 self.attachments.push(a);
389 self
390 }
391}
392
393#[derive(Debug, Clone, Deserialize, Serialize)]
395pub struct Attachment {
396 pub id: u64,
398 #[serde(default)]
400 pub filename: Option<String>,
401 #[serde(default)]
403 pub size: Option<String>,
404 #[serde(default)]
406 pub preferences: Option<Value>,
407}
408
409#[derive(Debug, Clone, Serialize)]
411pub struct NewAttachment {
412 pub filename: String,
414 pub data: String,
416 #[serde(rename = "mime-type")]
418 pub mime_type: String,
419}
420
421impl NewAttachment {
422 pub fn from_bytes(
424 filename: impl Into<String>,
425 mime_type: impl Into<String>,
426 bytes: &[u8],
427 ) -> Self {
428 use base64::Engine;
429 Self {
430 filename: filename.into(),
431 mime_type: mime_type.into(),
432 data: base64::engine::general_purpose::STANDARD.encode(bytes),
433 }
434 }
435}
436
437#[derive(Debug, Clone, Deserialize, Serialize)]
439pub struct Organization {
440 pub id: u64,
442 #[serde(default)]
444 pub name: Option<String>,
445 #[serde(default)]
447 pub shared: Option<bool>,
448 #[serde(default)]
450 pub domain: Option<String>,
451 #[serde(default)]
453 pub domain_assignment: Option<bool>,
454 #[serde(default)]
456 pub active: Option<bool>,
457 #[serde(default)]
459 pub note: Option<String>,
460 #[serde(default)]
462 pub created_at: Option<DateTime<Utc>>,
463 #[serde(default)]
465 pub updated_at: Option<DateTime<Utc>>,
466 #[serde(flatten)]
468 pub extra: Map<String, Value>,
469}
470
471#[derive(Debug, Clone, Default, Serialize)]
473pub struct NewOrganization {
474 pub name: String,
476 #[serde(skip_serializing_if = "Option::is_none")]
478 pub domain: Option<String>,
479 #[serde(skip_serializing_if = "Option::is_none")]
481 pub shared: Option<bool>,
482 #[serde(skip_serializing_if = "Option::is_none")]
484 pub note: Option<String>,
485 #[serde(flatten)]
487 pub extra: Map<String, Value>,
488}
489
490impl NewOrganization {
491 pub fn new(name: impl Into<String>) -> Self {
493 Self {
494 name: name.into(),
495 ..Self::default()
496 }
497 }
498}
499
500#[derive(Debug, Clone, Deserialize, Serialize)]
502pub struct Group {
503 pub id: u64,
505 #[serde(default)]
507 pub name: Option<String>,
508 #[serde(default)]
510 pub signature_id: Option<u64>,
511 #[serde(default)]
513 pub email_address_id: Option<u64>,
514 #[serde(default)]
516 pub follow_up_possible: Option<String>,
517 #[serde(default)]
519 pub follow_up_assignment: Option<bool>,
520 #[serde(default)]
522 pub active: Option<bool>,
523 #[serde(default)]
525 pub note: Option<String>,
526 #[serde(flatten)]
528 pub extra: Map<String, Value>,
529}
530
531#[derive(Debug, Clone, Deserialize, Serialize)]
533pub struct TicketState {
534 pub id: u64,
536 #[serde(default)]
538 pub name: Option<String>,
539 #[serde(default)]
541 pub state_type_id: Option<u64>,
542 #[serde(default)]
544 pub ignore_escalation: Option<bool>,
545 #[serde(default)]
547 pub default_create: Option<bool>,
548 #[serde(default)]
550 pub default_follow_up: Option<bool>,
551 #[serde(default)]
553 pub active: Option<bool>,
554 #[serde(default)]
556 pub note: Option<String>,
557}
558
559#[derive(Debug, Clone, Deserialize, Serialize)]
561pub struct TicketPriority {
562 pub id: u64,
564 #[serde(default)]
566 pub name: Option<String>,
567 #[serde(default)]
569 pub default_create: Option<bool>,
570 #[serde(default)]
572 pub ui_icon: Option<String>,
573 #[serde(default)]
575 pub ui_color: Option<String>,
576 #[serde(default)]
578 pub active: Option<bool>,
579 #[serde(default)]
581 pub note: Option<String>,
582}
583
584#[cfg(test)]
585mod tests {
586 use super::NewAttachment;
587
588 #[test]
589 fn attachment_encodes_bytes_as_base64() {
590 let a = NewAttachment::from_bytes("hi.txt", "text/plain", b"foobar");
591 assert_eq!(a.data, "Zm9vYmFy");
592 }
593}