ora_client/
schedule_query.rs

1//! Query filters for schedules.
2
3use std::time::SystemTime;
4
5use ora_proto::{
6    common::v1::TimeRange,
7    server::v1::{self, ScheduleQueryOrder},
8};
9use uuid::Uuid;
10
11use crate::{IndexSet, JobType};
12
13/// A filter for querying schedules.
14#[derive(Debug, Clone, Default)]
15#[must_use]
16pub struct ScheduleFilter {
17    /// The schedule IDs to filter by.
18    pub schedule_ids: IndexSet<Uuid>,
19    /// The job IDs to filter by.
20    ///
21    /// If the list is empty, all schedules are included.
22    pub job_ids: IndexSet<Uuid>,
23    /// The job type IDs to filter by.
24    ///
25    /// If the list is empty, all job types are included.
26    pub job_type_ids: IndexSet<String>,
27    /// A list of labels to filter by.
28    ///
29    /// If multiple filters are specified, all of them
30    /// must match.
31    pub labels: Vec<ScheduleLabelFilter>,
32    /// Only include active or inactive schedules.
33    ///
34    /// If not provided, all schedules are included.
35    pub active: Option<bool>,
36    /// Only include schedules created after the provided time.
37    ///
38    /// The time is inclusive.
39    pub created_after: Option<SystemTime>,
40    /// Only include schedules created before the provided time.
41    ///
42    /// The time is exclusive.
43    pub created_before: Option<SystemTime>,
44}
45
46impl ScheduleFilter {
47    /// Create a new job filter that includes all schedules.
48    pub fn new() -> Self {
49        Self::default()
50    }
51
52    /// Filter by a specific job ID.
53    pub fn with_job_id(mut self, job_id: Uuid) -> Self {
54        self.job_ids.insert(job_id);
55        self
56    }
57
58    /// Filter by specific job IDs.
59    pub fn with_job_ids(mut self, job_ids: impl IntoIterator<Item = Uuid>) -> Self {
60        self.job_ids.extend(job_ids);
61        self
62    }
63
64    /// Filter by a specific job type ID.
65    pub fn with_job_type_id(mut self, job_type_id: impl Into<String>) -> Self {
66        self.job_type_ids.insert(job_type_id.into());
67        self
68    }
69
70    /// Filter by specific job type IDs.
71    pub fn with_job_type_ids(mut self, job_type_ids: impl IntoIterator<Item = String>) -> Self {
72        self.job_type_ids.extend(job_type_ids);
73        self
74    }
75
76    /// Filter by a specific schedule ID.
77    pub fn with_schedule_id(mut self, schedule_id: Uuid) -> Self {
78        self.schedule_ids.insert(schedule_id);
79        self
80    }
81
82    /// Filter by specific schedule IDs.
83    pub fn with_schedule_ids(mut self, schedule_ids: impl IntoIterator<Item = Uuid>) -> Self {
84        self.schedule_ids.extend(schedule_ids);
85        self
86    }
87
88    /// Filter by active status.
89    pub fn active_only(mut self) -> Self {
90        self.active = Some(true);
91        self
92    }
93
94    /// Filter by inactive status.
95    pub fn inactive_only(mut self) -> Self {
96        self.active = Some(false);
97        self
98    }
99
100    /// Filter by a label.
101    pub fn with_label_value(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
102        self.labels.push(ScheduleLabelFilter {
103            key: key.into(),
104            value: ScheduleLabelFilterValue::Equals(value.into()),
105        });
106        self
107    }
108
109    /// Filter by a label that must exist.
110    pub fn with_label(mut self, key: impl Into<String>) -> Self {
111        self.labels.push(ScheduleLabelFilter {
112            key: key.into(),
113            value: ScheduleLabelFilterValue::Exists,
114        });
115        self
116    }
117
118    /// Filter by schedules created after the provided time.
119    ///
120    /// The time is inclusive.
121    pub fn created_after(mut self, time: SystemTime) -> Self {
122        self.created_after = Some(time);
123        self
124    }
125
126    /// Filter by schedules created before the provided time.
127    ///
128    /// The time is exclusive.
129    pub fn created_before(mut self, time: SystemTime) -> Self {
130        self.created_before = Some(time);
131        self
132    }
133
134    /// Filter by a job type.
135    pub fn include_job_type<J: JobType>(self) -> Self {
136        self.with_job_type_id(J::id())
137    }
138}
139
140impl From<ScheduleFilter> for v1::ScheduleQueryFilter {
141    fn from(filter: ScheduleFilter) -> Self {
142        Self {
143            job_ids: filter
144                .job_ids
145                .into_iter()
146                .map(|id| id.to_string())
147                .collect(),
148            job_type_ids: filter
149                .job_type_ids
150                .into_iter()
151                .map(|id| id.to_string())
152                .collect(),
153
154            schedule_ids: filter
155                .schedule_ids
156                .into_iter()
157                .map(|id| id.to_string())
158                .collect(),
159            labels: filter.labels.into_iter().map(Into::into).collect(),
160            active: filter.active,
161            created_at: Some(TimeRange {
162                start: filter.created_after.map(Into::into),
163                end: filter.created_before.map(Into::into),
164            }),
165        }
166    }
167}
168
169/// A label filter for schedules.
170#[derive(Debug, Clone)]
171pub struct ScheduleLabelFilter {
172    /// The key of the label.
173    pub key: String,
174    /// The condition for the label value.
175    pub value: ScheduleLabelFilterValue,
176}
177
178/// The condition for a label filter.
179#[derive(Debug, Clone)]
180pub enum ScheduleLabelFilterValue {
181    /// Any label value must exist with the key.
182    Exists,
183    /// The label value must be equal to the provided value.
184    Equals(String),
185}
186
187impl From<ScheduleLabelFilter> for v1::ScheduleLabelFilter {
188    fn from(filter: ScheduleLabelFilter) -> Self {
189        Self {
190            key: filter.key,
191            value: match filter.value {
192                ScheduleLabelFilterValue::Exists => Some(v1::schedule_label_filter::Value::Exists(
193                    v1::LabelFilterExistCondition::Exists.into(),
194                )),
195                ScheduleLabelFilterValue::Equals(value) => {
196                    Some(v1::schedule_label_filter::Value::Equals(value))
197                }
198            },
199        }
200    }
201}
202
203/// The order of schedules returned in a query.
204#[derive(Debug, Default, Clone, Copy)]
205pub enum ScheduleOrder {
206    /// Order by the time the job was created in ascending order.
207    CreatedAtAsc,
208    /// Order by the time the job was created in descending order.
209    #[default]
210    CreatedAtDesc,
211}
212
213impl From<ScheduleOrder> for ScheduleQueryOrder {
214    fn from(value: ScheduleOrder) -> Self {
215        match value {
216            ScheduleOrder::CreatedAtAsc => Self::CreatedAtAsc,
217            ScheduleOrder::CreatedAtDesc => Self::CreatedAtDesc,
218        }
219    }
220}