jira_query/
issue_model.rs

1/*
2Copyright 2022 Marek Suchánek <msuchane@redhat.com>
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17use chrono::{DateTime, NaiveDate, Utc};
18/// This module replicates the fields in a Jira issue as strongly typed structs.
19/// Any extra fields that come from a custom Jira configuration are captured
20/// in the `extra` hash map in the parent struct.
21use serde::{Deserialize, Serialize};
22use serde_json::Value;
23
24/// The response from Jira to a JQL query,
25/// which includes the list of requested issues and additional metadata.
26#[derive(Clone, Debug, Deserialize)]
27pub struct JqlResults {
28    pub issues: Vec<Issue>,
29    #[serde(flatten)]
30    pub extra: Value,
31}
32
33/// A single Jira issue with all its fields.
34#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
35pub struct Issue {
36    pub id: String,
37    pub key: String,
38    pub expand: String,
39    pub fields: Fields,
40    #[serde(rename = "self")]
41    pub self_link: String,
42    #[serde(flatten)]
43    pub extra: Value,
44}
45
46/// A container for most fields of a Jira issue.
47#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
48pub struct Fields {
49    #[serde(rename = "lastViewed")]
50    pub last_viewed: Option<DateTime<Utc>>,
51    pub labels: Vec<String>,
52    pub assignee: Option<User>,
53    pub description: Option<String>,
54    pub duedate: Option<NaiveDate>,
55    // Both `versions` and `fixVersions` are optional fields and they might
56    // either be missing or set to an empty list.
57    // I'm consolidating both cases as an empty list, because I don't believe
58    // that there's a meaningful semantic difference between them here.
59    #[serde(default)]
60    pub versions: Vec<Version>,
61    #[serde(default)]
62    #[serde(rename = "fixVersions")]
63    pub fix_versions: Vec<Version>,
64    pub reporter: User,
65    pub status: Status,
66    pub created: DateTime<Utc>,
67    pub updated: DateTime<Utc>,
68    pub issuetype: IssueType,
69    pub timeestimate: Option<i32>,
70    pub aggregatetimeestimate: Option<i32>,
71    pub timeoriginalestimate: Option<i32>,
72    pub timespent: Option<i32>,
73    pub aggregatetimespent: Option<i32>,
74    pub aggregatetimeoriginalestimate: Option<i32>,
75    pub progress: Option<Progress>,
76    pub aggregateprogress: Option<Progress>,
77    pub workratio: i64,
78    pub summary: String,
79    pub creator: User,
80    pub project: Project,
81    pub priority: Option<Priority>,
82    pub components: Vec<Component>,
83    pub watches: Watches,
84    pub archiveddate: Option<DateTime<Utc>>,
85    pub archivedby: Option<DateTime<Utc>>,
86    pub resolution: Option<Resolution>,
87    pub resolutiondate: Option<DateTime<Utc>>,
88    pub comment: Option<Comments>,
89    pub issuelinks: Vec<IssueLink>,
90    pub votes: Votes,
91    pub parent: Option<CondensedIssue>,
92    pub subtasks: Vec<CondensedIssue>,
93    pub environment: Option<String>,
94    pub security: Option<Security>,
95    #[serde(flatten)]
96    pub extra: Value,
97}
98
99/// The representation of a Jira user account.
100#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
101pub struct User {
102    pub active: bool,
103    #[serde(rename = "displayName")]
104    pub display_name: String,
105    #[serde(rename = "emailAddress")]
106    pub email_address: Option<String>,
107    pub key: Option<String>,
108    pub name: Option<String>,
109    #[serde(rename = "timeZone")]
110    pub time_zone: String,
111    #[serde(rename = "avatarUrls")]
112    pub avatar_urls: AvatarUrls,
113    #[serde(rename = "self")]
114    pub self_link: String,
115    #[serde(flatten)]
116    pub extra: Value,
117    #[serde(rename = "accountId")]
118    pub account_id: Option<String>,
119}
120
121/// The representation of a Jira product version.
122#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
123pub struct Version {
124    pub id: String,
125    pub description: Option<String>,
126    pub name: String,
127    pub archived: bool,
128    pub released: bool,
129    /// Jira stores `releaseDate` only as `YYYY-MM-DD`, so it can't Serialize, Deserialize to full `DateTime`.
130    #[serde(rename = "releaseDate")]
131    pub release_date: Option<NaiveDate>,
132    #[serde(rename = "self")]
133    pub self_link: String,
134    #[serde(flatten)]
135    pub extra: Value,
136}
137
138/// The Jira issue status.
139#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
140pub struct Status {
141    pub description: String,
142    #[serde(rename = "iconUrl")]
143    pub icon_url: String,
144    pub id: String,
145    pub name: String,
146    #[serde(rename = "statusCategory")]
147    pub status_category: StatusCategory,
148    #[serde(rename = "self")]
149    pub self_link: String,
150    #[serde(flatten)]
151    pub extra: Value,
152}
153
154/// The category of a Jira issue status.
155#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
156pub struct StatusCategory {
157    #[serde(rename = "colorName")]
158    pub color_name: String,
159    pub id: i32,
160    pub key: String,
161    pub name: String,
162    #[serde(rename = "self")]
163    pub self_link: String,
164    #[serde(flatten)]
165    pub extra: Value,
166}
167
168/// The resolution of a Jira issue when it's closed.
169#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
170pub struct Resolution {
171    pub description: String,
172    pub id: String,
173    pub name: String,
174    #[serde(rename = "self")]
175    pub self_link: String,
176    #[serde(flatten)]
177    pub extra: Value,
178}
179
180/// The type of a Jira issue.
181#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
182pub struct IssueType {
183    #[serde(rename = "avatarId")]
184    pub avatar_id: Option<i32>,
185    pub description: String,
186    #[serde(rename = "iconUrl")]
187    pub icon_url: String,
188    pub id: String,
189    pub name: String,
190    pub subtask: bool,
191    #[serde(rename = "self")]
192    pub self_link: String,
193    #[serde(flatten)]
194    pub extra: Value,
195}
196
197/// A project namespace that groups Jira issues.
198#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
199pub struct Project {
200    pub id: String,
201    pub key: String,
202    pub name: String,
203    #[serde(rename = "projectTypeKey")]
204    pub project_type_key: String,
205    #[serde(rename = "projectCategory")]
206    pub project_category: Option<ProjectCategory>,
207    #[serde(rename = "avatarUrls")]
208    pub avatar_urls: AvatarUrls,
209    #[serde(rename = "self")]
210    pub self_link: String,
211    #[serde(flatten)]
212    pub extra: Value,
213}
214
215/// The category of a Jira project.
216#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
217pub struct ProjectCategory {
218    pub description: String,
219    pub id: String,
220    pub name: String,
221    #[serde(rename = "self")]
222    pub self_link: String,
223    #[serde(flatten)]
224    pub extra: Value,
225}
226
227/// The priority of a Jira issue.
228#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
229pub struct Priority {
230    #[serde(rename = "iconUrl")]
231    pub icon_url: String,
232    pub id: String,
233    pub name: String,
234    #[serde(rename = "self")]
235    pub self_link: String,
236    #[serde(flatten)]
237    pub extra: Value,
238}
239
240/// The component of a Jira issue.
241#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
242pub struct Component {
243    pub description: Option<String>,
244    pub id: String,
245    pub name: String,
246    #[serde(rename = "self")]
247    pub self_link: String,
248    #[serde(flatten)]
249    pub extra: Value,
250}
251
252/// Users watching a Jira issue.
253#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
254pub struct Watches {
255    #[serde(rename = "isWatching")]
256    pub is_watching: bool,
257    #[serde(rename = "watchCount")]
258    pub watch_count: i32,
259    #[serde(rename = "self")]
260    pub self_link: String,
261    #[serde(flatten)]
262    pub extra: Value,
263}
264
265/// The progress of a Jira issue.
266#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
267pub struct Progress {
268    pub progress: i32,
269    pub total: i32,
270    #[serde(flatten)]
271    pub extra: Value,
272}
273
274/// A comment below a Jira issue.
275#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
276pub struct Comment {
277    pub author: User,
278    pub body: String,
279    pub created: DateTime<Utc>,
280    pub id: String,
281    #[serde(rename = "updateAuthor")]
282    pub update_author: User,
283    pub updated: DateTime<Utc>,
284    pub visibility: Option<Visibility>,
285    #[serde(rename = "self")]
286    pub self_link: String,
287    #[serde(flatten)]
288    pub extra: Value,
289}
290
291/// A container for all comments below a Jira issue.
292#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
293pub struct Comments {
294    pub comments: Vec<Comment>,
295    #[serde(rename = "maxResults")]
296    pub max_results: i32,
297    #[serde(rename = "startAt")]
298    pub start_at: i32,
299    pub total: i32,
300    #[serde(flatten)]
301    pub extra: Value,
302}
303
304/// A link from one Jira issue to another.
305#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
306pub struct IssueLink {
307    pub id: String,
308    #[serde(rename = "outwardIssue")]
309    pub outward_issue: Option<LinkedIssue>,
310    #[serde(rename = "inwardIssue")]
311    pub inward_issue: Option<LinkedIssue>,
312    #[serde(rename = "type")]
313    pub link_type: IssueLinkType,
314    #[serde(rename = "self")]
315    pub self_link: String,
316    #[serde(flatten)]
317    pub extra: Value,
318}
319
320/// A Jira issue linked from another one.
321#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
322pub struct LinkedIssue {
323    pub id: String,
324    pub key: String,
325    pub fields: LinkedIssueFields,
326    #[serde(rename = "self")]
327    pub self_link: String,
328    #[serde(flatten)]
329    pub extra: Value,
330}
331
332/// The reduced fields of a linked Jira issue.
333#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
334pub struct LinkedIssueFields {
335    pub issuetype: IssueType,
336    pub priority: Option<Priority>,
337    pub status: Status,
338    pub summary: String,
339    #[serde(flatten)]
340    pub extra: Value,
341}
342
343/// The direction of a link to a Jira issue.
344#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
345pub struct IssueLinkType {
346    pub id: String,
347    pub inward: String,
348    pub name: String,
349    pub outward: String,
350    #[serde(rename = "self")]
351    pub self_link: String,
352    #[serde(flatten)]
353    pub extra: Value,
354}
355
356/// The votes for a Jira issue.
357#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
358pub struct Votes {
359    #[serde(rename = "hasVoted")]
360    pub has_voted: bool,
361    pub votes: i32,
362    #[serde(rename = "self")]
363    pub self_link: String,
364    #[serde(flatten)]
365    pub extra: Value,
366}
367
368/// A Jira avatar in several different sizes:
369///
370/// * `xsmall` = 16x16 px
371/// * `small` = 24x24 px
372/// * `medium` = 48x48 px
373/// * `full` = maximum
374#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
375pub struct AvatarUrls {
376    #[serde(rename = "16x16")]
377    pub xsmall: String,
378    #[serde(rename = "24x24")]
379    pub small: String,
380    #[serde(rename = "32x32")]
381    pub medium: String,
382    #[serde(rename = "48x48")]
383    pub full: String,
384    #[serde(flatten)]
385    pub extra: Value,
386}
387
388/// A minimal, reduced representation of a Jira issue.
389#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
390pub struct CondensedIssue {
391    pub fields: CondensedFields,
392    pub id: String,
393    pub key: String,
394    #[serde(rename = "self")]
395    pub self_link: String,
396    #[serde(flatten)]
397    pub extra: Value,
398}
399
400/// A minimal, reduced listing of the fields of a Jira issue.
401#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
402pub struct CondensedFields {
403    pub issuetype: IssueType,
404    pub priority: Option<Priority>,
405    pub status: Status,
406    pub summary: String,
407    #[serde(flatten)]
408    pub extra: Value,
409}
410
411/// The visibility of a Jira issue.
412#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
413pub struct Visibility {
414    pub r#type: String,
415    pub value: String,
416    #[serde(flatten)]
417    pub extra: Value,
418}
419
420/// The security level of a Jira issue.
421#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
422// TODO: This seems to be a generic container, similar to several other structs.
423// In a future major release, try to consolidate them into one generic struct with:
424// description, id, name.
425// Also see if Serde can convert id to a number after all, somehow.
426pub struct Security {
427    pub description: String,
428    pub id: String,
429    pub name: String,
430    #[serde(rename = "self")]
431    pub self_link: String,
432    #[serde(flatten)]
433    pub extra: Value,
434}