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
678#[derive(Debug, Clone, Serialize)]
682pub struct JiraTransition {
683 pub id: String,
685 pub name: String,
687 #[serde(skip_serializing_if = "Option::is_none")]
689 pub to_status: Option<JiraTransitionToStatus>,
690 #[serde(skip_serializing_if = "Option::is_none")]
692 pub has_screen: Option<bool>,
693}
694
695#[derive(Debug, Clone, Serialize)]
698pub struct JiraTransitionToStatus {
699 pub id: String,
701 pub name: String,
703 #[serde(skip_serializing_if = "Option::is_none")]
705 pub category: Option<String>,
706}
707
708#[derive(Debug, Clone, Serialize)]
711pub struct JiraDevPullRequest {
712 pub id: String,
714 pub name: String,
716 pub status: String,
718 pub url: String,
720 pub repository_name: String,
722 pub source_branch: String,
724 pub destination_branch: String,
726 #[serde(skip_serializing_if = "Option::is_none")]
728 pub author: Option<String>,
729 #[serde(skip_serializing_if = "Vec::is_empty")]
731 pub reviewers: Vec<String>,
732 #[serde(skip_serializing_if = "Option::is_none")]
734 pub comment_count: Option<u32>,
735 #[serde(skip_serializing_if = "Option::is_none")]
737 pub last_update: Option<String>,
738}
739
740#[derive(Debug, Clone, Serialize)]
744pub struct JiraDevCommit {
745 pub id: String,
747 pub display_id: String,
749 pub message: String,
751 #[serde(skip_serializing_if = "Option::is_none")]
753 pub author: Option<String>,
754 #[serde(skip_serializing_if = "Option::is_none")]
756 pub timestamp: Option<String>,
757 pub url: String,
759 pub file_count: u32,
761 pub merge: bool,
763}
764
765#[derive(Debug, Clone, Serialize)]
768pub struct JiraDevBranch {
769 pub name: String,
771 pub url: String,
773 pub repository_name: String,
775 #[serde(skip_serializing_if = "Option::is_none")]
777 pub create_pr_url: Option<String>,
778 #[serde(skip_serializing_if = "Option::is_none")]
780 pub last_commit: Option<JiraDevCommit>,
781}
782
783#[derive(Debug, Clone, Serialize)]
786pub struct JiraDevRepository {
787 pub name: String,
789 pub url: String,
791 #[serde(skip_serializing_if = "Vec::is_empty")]
793 pub commits: Vec<JiraDevCommit>,
794}
795
796#[derive(Debug, Clone, Serialize)]
802pub struct JiraDevStatus {
803 #[serde(skip_serializing_if = "Vec::is_empty")]
805 pub pull_requests: Vec<JiraDevPullRequest>,
806 #[serde(skip_serializing_if = "Vec::is_empty")]
808 pub branches: Vec<JiraDevBranch>,
809 #[serde(skip_serializing_if = "Vec::is_empty")]
811 pub repositories: Vec<JiraDevRepository>,
812}
813
814#[derive(Debug, Clone, Serialize)]
818pub struct JiraDevStatusCount {
819 pub count: u32,
821 pub providers: Vec<JiraDevProvider>,
823}
824
825#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
828pub struct JiraDevProvider {
829 pub instance_type: String,
833 pub name: String,
835}
836
837#[derive(Debug, Clone, Serialize)]
841pub struct JiraDevStatusSummary {
842 pub pullrequest: JiraDevStatusCount,
844 pub branch: JiraDevStatusCount,
846 pub repository: JiraDevStatusCount,
848}
849
850#[derive(Debug, Clone, Serialize)]
853pub struct JiraWorklog {
854 pub id: String,
856 pub author: String,
858 pub time_spent: String,
860 pub time_spent_seconds: u64,
862 pub started: String,
864 #[serde(skip_serializing_if = "Option::is_none")]
866 pub comment: Option<String>,
867}
868
869#[derive(Debug, Clone, Serialize)]
872pub struct JiraWorklogList {
873 pub worklogs: Vec<JiraWorklog>,
875 pub total: u32,
877}
878
879#[derive(Deserialize)]
882pub(crate) struct JiraIssueResponse {
883 pub(crate) key: String,
884 pub(crate) fields: JiraIssueFields,
885}
886
887#[derive(Deserialize)]
890pub(crate) struct JiraIssueEnvelope {
891 pub(crate) key: String,
892 #[serde(default)]
893 pub(crate) fields: std::collections::BTreeMap<String, serde_json::Value>,
894 #[serde(default)]
895 pub(crate) names: std::collections::BTreeMap<String, String>,
896}
897
898impl JiraIssueEnvelope {
899 pub(crate) fn into_issue(self, selection: &FieldSelection) -> JiraIssue {
900 let Self {
901 key,
902 mut fields,
903 names,
904 } = self;
905
906 let description_adf = fields.remove("description").filter(|v| !v.is_null());
907 let summary = fields
908 .remove("summary")
909 .and_then(|v| v.as_str().map(str::to_string))
910 .unwrap_or_default();
911 let status = extract_named_field(fields.remove("status"));
912 let issue_type = extract_named_field(fields.remove("issuetype"));
913 let assignee = extract_display_name(fields.remove("assignee"));
914 let priority = extract_named_field(fields.remove("priority"));
915 let labels = fields
916 .remove("labels")
917 .and_then(|v| serde_json::from_value::<Vec<String>>(v).ok())
918 .unwrap_or_default();
919
920 let collect_customs = !matches!(selection, FieldSelection::Standard);
921 let custom_fields = if collect_customs {
922 fields
923 .into_iter()
924 .filter(|(_, value)| !value.is_null())
925 .map(|(id, value)| {
926 let name = names.get(&id).cloned().unwrap_or_else(|| id.clone());
927 JiraCustomField { id, name, value }
928 })
929 .collect()
930 } else {
931 Vec::new()
932 };
933
934 JiraIssue {
935 key,
936 summary,
937 description_adf,
938 status,
939 issue_type,
940 assignee,
941 priority,
942 labels,
943 custom_fields,
944 }
945 }
946}
947
948fn extract_named_field(value: Option<serde_json::Value>) -> Option<String> {
949 value
950 .and_then(|v| v.get("name").cloned())
951 .and_then(|n| n.as_str().map(str::to_string))
952}
953
954fn extract_display_name(value: Option<serde_json::Value>) -> Option<String> {
955 value
956 .and_then(|v| v.get("displayName").cloned())
957 .and_then(|n| n.as_str().map(str::to_string))
958}
959
960#[derive(Deserialize)]
961pub(crate) struct JiraEditMetaResponse {
962 #[serde(default)]
963 pub(crate) fields: std::collections::BTreeMap<String, JiraEditMetaField>,
964}
965
966#[derive(Deserialize)]
967pub(crate) struct JiraEditMetaField {
968 #[serde(default)]
969 pub(crate) name: Option<String>,
970 #[serde(default)]
971 pub(crate) schema: Option<JiraEditMetaSchemaRaw>,
972 #[serde(rename = "allowedValues", default)]
973 pub(crate) allowed_values: Vec<JiraAllowedValueRaw>,
974}
975
976impl JiraEditMetaField {
977 pub(crate) fn allowed_value_strings(&self) -> Vec<String> {
981 self.allowed_values
982 .iter()
983 .filter_map(|v| v.value.clone().or_else(|| v.name.clone()))
984 .collect()
985 }
986}
987
988#[derive(Deserialize)]
989pub(crate) struct JiraEditMetaSchemaRaw {
990 #[serde(rename = "type", default)]
991 pub(crate) kind: Option<String>,
992 #[serde(default)]
993 pub(crate) custom: Option<String>,
994 #[serde(default)]
995 pub(crate) items: Option<String>,
996 #[serde(default)]
997 pub(crate) system: Option<String>,
998}
999
1000impl From<Option<JiraEditMetaSchemaRaw>> for EditMetaSchema {
1001 fn from(raw: Option<JiraEditMetaSchemaRaw>) -> Self {
1002 raw.map_or_else(Self::default, |s| Self {
1003 kind: s.kind.unwrap_or_default(),
1004 custom: s.custom,
1005 items: s.items,
1006 system: s.system,
1007 })
1008 }
1009}
1010
1011#[derive(Deserialize)]
1012pub(crate) struct JiraCreateMetaResponse {
1013 #[serde(default)]
1014 pub(crate) projects: Vec<JiraCreateMetaProject>,
1015}
1016
1017#[derive(Deserialize)]
1018pub(crate) struct JiraCreateMetaProject {
1019 #[serde(default)]
1020 pub(crate) issuetypes: Vec<JiraCreateMetaIssueType>,
1021}
1022
1023#[derive(Deserialize)]
1024pub(crate) struct JiraCreateMetaIssueType {
1025 #[serde(default)]
1026 pub(crate) fields: std::collections::BTreeMap<String, JiraEditMetaField>,
1027}
1028
1029#[derive(Deserialize)]
1033pub(crate) struct JiraCreateMetaFullResponse {
1034 #[serde(default)]
1035 pub(crate) projects: Vec<JiraCreateMetaFullProject>,
1036}
1037
1038#[derive(Deserialize)]
1039pub(crate) struct JiraCreateMetaFullProject {
1040 #[serde(default)]
1041 pub(crate) issuetypes: Vec<JiraCreateMetaFullIssueType>,
1042}
1043
1044#[derive(Deserialize)]
1045pub(crate) struct JiraCreateMetaFullIssueType {
1046 #[serde(default)]
1047 pub(crate) fields: std::collections::BTreeMap<String, JiraCreateMetaFieldRaw>,
1048}
1049
1050#[derive(Deserialize)]
1051pub(crate) struct JiraCreateMetaFieldRaw {
1052 #[serde(default)]
1053 pub(crate) name: Option<String>,
1054 #[serde(default)]
1055 pub(crate) required: bool,
1056 #[serde(default)]
1057 pub(crate) schema: Option<JiraCreateMetaSchemaRaw>,
1058 #[serde(rename = "allowedValues", default)]
1059 pub(crate) allowed_values: Vec<JiraAllowedValueRaw>,
1060 #[serde(rename = "defaultValue", default)]
1061 pub(crate) default_value: Option<serde_json::Value>,
1062}
1063
1064#[derive(Deserialize)]
1065pub(crate) struct JiraCreateMetaSchemaRaw {
1066 #[serde(rename = "type", default)]
1067 pub(crate) kind: Option<String>,
1068 #[serde(default)]
1069 pub(crate) items: Option<String>,
1070 #[serde(default)]
1071 pub(crate) custom: Option<String>,
1072}
1073
1074#[derive(Deserialize)]
1075pub(crate) struct JiraAllowedValueRaw {
1076 #[serde(default)]
1077 pub(crate) id: Option<String>,
1078 #[serde(default)]
1079 pub(crate) value: Option<String>,
1080 #[serde(default)]
1081 pub(crate) name: Option<String>,
1082 #[serde(default)]
1083 pub(crate) children: Vec<Self>,
1084}
1085
1086impl JiraAllowedValueRaw {
1087 pub(crate) fn into_allowed_value(self) -> CreateMetaAllowedValue {
1090 CreateMetaAllowedValue {
1091 id: self.id,
1092 value: self.value.or(self.name),
1093 children: self
1094 .children
1095 .into_iter()
1096 .map(Self::into_allowed_value)
1097 .collect(),
1098 }
1099 }
1100}
1101
1102#[derive(Deserialize)]
1103pub(crate) struct JiraIssueFields {
1104 pub(crate) summary: Option<String>,
1105 pub(crate) description: Option<serde_json::Value>,
1106 pub(crate) status: Option<JiraNameField>,
1107 pub(crate) issuetype: Option<JiraNameField>,
1108 pub(crate) assignee: Option<JiraAssigneeField>,
1109 pub(crate) priority: Option<JiraNameField>,
1110 #[serde(default)]
1111 pub(crate) labels: Vec<String>,
1112}
1113
1114#[derive(Deserialize)]
1115pub(crate) struct JiraNameField {
1116 pub(crate) name: Option<String>,
1117}
1118
1119#[derive(Deserialize)]
1120pub(crate) struct JiraAssigneeField {
1121 #[serde(rename = "displayName")]
1122 pub(crate) display_name: Option<String>,
1123}
1124
1125#[derive(Deserialize)]
1126#[allow(dead_code)]
1127pub(crate) struct JiraSearchResponse {
1128 pub(crate) issues: Vec<JiraIssueResponse>,
1129 #[serde(default)]
1130 pub(crate) total: u32,
1131 #[serde(rename = "nextPageToken", default)]
1132 pub(crate) next_page_token: Option<String>,
1133}
1134
1135#[derive(Deserialize)]
1136pub(crate) struct JiraTransitionsResponse {
1137 pub(crate) transitions: Vec<JiraTransitionEntry>,
1138}
1139
1140#[derive(Deserialize)]
1141pub(crate) struct JiraTransitionEntry {
1142 pub(crate) id: String,
1143 pub(crate) name: String,
1144 #[serde(default)]
1145 pub(crate) to: Option<JiraTransitionToEntry>,
1146 #[serde(rename = "hasScreen", default)]
1147 pub(crate) has_screen: Option<bool>,
1148}
1149
1150#[derive(Deserialize)]
1151pub(crate) struct JiraTransitionToEntry {
1152 pub(crate) id: String,
1153 pub(crate) name: String,
1154 #[serde(rename = "statusCategory", default)]
1155 pub(crate) status_category: Option<JiraStatusCategoryEntry>,
1156}
1157
1158#[derive(Deserialize)]
1159pub(crate) struct JiraStatusCategoryEntry {
1160 #[serde(default)]
1161 pub(crate) key: Option<String>,
1162}
1163
1164#[derive(Deserialize)]
1165pub(crate) struct JiraCommentsResponse {
1166 #[serde(default)]
1167 pub(crate) comments: Vec<JiraCommentEntry>,
1168 #[serde(default)]
1169 pub(crate) total: u32,
1170 #[serde(rename = "startAt", default)]
1171 pub(crate) start_at: u32,
1172 #[serde(rename = "maxResults", default)]
1173 #[allow(dead_code)]
1174 pub(crate) max_results: u32,
1175}
1176
1177#[derive(Deserialize)]
1178pub(crate) struct JiraCommentEntry {
1179 pub(crate) id: String,
1180 pub(crate) author: Option<JiraCommentAuthor>,
1181 pub(crate) body: Option<serde_json::Value>,
1182 pub(crate) created: Option<String>,
1183 #[serde(default)]
1184 pub(crate) updated: Option<String>,
1185}
1186
1187#[derive(Deserialize)]
1188pub(crate) struct JiraCommentAuthor {
1189 #[serde(rename = "displayName")]
1190 pub(crate) display_name: Option<String>,
1191}
1192
1193#[derive(Deserialize)]
1194pub(crate) struct JiraWorklogResponse {
1195 #[serde(default)]
1196 pub(crate) worklogs: Vec<JiraWorklogEntry>,
1197 #[serde(default)]
1198 pub(crate) total: u32,
1199}
1200
1201#[derive(Deserialize)]
1202pub(crate) struct JiraWorklogEntry {
1203 pub(crate) id: String,
1204 pub(crate) author: Option<JiraCommentAuthor>,
1205 #[serde(rename = "timeSpent")]
1206 pub(crate) time_spent: Option<String>,
1207 #[serde(rename = "timeSpentSeconds", default)]
1208 pub(crate) time_spent_seconds: u64,
1209 pub(crate) started: Option<String>,
1210 pub(crate) comment: Option<serde_json::Value>,
1211}
1212
1213#[derive(Deserialize)]
1216pub(crate) struct JiraUserSearchEntry {
1217 #[serde(rename = "accountId")]
1218 pub(crate) account_id: String,
1219 #[serde(rename = "displayName", default)]
1220 pub(crate) display_name: Option<String>,
1221 #[serde(rename = "emailAddress", default)]
1222 pub(crate) email_address: Option<String>,
1223 #[serde(default)]
1224 pub(crate) active: bool,
1225 #[serde(rename = "accountType", default)]
1226 pub(crate) account_type: Option<String>,
1227}
1228
1229#[derive(Deserialize)]
1232#[allow(dead_code)]
1233pub(crate) struct AgileBoardListResponse {
1234 pub(crate) values: Vec<AgileBoardEntry>,
1235 #[serde(default)]
1236 pub(crate) total: u32,
1237 #[serde(rename = "isLast", default)]
1238 pub(crate) is_last: bool,
1239}
1240
1241#[derive(Deserialize)]
1242pub(crate) struct AgileBoardEntry {
1243 pub(crate) id: u64,
1244 pub(crate) name: String,
1245 #[serde(rename = "type")]
1246 pub(crate) board_type: String,
1247 pub(crate) location: Option<AgileBoardLocation>,
1248}
1249
1250#[derive(Deserialize)]
1251pub(crate) struct AgileBoardLocation {
1252 #[serde(rename = "projectKey")]
1253 pub(crate) project_key: Option<String>,
1254}
1255
1256#[derive(Deserialize)]
1257#[allow(dead_code)]
1258pub(crate) struct AgileIssueListResponse {
1259 pub(crate) issues: Vec<JiraIssueResponse>,
1260 #[serde(default)]
1261 pub(crate) total: u32,
1262 #[serde(rename = "isLast", default)]
1263 pub(crate) is_last: bool,
1264}
1265
1266#[derive(Deserialize)]
1267#[allow(dead_code)]
1268pub(crate) struct AgileSprintListResponse {
1269 pub(crate) values: Vec<AgileSprintEntry>,
1270 #[serde(default)]
1271 pub(crate) total: u32,
1272 #[serde(rename = "isLast", default)]
1273 pub(crate) is_last: bool,
1274}
1275
1276#[derive(Deserialize)]
1277pub(crate) struct AgileSprintEntry {
1278 pub(crate) id: u64,
1279 pub(crate) name: String,
1280 pub(crate) state: String,
1281 #[serde(rename = "startDate")]
1282 pub(crate) start_date: Option<String>,
1283 #[serde(rename = "endDate")]
1284 pub(crate) end_date: Option<String>,
1285 pub(crate) goal: Option<String>,
1286}
1287
1288#[derive(Deserialize)]
1289pub(crate) struct JiraProjectVersionEntry {
1290 pub(crate) id: String,
1291 pub(crate) name: String,
1292 #[serde(default)]
1293 pub(crate) description: Option<String>,
1294 #[serde(default)]
1295 pub(crate) released: bool,
1296 #[serde(default)]
1297 pub(crate) archived: bool,
1298 #[serde(rename = "releaseDate", default)]
1299 pub(crate) release_date: Option<String>,
1300 #[serde(rename = "startDate", default)]
1301 pub(crate) start_date: Option<String>,
1302}
1303
1304#[derive(Deserialize)]
1305pub(crate) struct JiraIssueLinksResponse {
1306 pub(crate) fields: JiraIssueLinksFields,
1307}
1308
1309#[derive(Deserialize)]
1310pub(crate) struct JiraIssueLinksFields {
1311 #[serde(default)]
1312 pub(crate) issuelinks: Vec<JiraIssueLinkEntry>,
1313}
1314
1315#[derive(Deserialize)]
1316pub(crate) struct JiraIssueLinkEntry {
1317 pub(crate) id: String,
1318 #[serde(rename = "type")]
1319 pub(crate) link_type: JiraIssueLinkType,
1320 #[serde(rename = "inwardIssue")]
1321 pub(crate) inward_issue: Option<JiraIssueLinkIssue>,
1322 #[serde(rename = "outwardIssue")]
1323 pub(crate) outward_issue: Option<JiraIssueLinkIssue>,
1324}
1325
1326#[derive(Deserialize)]
1327pub(crate) struct JiraIssueLinkType {
1328 pub(crate) name: String,
1329}
1330
1331#[derive(Deserialize)]
1332pub(crate) struct JiraIssueLinkIssue {
1333 pub(crate) key: String,
1334 pub(crate) fields: Option<JiraIssueLinkIssueFields>,
1335}
1336
1337#[derive(Deserialize)]
1338pub(crate) struct JiraIssueLinkIssueFields {
1339 pub(crate) summary: Option<String>,
1340}
1341
1342#[derive(Deserialize)]
1343pub(crate) struct JiraRemoteIssueLinkEntry {
1344 pub(crate) id: serde_json::Value,
1345 #[serde(rename = "globalId", default)]
1346 pub(crate) global_id: Option<String>,
1347 #[serde(default)]
1348 pub(crate) relationship: Option<String>,
1349 pub(crate) object: JiraRemoteIssueLinkObjectEntry,
1350}
1351
1352#[derive(Deserialize)]
1353pub(crate) struct JiraRemoteIssueLinkObjectEntry {
1354 pub(crate) url: String,
1355 #[serde(default)]
1356 pub(crate) title: Option<String>,
1357 #[serde(default)]
1358 pub(crate) summary: Option<String>,
1359 #[serde(default)]
1360 pub(crate) icon: Option<JiraRemoteIssueLinkIconEntry>,
1361}
1362
1363#[derive(Deserialize)]
1364pub(crate) struct JiraRemoteIssueLinkIconEntry {
1365 #[serde(rename = "url16x16", default)]
1366 pub(crate) url: Option<String>,
1367 #[serde(default)]
1368 pub(crate) title: Option<String>,
1369}
1370
1371#[derive(Deserialize)]
1372pub(crate) struct JiraLinkTypesResponse {
1373 #[serde(rename = "issueLinkTypes")]
1374 pub(crate) issue_link_types: Vec<JiraLinkTypeEntry>,
1375}
1376
1377#[derive(Deserialize)]
1378pub(crate) struct JiraLinkTypeEntry {
1379 pub(crate) id: String,
1380 pub(crate) name: String,
1381 pub(crate) inward: String,
1382 pub(crate) outward: String,
1383}
1384
1385#[derive(Deserialize)]
1386pub(crate) struct JiraAttachmentIssueResponse {
1387 pub(crate) fields: JiraAttachmentFields,
1388}
1389
1390#[derive(Deserialize)]
1391pub(crate) struct JiraAttachmentFields {
1392 #[serde(default)]
1393 pub(crate) attachment: Vec<JiraAttachmentEntry>,
1394}
1395
1396#[derive(Deserialize)]
1397pub(crate) struct JiraAttachmentEntry {
1398 pub(crate) id: String,
1399 pub(crate) filename: String,
1400 #[serde(rename = "mimeType")]
1401 pub(crate) mime_type: String,
1402 pub(crate) size: u64,
1403 pub(crate) content: String,
1404}
1405
1406#[derive(Deserialize)]
1407#[allow(dead_code)]
1408pub(crate) struct JiraChangelogResponse {
1409 pub(crate) values: Vec<JiraChangelogEntryResponse>,
1410 #[serde(default)]
1411 pub(crate) total: u32,
1412 #[serde(rename = "isLast", default)]
1413 pub(crate) is_last: bool,
1414}
1415
1416#[derive(Deserialize)]
1417pub(crate) struct JiraChangelogEntryResponse {
1418 pub(crate) id: String,
1419 pub(crate) author: Option<JiraCommentAuthor>,
1420 pub(crate) created: Option<String>,
1421 #[serde(default)]
1422 pub(crate) items: Vec<JiraChangelogItemResponse>,
1423}
1424
1425#[derive(Deserialize)]
1426pub(crate) struct JiraChangelogItemResponse {
1427 pub(crate) field: String,
1428 #[serde(rename = "fromString")]
1429 pub(crate) from_string: Option<String>,
1430 #[serde(rename = "toString")]
1431 pub(crate) to_string: Option<String>,
1432}
1433
1434#[derive(Deserialize)]
1435pub(crate) struct JiraFieldEntry {
1436 pub(crate) id: String,
1437 pub(crate) name: String,
1438 #[serde(default)]
1439 pub(crate) custom: bool,
1440 pub(crate) schema: Option<JiraFieldSchema>,
1441}
1442
1443#[derive(Deserialize)]
1444pub(crate) struct JiraFieldSchema {
1445 #[serde(rename = "type")]
1446 pub(crate) schema_type: Option<String>,
1447 pub(crate) custom: Option<String>,
1448}
1449
1450#[derive(Deserialize)]
1451pub(crate) struct JiraFieldContextsResponse {
1452 pub(crate) values: Vec<JiraFieldContextEntry>,
1453}
1454
1455#[derive(Deserialize)]
1456pub(crate) struct JiraFieldContextEntry {
1457 pub(crate) id: String,
1458}
1459
1460#[derive(Deserialize)]
1461pub(crate) struct JiraFieldOptionsResponse {
1462 pub(crate) values: Vec<JiraFieldOptionEntry>,
1463}
1464
1465#[derive(Deserialize)]
1466pub(crate) struct JiraFieldOptionEntry {
1467 pub(crate) id: String,
1468 pub(crate) value: String,
1469}
1470
1471#[derive(Deserialize)]
1472#[allow(dead_code)]
1473pub(crate) struct JiraProjectSearchResponse {
1474 pub(crate) values: Vec<JiraProjectEntry>,
1475 pub(crate) total: u32,
1476 #[serde(rename = "isLast", default)]
1477 pub(crate) is_last: bool,
1478}
1479
1480#[derive(Deserialize)]
1481pub(crate) struct JiraProjectEntry {
1482 pub(crate) id: String,
1483 pub(crate) key: String,
1484 pub(crate) name: String,
1485 #[serde(rename = "projectTypeKey")]
1486 pub(crate) project_type_key: Option<String>,
1487 pub(crate) lead: Option<JiraProjectLead>,
1488}
1489
1490#[derive(Deserialize)]
1491pub(crate) struct JiraProjectLead {
1492 #[serde(rename = "displayName")]
1493 pub(crate) display_name: Option<String>,
1494}
1495
1496#[derive(Deserialize)]
1497pub(crate) struct JiraCreateResponse {
1498 pub(crate) key: String,
1499 pub(crate) id: String,
1500 #[serde(rename = "self")]
1501 pub(crate) self_url: String,
1502}
1503
1504#[derive(Deserialize)]
1508pub(crate) struct JiraIssueIdResponse {
1509 pub(crate) id: String,
1510}
1511
1512#[derive(Deserialize)]
1513pub(crate) struct DevStatusResponse {
1514 #[serde(default)]
1515 pub(crate) detail: Vec<DevStatusDetail>,
1516}
1517
1518#[derive(Deserialize)]
1519pub(crate) struct DevStatusDetail {
1520 #[serde(rename = "pullRequests", default)]
1521 pub(crate) pull_requests: Vec<DevStatusPullRequest>,
1522 #[serde(default)]
1523 pub(crate) branches: Vec<DevStatusBranch>,
1524 #[serde(default)]
1525 pub(crate) repositories: Vec<DevStatusRepositoryEntry>,
1526}
1527
1528#[derive(Deserialize)]
1529pub(crate) struct DevStatusPullRequest {
1530 #[serde(default)]
1531 pub(crate) id: String,
1532 #[serde(default)]
1533 pub(crate) name: String,
1534 #[serde(default)]
1535 pub(crate) status: String,
1536 #[serde(default)]
1537 pub(crate) url: String,
1538 #[serde(rename = "repositoryName", default)]
1539 pub(crate) repository_name: String,
1540 #[serde(default)]
1541 pub(crate) source: Option<DevStatusBranchRef>,
1542 #[serde(default)]
1543 pub(crate) destination: Option<DevStatusBranchRef>,
1544 #[serde(default)]
1545 pub(crate) author: Option<DevStatusAuthor>,
1546 #[serde(default)]
1547 pub(crate) reviewers: Vec<DevStatusReviewer>,
1548 #[serde(rename = "commentCount", default)]
1549 pub(crate) comment_count: Option<u32>,
1550 #[serde(rename = "lastUpdate", default)]
1551 pub(crate) last_update: Option<String>,
1552}
1553
1554#[derive(Deserialize)]
1555pub(crate) struct DevStatusBranchRef {
1556 #[serde(default)]
1557 pub(crate) branch: String,
1558}
1559
1560#[derive(Deserialize)]
1561pub(crate) struct DevStatusAuthor {
1562 #[serde(default)]
1563 pub(crate) name: String,
1564}
1565
1566#[derive(Deserialize)]
1567pub(crate) struct DevStatusReviewer {
1568 #[serde(default)]
1569 pub(crate) name: String,
1570}
1571
1572#[derive(Deserialize)]
1573pub(crate) struct DevStatusCommit {
1574 #[serde(default)]
1575 pub(crate) id: String,
1576 #[serde(rename = "displayId", default)]
1577 pub(crate) display_id: String,
1578 #[serde(default)]
1579 pub(crate) message: String,
1580 #[serde(default)]
1581 pub(crate) author: Option<DevStatusAuthor>,
1582 #[serde(rename = "authorTimestamp", default)]
1583 pub(crate) author_timestamp: Option<String>,
1584 #[serde(default)]
1585 pub(crate) url: String,
1586 #[serde(rename = "fileCount", default)]
1587 pub(crate) file_count: u32,
1588 #[serde(default)]
1589 pub(crate) merge: bool,
1590}
1591
1592#[derive(Deserialize)]
1593pub(crate) struct DevStatusBranch {
1594 #[serde(default)]
1595 pub(crate) name: String,
1596 #[serde(default)]
1597 pub(crate) url: String,
1598 #[serde(rename = "repositoryName", default)]
1599 pub(crate) repository_name: String,
1600 #[serde(rename = "createPullRequestUrl", default)]
1601 pub(crate) create_pr_url: Option<String>,
1602 #[serde(rename = "lastCommit", default)]
1603 pub(crate) last_commit: Option<DevStatusCommit>,
1604}
1605
1606#[derive(Deserialize)]
1607pub(crate) struct DevStatusRepositoryEntry {
1608 #[serde(default)]
1609 pub(crate) name: String,
1610 #[serde(default)]
1611 pub(crate) url: String,
1612 #[serde(default)]
1613 pub(crate) commits: Vec<DevStatusCommit>,
1614}
1615
1616#[derive(Deserialize)]
1619pub(crate) struct DevStatusSummaryResponse {
1620 #[serde(default)]
1621 pub(crate) summary: DevStatusSummaryData,
1622}
1623
1624#[derive(Deserialize, Default)]
1625pub(crate) struct DevStatusSummaryData {
1626 #[serde(default)]
1627 pub(crate) pullrequest: Option<DevStatusSummaryCategory>,
1628 #[serde(default)]
1629 pub(crate) branch: Option<DevStatusSummaryCategory>,
1630 #[serde(default)]
1631 pub(crate) repository: Option<DevStatusSummaryCategory>,
1632}
1633
1634#[derive(Deserialize)]
1635pub(crate) struct DevStatusSummaryCategory {
1636 pub(crate) overall: Option<DevStatusSummaryOverall>,
1637 #[serde(rename = "byInstanceType", default)]
1640 pub(crate) by_instance_type: HashMap<String, serde_json::Value>,
1641}
1642
1643#[derive(Deserialize)]
1644pub(crate) struct DevStatusSummaryOverall {
1645 #[serde(default)]
1646 pub(crate) count: u32,
1647}