1use std::collections::HashMap;
8
9use serde::{Deserialize, Serialize};
10
11#[derive(Debug, Clone, Serialize)]
18pub struct JiraIssue {
19 pub key: String,
21
22 pub summary: String,
24
25 pub description_adf: Option<serde_json::Value>,
27
28 pub status: Option<String>,
30
31 pub issue_type: Option<String>,
33
34 pub assignee: Option<String>,
36
37 pub priority: Option<String>,
39
40 pub labels: Vec<String>,
42
43 #[serde(default, skip_serializing_if = "Vec::is_empty")]
46 pub custom_fields: Vec<JiraCustomField>,
47}
48
49#[derive(Debug, Clone, Default)]
56pub enum FieldSelection {
57 #[default]
60 Standard,
61
62 Named(Vec<String>),
66
67 All,
69}
70
71#[derive(Debug, Clone, Serialize)]
76pub struct JiraCustomField {
77 pub id: String,
79
80 pub name: String,
83
84 pub value: serde_json::Value,
87}
88
89#[derive(Debug, Clone, Default)]
95pub struct EditMeta {
96 pub fields: std::collections::BTreeMap<String, EditMetaField>,
98}
99
100#[derive(Debug, Clone)]
102pub struct EditMetaField {
103 pub name: String,
105
106 pub schema: EditMetaSchema,
108
109 pub allowed_values: Vec<String>,
117}
118
119#[derive(Debug, Clone, Default)]
121pub struct EditMetaSchema {
122 pub kind: String,
124
125 pub custom: Option<String>,
128
129 pub items: Option<String>,
132
133 pub system: Option<String>,
136}
137
138impl EditMetaField {
139 pub fn is_adf_rich_text(&self) -> bool {
143 self.schema.custom.as_deref() == Some(TEXTAREA_CUSTOM_TYPE)
144 || matches!(
145 self.schema.system.as_deref(),
146 Some("description" | "environment")
147 )
148 }
149}
150
151#[derive(Debug, Clone, Serialize)]
159pub struct CreateMeta {
160 pub project: String,
162
163 pub issue_type: String,
165
166 pub fields: Vec<CreateMetaField>,
168}
169
170#[derive(Debug, Clone, Serialize)]
172pub struct CreateMetaField {
173 pub field_id: String,
175
176 pub name: String,
178
179 pub required: bool,
181
182 pub schema_type: String,
184
185 #[serde(skip_serializing_if = "Option::is_none")]
187 pub items: Option<String>,
188
189 #[serde(skip_serializing_if = "Option::is_none")]
192 pub custom: Option<String>,
193
194 #[serde(skip_serializing_if = "Vec::is_empty")]
197 pub allowed_values: Vec<CreateMetaAllowedValue>,
198
199 #[serde(skip_serializing_if = "Option::is_none")]
201 pub default_value: Option<serde_json::Value>,
202}
203
204#[derive(Debug, Clone, Serialize)]
207pub struct CreateMetaAllowedValue {
208 #[serde(skip_serializing_if = "Option::is_none")]
210 pub id: Option<String>,
211
212 #[serde(skip_serializing_if = "Option::is_none")]
214 pub value: Option<String>,
215
216 #[serde(skip_serializing_if = "Vec::is_empty")]
218 pub children: Vec<Self>,
219}
220
221#[derive(Debug, Clone, Serialize, Deserialize)]
224pub struct JiraUser {
225 #[serde(rename = "displayName")]
227 pub display_name: String,
228
229 #[serde(rename = "emailAddress")]
231 pub email_address: Option<String>,
232
233 #[serde(rename = "accountId")]
235 pub account_id: String,
236}
237
238#[derive(Debug, Clone, Serialize)]
240pub struct JiraWatcherList {
241 pub watchers: Vec<JiraUser>,
243
244 pub watch_count: u32,
246}
247
248#[derive(Debug, Clone, Serialize)]
250pub struct JiraCreatedIssue {
251 pub key: String,
253 pub id: String,
255 pub self_url: String,
257}
258
259#[derive(Debug, Clone, Serialize)]
264pub struct JiraSearchResult {
265 pub issues: Vec<JiraIssue>,
267
268 pub total: u32,
270}
271
272#[derive(Debug, Clone, Serialize)]
281pub struct JiraUserSearchResult {
282 pub account_id: String,
284 #[serde(skip_serializing_if = "Option::is_none")]
286 pub display_name: Option<String>,
287 #[serde(skip_serializing_if = "Option::is_none")]
289 pub email_address: Option<String>,
290 pub active: bool,
292 #[serde(skip_serializing_if = "Option::is_none")]
294 pub account_type: Option<String>,
295}
296
297#[derive(Debug, Clone, Serialize)]
303pub struct JiraUserSearchResults {
304 pub users: Vec<JiraUserSearchResult>,
306 pub count: u32,
309}
310
311#[derive(Debug, Clone, Serialize)]
321pub struct JiraUserRecord {
322 pub account_id: String,
324 #[serde(skip_serializing_if = "Option::is_none")]
326 pub display_name: Option<String>,
327 #[serde(skip_serializing_if = "Option::is_none")]
329 pub email_address: Option<String>,
330 #[serde(skip_serializing_if = "Option::is_none")]
332 pub active: Option<bool>,
333 #[serde(skip_serializing_if = "Option::is_none")]
335 pub account_type: Option<String>,
336 #[serde(skip_serializing_if = "Option::is_none")]
339 pub error: Option<String>,
340}
341
342#[derive(Debug, Clone, Serialize)]
345pub struct JiraUserGetResults {
346 pub users: Vec<JiraUserRecord>,
348}
349
350#[derive(Debug, Clone, Serialize)]
352pub struct JiraComment {
353 pub id: String,
355 pub author: String,
357 pub body_adf: Option<serde_json::Value>,
359 pub created: String,
361 #[serde(skip_serializing_if = "Option::is_none")]
363 pub updated: Option<String>,
364}
365
366#[derive(Debug, Clone, Copy, Serialize)]
372#[serde(rename_all = "lowercase")]
373pub enum JiraVisibilityType {
374 Group,
376 Role,
378}
379
380#[derive(Debug, Clone)]
385pub struct JiraVisibility {
386 pub ty: JiraVisibilityType,
388 pub value: String,
390}
391
392impl Serialize for JiraVisibility {
393 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
394 use serde::ser::SerializeStruct;
395 let mut s = serializer.serialize_struct("JiraVisibility", 2)?;
396 s.serialize_field("type", &self.ty)?;
397 s.serialize_field("identifier", &self.value)?;
398 s.end()
399 }
400}
401
402#[derive(Debug, Clone, Serialize)]
406pub struct JiraProject {
407 pub id: String,
409 pub key: String,
411 pub name: String,
413 pub project_type: Option<String>,
415 pub lead: Option<String>,
417}
418
419#[derive(Debug, Clone, Serialize)]
422pub struct JiraProjectList {
423 pub projects: Vec<JiraProject>,
425 pub total: u32,
427}
428
429pub(crate) const TEXTAREA_CUSTOM_TYPE: &str =
432 "com.atlassian.jira.plugin.system.customfieldtypes:textarea";
433
434#[derive(Debug, Clone, Serialize)]
436pub struct JiraField {
437 pub id: String,
439 pub name: String,
441 pub custom: bool,
443 pub schema_type: Option<String>,
448 #[serde(skip_serializing_if = "Option::is_none")]
452 pub schema_custom: Option<String>,
453}
454
455#[derive(Debug, Clone, Serialize)]
458pub struct JiraFieldOption {
459 pub id: String,
461 pub value: String,
463}
464
465#[derive(Debug, Clone, Serialize)]
469pub struct AgileBoard {
470 pub id: u64,
472 pub name: String,
474 pub board_type: String,
476 pub project_key: Option<String>,
478}
479
480#[derive(Debug, Clone, Serialize)]
483pub struct AgileBoardList {
484 pub boards: Vec<AgileBoard>,
486 pub total: u32,
488}
489
490#[derive(Debug, Clone, Serialize)]
494pub struct AgileSprint {
495 pub id: u64,
497 pub name: String,
499 pub state: String,
501 pub start_date: Option<String>,
503 pub end_date: Option<String>,
505 pub goal: Option<String>,
507}
508
509#[derive(Debug, Clone, Serialize)]
512pub struct AgileSprintList {
513 pub sprints: Vec<AgileSprint>,
515 pub total: u32,
517}
518
519#[derive(Debug, Clone, Serialize)]
525pub struct JiraProjectVersion {
526 pub id: String,
528 pub name: String,
530 pub description: Option<String>,
532 pub project_key: String,
534 pub released: bool,
536 pub archived: bool,
538 pub release_date: Option<String>,
540 pub start_date: Option<String>,
542}
543
544#[derive(Debug, Clone, Serialize)]
547pub struct JiraProjectVersionList {
548 pub versions: Vec<JiraProjectVersion>,
550 pub total: u32,
552}
553
554#[derive(Debug, Clone, Serialize)]
557pub struct JiraChangelogEntry {
558 pub id: String,
560 pub author: String,
562 pub created: String,
564 pub items: Vec<JiraChangelogItem>,
566}
567
568#[derive(Debug, Clone, Serialize)]
570pub struct JiraChangelogItem {
571 pub field: String,
573 pub from_string: Option<String>,
575 pub to_string: Option<String>,
577}
578
579#[derive(Debug, Clone, Serialize)]
581pub struct JiraLinkType {
582 pub id: String,
584 pub name: String,
586 pub inward: String,
588 pub outward: String,
590}
591
592#[derive(Debug, Clone, Serialize)]
596pub struct JiraIssueLink {
597 pub id: String,
599 pub link_type: String,
601 pub direction: String,
603 pub linked_issue_key: String,
605 pub linked_issue_summary: String,
607}
608
609#[derive(Debug, Clone, Serialize)]
615pub struct JiraRemoteIssueLink {
616 pub id: String,
618 #[serde(skip_serializing_if = "Option::is_none")]
621 pub global_id: Option<String>,
622 #[serde(skip_serializing_if = "Option::is_none")]
625 pub relationship: Option<String>,
626 pub object: JiraRemoteIssueLinkObject,
628}
629
630#[derive(Debug, Clone, Serialize)]
632pub struct JiraRemoteIssueLinkObject {
633 pub url: String,
635 #[serde(skip_serializing_if = "Option::is_none")]
637 pub title: Option<String>,
638 #[serde(skip_serializing_if = "Option::is_none")]
640 pub summary: Option<String>,
641 #[serde(skip_serializing_if = "Option::is_none")]
644 pub icon: Option<JiraRemoteIssueLinkIcon>,
645}
646
647#[derive(Debug, Clone, Serialize)]
650pub struct JiraRemoteIssueLinkIcon {
651 #[serde(skip_serializing_if = "Option::is_none")]
653 pub url: Option<String>,
654 #[serde(skip_serializing_if = "Option::is_none")]
656 pub title: Option<String>,
657}
658
659#[derive(Debug, Clone, Serialize)]
665pub struct JiraAttachment {
666 pub id: String,
668 pub filename: String,
670 pub mime_type: String,
672 pub size: u64,
674 pub content_url: String,
676}
677
678impl From<JiraAttachmentEntry> for JiraAttachment {
679 fn from(entry: JiraAttachmentEntry) -> Self {
680 Self {
681 id: entry.id,
682 filename: entry.filename,
683 mime_type: entry.mime_type,
684 size: entry.size,
685 content_url: entry.content,
686 }
687 }
688}
689
690#[derive(Debug, Clone, Serialize)]
694pub struct JiraTransition {
695 pub id: String,
697 pub name: String,
699 #[serde(skip_serializing_if = "Option::is_none")]
701 pub to_status: Option<JiraTransitionToStatus>,
702 #[serde(skip_serializing_if = "Option::is_none")]
704 pub has_screen: Option<bool>,
705}
706
707#[derive(Debug, Clone, Serialize)]
710pub struct JiraTransitionToStatus {
711 pub id: String,
713 pub name: String,
715 #[serde(skip_serializing_if = "Option::is_none")]
717 pub category: Option<String>,
718}
719
720#[derive(Debug, Clone, Serialize)]
723pub struct JiraDevPullRequest {
724 pub id: String,
726 pub name: String,
728 pub status: String,
730 pub url: String,
732 pub repository_name: String,
734 pub source_branch: String,
736 pub destination_branch: String,
738 #[serde(skip_serializing_if = "Option::is_none")]
740 pub author: Option<String>,
741 #[serde(skip_serializing_if = "Vec::is_empty")]
743 pub reviewers: Vec<String>,
744 #[serde(skip_serializing_if = "Option::is_none")]
746 pub comment_count: Option<u32>,
747 #[serde(skip_serializing_if = "Option::is_none")]
749 pub last_update: Option<String>,
750}
751
752#[derive(Debug, Clone, Serialize)]
756pub struct JiraDevCommit {
757 pub id: String,
759 pub display_id: String,
761 pub message: String,
763 #[serde(skip_serializing_if = "Option::is_none")]
765 pub author: Option<String>,
766 #[serde(skip_serializing_if = "Option::is_none")]
768 pub timestamp: Option<String>,
769 pub url: String,
771 pub file_count: u32,
773 pub merge: bool,
775}
776
777#[derive(Debug, Clone, Serialize)]
780pub struct JiraDevBranch {
781 pub name: String,
783 pub url: String,
785 pub repository_name: String,
787 #[serde(skip_serializing_if = "Option::is_none")]
789 pub create_pr_url: Option<String>,
790 #[serde(skip_serializing_if = "Option::is_none")]
792 pub last_commit: Option<JiraDevCommit>,
793}
794
795#[derive(Debug, Clone, Serialize)]
798pub struct JiraDevRepository {
799 pub name: String,
801 pub url: String,
803 #[serde(skip_serializing_if = "Vec::is_empty")]
805 pub commits: Vec<JiraDevCommit>,
806}
807
808#[derive(Debug, Clone, Serialize)]
814pub struct JiraDevStatus {
815 #[serde(skip_serializing_if = "Vec::is_empty")]
817 pub pull_requests: Vec<JiraDevPullRequest>,
818 #[serde(skip_serializing_if = "Vec::is_empty")]
820 pub branches: Vec<JiraDevBranch>,
821 #[serde(skip_serializing_if = "Vec::is_empty")]
823 pub repositories: Vec<JiraDevRepository>,
824}
825
826#[derive(Debug, Clone, Serialize)]
830pub struct JiraDevStatusCount {
831 pub count: u32,
833 pub providers: Vec<JiraDevProvider>,
835}
836
837#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
840pub struct JiraDevProvider {
841 pub instance_type: String,
845 pub name: String,
847}
848
849#[derive(Debug, Clone, Serialize)]
853pub struct JiraDevStatusSummary {
854 pub pullrequest: JiraDevStatusCount,
856 pub branch: JiraDevStatusCount,
858 pub repository: JiraDevStatusCount,
860}
861
862#[derive(Debug, Clone, Serialize)]
865pub struct JiraWorklog {
866 pub id: String,
868 pub author: String,
870 pub time_spent: String,
872 pub time_spent_seconds: u64,
874 pub started: String,
876 #[serde(skip_serializing_if = "Option::is_none")]
878 pub comment: Option<String>,
879}
880
881#[derive(Debug, Clone, Serialize)]
884pub struct JiraWorklogList {
885 pub worklogs: Vec<JiraWorklog>,
887 pub total: u32,
889}
890
891#[derive(Deserialize)]
894pub(crate) struct JiraIssueResponse {
895 pub(crate) key: String,
896 pub(crate) fields: JiraIssueFields,
897}
898
899#[derive(Deserialize)]
902pub(crate) struct JiraIssueEnvelope {
903 pub(crate) key: String,
904 #[serde(default)]
905 pub(crate) fields: std::collections::BTreeMap<String, serde_json::Value>,
906 #[serde(default)]
907 pub(crate) names: std::collections::BTreeMap<String, String>,
908}
909
910impl JiraIssueEnvelope {
911 pub(crate) fn into_issue(self, selection: &FieldSelection) -> JiraIssue {
912 let Self {
913 key,
914 mut fields,
915 names,
916 } = self;
917
918 let description_adf = fields.remove("description").filter(|v| !v.is_null());
919 let summary = fields
920 .remove("summary")
921 .and_then(|v| v.as_str().map(str::to_string))
922 .unwrap_or_default();
923 let status = extract_named_field(fields.remove("status"));
924 let issue_type = extract_named_field(fields.remove("issuetype"));
925 let assignee = extract_display_name(fields.remove("assignee"));
926 let priority = extract_named_field(fields.remove("priority"));
927 let labels = fields
928 .remove("labels")
929 .and_then(|v| serde_json::from_value::<Vec<String>>(v).ok())
930 .unwrap_or_default();
931
932 let collect_customs = !matches!(selection, FieldSelection::Standard);
933 let custom_fields = if collect_customs {
934 fields
935 .into_iter()
936 .filter(|(_, value)| !value.is_null())
937 .map(|(id, value)| {
938 let name = names.get(&id).cloned().unwrap_or_else(|| id.clone());
939 JiraCustomField { id, name, value }
940 })
941 .collect()
942 } else {
943 Vec::new()
944 };
945
946 JiraIssue {
947 key,
948 summary,
949 description_adf,
950 status,
951 issue_type,
952 assignee,
953 priority,
954 labels,
955 custom_fields,
956 }
957 }
958}
959
960fn extract_named_field(value: Option<serde_json::Value>) -> Option<String> {
961 value
962 .and_then(|v| v.get("name").cloned())
963 .and_then(|n| n.as_str().map(str::to_string))
964}
965
966fn extract_display_name(value: Option<serde_json::Value>) -> Option<String> {
967 value
968 .and_then(|v| v.get("displayName").cloned())
969 .and_then(|n| n.as_str().map(str::to_string))
970}
971
972#[derive(Deserialize)]
973pub(crate) struct JiraEditMetaResponse {
974 #[serde(default)]
975 pub(crate) fields: std::collections::BTreeMap<String, JiraEditMetaField>,
976}
977
978#[derive(Deserialize)]
979pub(crate) struct JiraEditMetaField {
980 #[serde(default)]
981 pub(crate) name: Option<String>,
982 #[serde(default)]
983 pub(crate) schema: Option<JiraEditMetaSchemaRaw>,
984 #[serde(rename = "allowedValues", default)]
985 pub(crate) allowed_values: Vec<JiraAllowedValueRaw>,
986}
987
988impl JiraEditMetaField {
989 pub(crate) fn allowed_value_strings(&self) -> Vec<String> {
993 self.allowed_values
994 .iter()
995 .filter_map(|v| v.value.clone().or_else(|| v.name.clone()))
996 .collect()
997 }
998}
999
1000#[derive(Deserialize)]
1001pub(crate) struct JiraEditMetaSchemaRaw {
1002 #[serde(rename = "type", default)]
1003 pub(crate) kind: Option<String>,
1004 #[serde(default)]
1005 pub(crate) custom: Option<String>,
1006 #[serde(default)]
1007 pub(crate) items: Option<String>,
1008 #[serde(default)]
1009 pub(crate) system: Option<String>,
1010}
1011
1012impl From<Option<JiraEditMetaSchemaRaw>> for EditMetaSchema {
1013 fn from(raw: Option<JiraEditMetaSchemaRaw>) -> Self {
1014 raw.map_or_else(Self::default, |s| Self {
1015 kind: s.kind.unwrap_or_default(),
1016 custom: s.custom,
1017 items: s.items,
1018 system: s.system,
1019 })
1020 }
1021}
1022
1023#[derive(Deserialize)]
1024pub(crate) struct JiraCreateMetaResponse {
1025 #[serde(default)]
1026 pub(crate) projects: Vec<JiraCreateMetaProject>,
1027}
1028
1029#[derive(Deserialize)]
1030pub(crate) struct JiraCreateMetaProject {
1031 #[serde(default)]
1032 pub(crate) issuetypes: Vec<JiraCreateMetaIssueType>,
1033}
1034
1035#[derive(Deserialize)]
1036pub(crate) struct JiraCreateMetaIssueType {
1037 #[serde(default)]
1038 pub(crate) fields: std::collections::BTreeMap<String, JiraEditMetaField>,
1039}
1040
1041#[derive(Deserialize)]
1045pub(crate) struct JiraCreateMetaFullResponse {
1046 #[serde(default)]
1047 pub(crate) projects: Vec<JiraCreateMetaFullProject>,
1048}
1049
1050#[derive(Deserialize)]
1051pub(crate) struct JiraCreateMetaFullProject {
1052 #[serde(default)]
1053 pub(crate) issuetypes: Vec<JiraCreateMetaFullIssueType>,
1054}
1055
1056#[derive(Deserialize)]
1057pub(crate) struct JiraCreateMetaFullIssueType {
1058 #[serde(default)]
1059 pub(crate) fields: std::collections::BTreeMap<String, JiraCreateMetaFieldRaw>,
1060}
1061
1062#[derive(Deserialize)]
1063pub(crate) struct JiraCreateMetaFieldRaw {
1064 #[serde(default)]
1065 pub(crate) name: Option<String>,
1066 #[serde(default)]
1067 pub(crate) required: bool,
1068 #[serde(default)]
1069 pub(crate) schema: Option<JiraCreateMetaSchemaRaw>,
1070 #[serde(rename = "allowedValues", default)]
1071 pub(crate) allowed_values: Vec<JiraAllowedValueRaw>,
1072 #[serde(rename = "defaultValue", default)]
1073 pub(crate) default_value: Option<serde_json::Value>,
1074}
1075
1076#[derive(Deserialize)]
1077pub(crate) struct JiraCreateMetaSchemaRaw {
1078 #[serde(rename = "type", default)]
1079 pub(crate) kind: Option<String>,
1080 #[serde(default)]
1081 pub(crate) items: Option<String>,
1082 #[serde(default)]
1083 pub(crate) custom: Option<String>,
1084}
1085
1086#[derive(Deserialize)]
1087pub(crate) struct JiraAllowedValueRaw {
1088 #[serde(default)]
1089 pub(crate) id: Option<String>,
1090 #[serde(default)]
1091 pub(crate) value: Option<String>,
1092 #[serde(default)]
1093 pub(crate) name: Option<String>,
1094 #[serde(default)]
1095 pub(crate) children: Vec<Self>,
1096}
1097
1098impl JiraAllowedValueRaw {
1099 pub(crate) fn into_allowed_value(self) -> CreateMetaAllowedValue {
1102 CreateMetaAllowedValue {
1103 id: self.id,
1104 value: self.value.or(self.name),
1105 children: self
1106 .children
1107 .into_iter()
1108 .map(Self::into_allowed_value)
1109 .collect(),
1110 }
1111 }
1112}
1113
1114#[derive(Deserialize)]
1115pub(crate) struct JiraIssueFields {
1116 pub(crate) summary: Option<String>,
1117 pub(crate) description: Option<serde_json::Value>,
1118 pub(crate) status: Option<JiraNameField>,
1119 pub(crate) issuetype: Option<JiraNameField>,
1120 pub(crate) assignee: Option<JiraAssigneeField>,
1121 pub(crate) priority: Option<JiraNameField>,
1122 #[serde(default)]
1123 pub(crate) labels: Vec<String>,
1124}
1125
1126#[derive(Deserialize)]
1127pub(crate) struct JiraNameField {
1128 pub(crate) name: Option<String>,
1129}
1130
1131#[derive(Deserialize)]
1132pub(crate) struct JiraAssigneeField {
1133 #[serde(rename = "displayName")]
1134 pub(crate) display_name: Option<String>,
1135}
1136
1137#[derive(Deserialize)]
1138#[allow(dead_code)]
1139pub(crate) struct JiraSearchResponse {
1140 pub(crate) issues: Vec<JiraIssueResponse>,
1141 #[serde(default)]
1142 pub(crate) total: u32,
1143 #[serde(rename = "nextPageToken", default)]
1144 pub(crate) next_page_token: Option<String>,
1145}
1146
1147#[derive(Deserialize)]
1148pub(crate) struct JiraTransitionsResponse {
1149 pub(crate) transitions: Vec<JiraTransitionEntry>,
1150}
1151
1152#[derive(Deserialize)]
1153pub(crate) struct JiraTransitionEntry {
1154 pub(crate) id: String,
1155 pub(crate) name: String,
1156 #[serde(default)]
1157 pub(crate) to: Option<JiraTransitionToEntry>,
1158 #[serde(rename = "hasScreen", default)]
1159 pub(crate) has_screen: Option<bool>,
1160}
1161
1162#[derive(Deserialize)]
1163pub(crate) struct JiraTransitionToEntry {
1164 pub(crate) id: String,
1165 pub(crate) name: String,
1166 #[serde(rename = "statusCategory", default)]
1167 pub(crate) status_category: Option<JiraStatusCategoryEntry>,
1168}
1169
1170#[derive(Deserialize)]
1171pub(crate) struct JiraStatusCategoryEntry {
1172 #[serde(default)]
1173 pub(crate) key: Option<String>,
1174}
1175
1176#[derive(Deserialize)]
1177pub(crate) struct JiraCommentsResponse {
1178 #[serde(default)]
1179 pub(crate) comments: Vec<JiraCommentEntry>,
1180 #[serde(default)]
1181 pub(crate) total: u32,
1182 #[serde(rename = "startAt", default)]
1183 pub(crate) start_at: u32,
1184 #[serde(rename = "maxResults", default)]
1185 #[allow(dead_code)]
1186 pub(crate) max_results: u32,
1187}
1188
1189#[derive(Deserialize)]
1190pub(crate) struct JiraCommentEntry {
1191 pub(crate) id: String,
1192 pub(crate) author: Option<JiraCommentAuthor>,
1193 pub(crate) body: Option<serde_json::Value>,
1194 pub(crate) created: Option<String>,
1195 #[serde(default)]
1196 pub(crate) updated: Option<String>,
1197}
1198
1199#[derive(Deserialize)]
1200pub(crate) struct JiraCommentAuthor {
1201 #[serde(rename = "displayName")]
1202 pub(crate) display_name: Option<String>,
1203}
1204
1205#[derive(Deserialize)]
1206pub(crate) struct JiraWorklogResponse {
1207 #[serde(default)]
1208 pub(crate) worklogs: Vec<JiraWorklogEntry>,
1209 #[serde(default)]
1210 pub(crate) total: u32,
1211}
1212
1213#[derive(Deserialize)]
1214pub(crate) struct JiraWorklogEntry {
1215 pub(crate) id: String,
1216 pub(crate) author: Option<JiraCommentAuthor>,
1217 #[serde(rename = "timeSpent")]
1218 pub(crate) time_spent: Option<String>,
1219 #[serde(rename = "timeSpentSeconds", default)]
1220 pub(crate) time_spent_seconds: u64,
1221 pub(crate) started: Option<String>,
1222 pub(crate) comment: Option<serde_json::Value>,
1223}
1224
1225#[derive(Deserialize)]
1228pub(crate) struct JiraUserSearchEntry {
1229 #[serde(rename = "accountId")]
1230 pub(crate) account_id: String,
1231 #[serde(rename = "displayName", default)]
1232 pub(crate) display_name: Option<String>,
1233 #[serde(rename = "emailAddress", default)]
1234 pub(crate) email_address: Option<String>,
1235 #[serde(default)]
1236 pub(crate) active: bool,
1237 #[serde(rename = "accountType", default)]
1238 pub(crate) account_type: Option<String>,
1239}
1240
1241#[derive(Deserialize)]
1244#[allow(dead_code)]
1245pub(crate) struct AgileBoardListResponse {
1246 pub(crate) values: Vec<AgileBoardEntry>,
1247 #[serde(default)]
1248 pub(crate) total: u32,
1249 #[serde(rename = "isLast", default)]
1250 pub(crate) is_last: bool,
1251}
1252
1253#[derive(Deserialize)]
1254pub(crate) struct AgileBoardEntry {
1255 pub(crate) id: u64,
1256 pub(crate) name: String,
1257 #[serde(rename = "type")]
1258 pub(crate) board_type: String,
1259 pub(crate) location: Option<AgileBoardLocation>,
1260}
1261
1262#[derive(Deserialize)]
1263pub(crate) struct AgileBoardLocation {
1264 #[serde(rename = "projectKey")]
1265 pub(crate) project_key: Option<String>,
1266}
1267
1268#[derive(Deserialize)]
1269#[allow(dead_code)]
1270pub(crate) struct AgileIssueListResponse {
1271 pub(crate) issues: Vec<JiraIssueResponse>,
1272 #[serde(default)]
1273 pub(crate) total: u32,
1274 #[serde(rename = "isLast", default)]
1275 pub(crate) is_last: bool,
1276}
1277
1278#[derive(Deserialize)]
1279#[allow(dead_code)]
1280pub(crate) struct AgileSprintListResponse {
1281 pub(crate) values: Vec<AgileSprintEntry>,
1282 #[serde(default)]
1283 pub(crate) total: u32,
1284 #[serde(rename = "isLast", default)]
1285 pub(crate) is_last: bool,
1286}
1287
1288#[derive(Deserialize)]
1289pub(crate) struct AgileSprintEntry {
1290 pub(crate) id: u64,
1291 pub(crate) name: String,
1292 pub(crate) state: String,
1293 #[serde(rename = "startDate")]
1294 pub(crate) start_date: Option<String>,
1295 #[serde(rename = "endDate")]
1296 pub(crate) end_date: Option<String>,
1297 pub(crate) goal: Option<String>,
1298}
1299
1300#[derive(Deserialize)]
1301pub(crate) struct JiraProjectVersionEntry {
1302 pub(crate) id: String,
1303 pub(crate) name: String,
1304 #[serde(default)]
1305 pub(crate) description: Option<String>,
1306 #[serde(default)]
1307 pub(crate) released: bool,
1308 #[serde(default)]
1309 pub(crate) archived: bool,
1310 #[serde(rename = "releaseDate", default)]
1311 pub(crate) release_date: Option<String>,
1312 #[serde(rename = "startDate", default)]
1313 pub(crate) start_date: Option<String>,
1314}
1315
1316#[derive(Deserialize)]
1317pub(crate) struct JiraIssueLinksResponse {
1318 pub(crate) fields: JiraIssueLinksFields,
1319}
1320
1321#[derive(Deserialize)]
1322pub(crate) struct JiraIssueLinksFields {
1323 #[serde(default)]
1324 pub(crate) issuelinks: Vec<JiraIssueLinkEntry>,
1325}
1326
1327#[derive(Deserialize)]
1328pub(crate) struct JiraIssueLinkEntry {
1329 pub(crate) id: String,
1330 #[serde(rename = "type")]
1331 pub(crate) link_type: JiraIssueLinkType,
1332 #[serde(rename = "inwardIssue")]
1333 pub(crate) inward_issue: Option<JiraIssueLinkIssue>,
1334 #[serde(rename = "outwardIssue")]
1335 pub(crate) outward_issue: Option<JiraIssueLinkIssue>,
1336}
1337
1338#[derive(Deserialize)]
1339pub(crate) struct JiraIssueLinkType {
1340 pub(crate) name: String,
1341}
1342
1343#[derive(Deserialize)]
1344pub(crate) struct JiraIssueLinkIssue {
1345 pub(crate) key: String,
1346 pub(crate) fields: Option<JiraIssueLinkIssueFields>,
1347}
1348
1349#[derive(Deserialize)]
1350pub(crate) struct JiraIssueLinkIssueFields {
1351 pub(crate) summary: Option<String>,
1352}
1353
1354#[derive(Deserialize)]
1355pub(crate) struct JiraRemoteIssueLinkEntry {
1356 pub(crate) id: serde_json::Value,
1357 #[serde(rename = "globalId", default)]
1358 pub(crate) global_id: Option<String>,
1359 #[serde(default)]
1360 pub(crate) relationship: Option<String>,
1361 pub(crate) object: JiraRemoteIssueLinkObjectEntry,
1362}
1363
1364#[derive(Deserialize)]
1365pub(crate) struct JiraRemoteIssueLinkObjectEntry {
1366 pub(crate) url: String,
1367 #[serde(default)]
1368 pub(crate) title: Option<String>,
1369 #[serde(default)]
1370 pub(crate) summary: Option<String>,
1371 #[serde(default)]
1372 pub(crate) icon: Option<JiraRemoteIssueLinkIconEntry>,
1373}
1374
1375#[derive(Deserialize)]
1376pub(crate) struct JiraRemoteIssueLinkIconEntry {
1377 #[serde(rename = "url16x16", default)]
1378 pub(crate) url: Option<String>,
1379 #[serde(default)]
1380 pub(crate) title: Option<String>,
1381}
1382
1383#[derive(Deserialize)]
1384pub(crate) struct JiraLinkTypesResponse {
1385 #[serde(rename = "issueLinkTypes")]
1386 pub(crate) issue_link_types: Vec<JiraLinkTypeEntry>,
1387}
1388
1389#[derive(Deserialize)]
1390pub(crate) struct JiraLinkTypeEntry {
1391 pub(crate) id: String,
1392 pub(crate) name: String,
1393 pub(crate) inward: String,
1394 pub(crate) outward: String,
1395}
1396
1397#[derive(Deserialize)]
1398pub(crate) struct JiraAttachmentIssueResponse {
1399 pub(crate) fields: JiraAttachmentFields,
1400}
1401
1402#[derive(Deserialize)]
1403pub(crate) struct JiraAttachmentFields {
1404 #[serde(default)]
1405 pub(crate) attachment: Vec<JiraAttachmentEntry>,
1406}
1407
1408#[derive(Deserialize)]
1409pub(crate) struct JiraAttachmentEntry {
1410 pub(crate) id: String,
1411 pub(crate) filename: String,
1412 #[serde(rename = "mimeType")]
1413 pub(crate) mime_type: String,
1414 pub(crate) size: u64,
1415 pub(crate) content: String,
1416}
1417
1418#[derive(Deserialize)]
1419#[allow(dead_code)]
1420pub(crate) struct JiraChangelogResponse {
1421 pub(crate) values: Vec<JiraChangelogEntryResponse>,
1422 #[serde(default)]
1423 pub(crate) total: u32,
1424 #[serde(rename = "isLast", default)]
1425 pub(crate) is_last: bool,
1426}
1427
1428#[derive(Deserialize)]
1429pub(crate) struct JiraChangelogEntryResponse {
1430 pub(crate) id: String,
1431 pub(crate) author: Option<JiraCommentAuthor>,
1432 pub(crate) created: Option<String>,
1433 #[serde(default)]
1434 pub(crate) items: Vec<JiraChangelogItemResponse>,
1435}
1436
1437#[derive(Deserialize)]
1438pub(crate) struct JiraChangelogItemResponse {
1439 pub(crate) field: String,
1440 #[serde(rename = "fromString")]
1441 pub(crate) from_string: Option<String>,
1442 #[serde(rename = "toString")]
1443 pub(crate) to_string: Option<String>,
1444}
1445
1446#[derive(Deserialize)]
1447pub(crate) struct JiraFieldEntry {
1448 pub(crate) id: String,
1449 pub(crate) name: String,
1450 #[serde(default)]
1451 pub(crate) custom: bool,
1452 pub(crate) schema: Option<JiraFieldSchema>,
1453}
1454
1455#[derive(Deserialize)]
1456pub(crate) struct JiraFieldSchema {
1457 #[serde(rename = "type")]
1458 pub(crate) schema_type: Option<String>,
1459 pub(crate) custom: Option<String>,
1460}
1461
1462#[derive(Deserialize)]
1463pub(crate) struct JiraFieldContextsResponse {
1464 pub(crate) values: Vec<JiraFieldContextEntry>,
1465}
1466
1467#[derive(Deserialize)]
1468pub(crate) struct JiraFieldContextEntry {
1469 pub(crate) id: String,
1470}
1471
1472#[derive(Deserialize)]
1473pub(crate) struct JiraFieldOptionsResponse {
1474 pub(crate) values: Vec<JiraFieldOptionEntry>,
1475}
1476
1477#[derive(Deserialize)]
1478pub(crate) struct JiraFieldOptionEntry {
1479 pub(crate) id: String,
1480 pub(crate) value: String,
1481}
1482
1483#[derive(Deserialize)]
1484#[allow(dead_code)]
1485pub(crate) struct JiraProjectSearchResponse {
1486 pub(crate) values: Vec<JiraProjectEntry>,
1487 pub(crate) total: u32,
1488 #[serde(rename = "isLast", default)]
1489 pub(crate) is_last: bool,
1490}
1491
1492#[derive(Deserialize)]
1493pub(crate) struct JiraProjectEntry {
1494 pub(crate) id: String,
1495 pub(crate) key: String,
1496 pub(crate) name: String,
1497 #[serde(rename = "projectTypeKey")]
1498 pub(crate) project_type_key: Option<String>,
1499 pub(crate) lead: Option<JiraProjectLead>,
1500}
1501
1502#[derive(Deserialize)]
1503pub(crate) struct JiraProjectLead {
1504 #[serde(rename = "displayName")]
1505 pub(crate) display_name: Option<String>,
1506}
1507
1508#[derive(Deserialize)]
1509pub(crate) struct JiraCreateResponse {
1510 pub(crate) key: String,
1511 pub(crate) id: String,
1512 #[serde(rename = "self")]
1513 pub(crate) self_url: String,
1514}
1515
1516#[derive(Deserialize)]
1520pub(crate) struct JiraIssueIdResponse {
1521 pub(crate) id: String,
1522}
1523
1524#[derive(Deserialize)]
1525pub(crate) struct DevStatusResponse {
1526 #[serde(default)]
1527 pub(crate) detail: Vec<DevStatusDetail>,
1528}
1529
1530#[derive(Deserialize)]
1531pub(crate) struct DevStatusDetail {
1532 #[serde(rename = "pullRequests", default)]
1533 pub(crate) pull_requests: Vec<DevStatusPullRequest>,
1534 #[serde(default)]
1535 pub(crate) branches: Vec<DevStatusBranch>,
1536 #[serde(default)]
1537 pub(crate) repositories: Vec<DevStatusRepositoryEntry>,
1538}
1539
1540#[derive(Deserialize)]
1541pub(crate) struct DevStatusPullRequest {
1542 #[serde(default)]
1543 pub(crate) id: String,
1544 #[serde(default)]
1545 pub(crate) name: String,
1546 #[serde(default)]
1547 pub(crate) status: String,
1548 #[serde(default)]
1549 pub(crate) url: String,
1550 #[serde(rename = "repositoryName", default)]
1551 pub(crate) repository_name: String,
1552 #[serde(default)]
1553 pub(crate) source: Option<DevStatusBranchRef>,
1554 #[serde(default)]
1555 pub(crate) destination: Option<DevStatusBranchRef>,
1556 #[serde(default)]
1557 pub(crate) author: Option<DevStatusAuthor>,
1558 #[serde(default)]
1559 pub(crate) reviewers: Vec<DevStatusReviewer>,
1560 #[serde(rename = "commentCount", default)]
1561 pub(crate) comment_count: Option<u32>,
1562 #[serde(rename = "lastUpdate", default)]
1563 pub(crate) last_update: Option<String>,
1564}
1565
1566#[derive(Deserialize)]
1567pub(crate) struct DevStatusBranchRef {
1568 #[serde(default)]
1569 pub(crate) branch: String,
1570}
1571
1572#[derive(Deserialize)]
1573pub(crate) struct DevStatusAuthor {
1574 #[serde(default)]
1575 pub(crate) name: String,
1576}
1577
1578#[derive(Deserialize)]
1579pub(crate) struct DevStatusReviewer {
1580 #[serde(default)]
1581 pub(crate) name: String,
1582}
1583
1584#[derive(Deserialize)]
1585pub(crate) struct DevStatusCommit {
1586 #[serde(default)]
1587 pub(crate) id: String,
1588 #[serde(rename = "displayId", default)]
1589 pub(crate) display_id: String,
1590 #[serde(default)]
1591 pub(crate) message: String,
1592 #[serde(default)]
1593 pub(crate) author: Option<DevStatusAuthor>,
1594 #[serde(rename = "authorTimestamp", default)]
1595 pub(crate) author_timestamp: Option<String>,
1596 #[serde(default)]
1597 pub(crate) url: String,
1598 #[serde(rename = "fileCount", default)]
1599 pub(crate) file_count: u32,
1600 #[serde(default)]
1601 pub(crate) merge: bool,
1602}
1603
1604#[derive(Deserialize)]
1605pub(crate) struct DevStatusBranch {
1606 #[serde(default)]
1607 pub(crate) name: String,
1608 #[serde(default)]
1609 pub(crate) url: String,
1610 #[serde(rename = "repositoryName", default)]
1611 pub(crate) repository_name: String,
1612 #[serde(rename = "createPullRequestUrl", default)]
1613 pub(crate) create_pr_url: Option<String>,
1614 #[serde(rename = "lastCommit", default)]
1615 pub(crate) last_commit: Option<DevStatusCommit>,
1616}
1617
1618#[derive(Deserialize)]
1619pub(crate) struct DevStatusRepositoryEntry {
1620 #[serde(default)]
1621 pub(crate) name: String,
1622 #[serde(default)]
1623 pub(crate) url: String,
1624 #[serde(default)]
1625 pub(crate) commits: Vec<DevStatusCommit>,
1626}
1627
1628#[derive(Deserialize)]
1631pub(crate) struct DevStatusSummaryResponse {
1632 #[serde(default)]
1633 pub(crate) summary: DevStatusSummaryData,
1634}
1635
1636#[derive(Deserialize, Default)]
1637pub(crate) struct DevStatusSummaryData {
1638 #[serde(default)]
1639 pub(crate) pullrequest: Option<DevStatusSummaryCategory>,
1640 #[serde(default)]
1641 pub(crate) branch: Option<DevStatusSummaryCategory>,
1642 #[serde(default)]
1643 pub(crate) repository: Option<DevStatusSummaryCategory>,
1644}
1645
1646#[derive(Deserialize)]
1647pub(crate) struct DevStatusSummaryCategory {
1648 pub(crate) overall: Option<DevStatusSummaryOverall>,
1649 #[serde(rename = "byInstanceType", default)]
1652 pub(crate) by_instance_type: HashMap<String, serde_json::Value>,
1653}
1654
1655#[derive(Deserialize)]
1656pub(crate) struct DevStatusSummaryOverall {
1657 #[serde(default)]
1658 pub(crate) count: u32,
1659}