ora_client/
job_query.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
//! Query filters for jobs.

use ora_proto::server::v1::{self, JobExecutionStatus, JobQueryOrder};
use uuid::Uuid;

use crate::{job_definition::JobStatus, IndexSet, JobType};

/// A filter for querying jobs.
#[derive(Debug, Clone, Default)]
#[must_use]
pub struct JobFilter {
    /// The job IDs to filter by.
    ///
    /// If the list is empty, all jobs are included.
    pub job_ids: IndexSet<Uuid>,
    /// The job type IDs to filter by.
    ///
    /// If the list is empty, all job types are included.
    pub job_type_ids: IndexSet<String>,
    /// The execution IDs to filter by.
    ///
    /// If the list is empty, all executors are included.
    pub execution_ids: IndexSet<Uuid>,
    /// The schedule IDs to filter by.
    pub schedule_ids: IndexSet<Uuid>,
    /// A list of execution statuses to filter by.
    /// If the list is empty, all statuses are included.
    ///
    /// If a job has multiple executions, the last
    /// execution status is used.
    ///
    /// If a job has no executions, its status is
    /// considered to be pending.
    pub status: IndexSet<JobStatus>,
    /// A list of labels to filter by.
    ///
    /// If multiple filters are specified, all of them
    /// must match.
    pub labels: Vec<JobLabelFilter>,
    /// Only include active or inactive jobs.
    ///
    /// If not provided, all jobs are included.
    pub active: Option<bool>,
}

impl JobFilter {
    /// Create a new job filter that includes all jobs.
    pub fn new() -> Self {
        Self::default()
    }

    /// Filter by a specific job ID.
    pub fn with_job_id(mut self, job_id: Uuid) -> Self {
        self.job_ids.insert(job_id);
        self
    }

    /// Filter by specific job IDs.
    pub fn with_job_ids(mut self, job_ids: impl IntoIterator<Item = Uuid>) -> Self {
        self.job_ids.extend(job_ids);
        self
    }

    /// Filter by a specific job type ID.
    pub fn with_job_type_id(mut self, job_type_id: impl Into<String>) -> Self {
        self.job_type_ids.insert(job_type_id.into());
        self
    }

    /// Filter by specific job type IDs.
    pub fn with_job_type_ids(mut self, job_type_ids: impl IntoIterator<Item = String>) -> Self {
        self.job_type_ids.extend(job_type_ids);
        self
    }

    /// Filter by a specific execution ID.
    pub fn with_execution_id(mut self, execution_id: Uuid) -> Self {
        self.execution_ids.insert(execution_id);
        self
    }

    /// Filter by specific execution IDs.
    pub fn with_execution_ids(mut self, execution_ids: impl IntoIterator<Item = Uuid>) -> Self {
        self.execution_ids.extend(execution_ids);
        self
    }

    /// Filter by a specific schedule ID.
    pub fn with_schedule_id(mut self, schedule_id: Uuid) -> Self {
        self.schedule_ids.insert(schedule_id);
        self
    }

    /// Filter by specific schedule IDs.
    pub fn with_schedule_ids(mut self, schedule_ids: impl IntoIterator<Item = Uuid>) -> Self {
        self.schedule_ids.extend(schedule_ids);
        self
    }

    /// Filter by a specific status.
    pub fn include_status(mut self, status: JobStatus) -> Self {
        self.status.insert(status);
        self
    }

    /// Include pending jobs.
    pub fn include_pending(mut self) -> Self {
        self.status.insert(JobStatus::Pending);
        self
    }

    /// Include ready jobs.
    pub fn include_ready(mut self) -> Self {
        self.status.insert(JobStatus::Ready);
        self
    }

    /// Include assigned jobs.
    pub fn include_assigned(mut self) -> Self {
        self.status.insert(JobStatus::Assigned);
        self
    }

    /// Include running jobs.
    pub fn include_running(mut self) -> Self {
        self.status.insert(JobStatus::Running);
        self
    }

    /// Include successful jobs.
    pub fn include_succeeded(mut self) -> Self {
        self.status.insert(JobStatus::Succeeded);
        self
    }

    /// Include failed jobs.
    pub fn include_failed(mut self) -> Self {
        self.status.insert(JobStatus::Failed);
        self
    }

    /// Filter by active status.
    pub fn active_only(mut self) -> Self {
        self.active = Some(true);
        self
    }

    /// Filter by inactive status.
    pub fn inactive_only(mut self) -> Self {
        self.active = Some(false);
        self
    }

    /// Filter by a label.
    pub fn with_label_value(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
        self.labels.push(JobLabelFilter {
            key: key.into(),
            value: JobLabelFilterValue::Equals(value.into()),
        });
        self
    }

    /// Filter by a label that must exist.
    pub fn with_label(mut self, key: impl Into<String>) -> Self {
        self.labels.push(JobLabelFilter {
            key: key.into(),
            value: JobLabelFilterValue::Exists,
        });
        self
    }

    /// Include a given job type.
    pub fn include_job_type<J>(self) -> Self
    where
        J: JobType,
    {
        self.with_job_type_id(J::id())
    }
}

impl From<JobFilter> for v1::JobQueryFilter {
    fn from(filter: JobFilter) -> Self {
        Self {
            job_ids: filter
                .job_ids
                .into_iter()
                .map(|id| id.to_string())
                .collect(),
            job_type_ids: filter
                .job_type_ids
                .into_iter()
                .map(|id| id.to_string())
                .collect(),
            execution_ids: filter
                .execution_ids
                .into_iter()
                .map(|id| id.to_string())
                .collect(),
            schedule_ids: filter
                .schedule_ids
                .into_iter()
                .map(|id| id.to_string())
                .collect(),
            status: filter
                .status
                .into_iter()
                .map(|s| JobExecutionStatus::from(s).into())
                .collect(),
            labels: filter.labels.into_iter().map(Into::into).collect(),
            active: filter.active,
        }
    }
}

/// A filter for querying jobs by label.
#[derive(Debug, Clone)]
pub struct JobLabelFilter {
    /// The key of the label.
    pub key: String,
    /// The condition for the label value.
    pub value: JobLabelFilterValue,
}

/// The condition for a label filter.
#[derive(Debug, Clone)]
pub enum JobLabelFilterValue {
    /// Any label value must exist with the key.
    Exists,
    /// The label value must be equal to the provided value.
    Equals(String),
}

impl From<JobLabelFilter> for v1::JobLabelFilter {
    fn from(filter: JobLabelFilter) -> Self {
        Self {
            key: filter.key,
            value: match filter.value {
                JobLabelFilterValue::Exists => Some(v1::job_label_filter::Value::Exists(
                    v1::LabelFilterExistCondition::Exists.into(),
                )),
                JobLabelFilterValue::Equals(value) => {
                    Some(v1::job_label_filter::Value::Equals(value))
                }
            },
        }
    }
}

/// The order of jobs returned in a query.
#[derive(Debug, Default, Clone, Copy)]
pub enum JobOrder {
    /// Order by the time the job was created in ascending order.
    CreatedAtAsc,
    /// Order by the time the job was created in descending order.
    #[default]
    CreatedAtDesc,
    /// Order by the target execution time in ascending order.
    TargetExecutionTimeAsc,
    /// Order by the target execution time in descending order.
    TargetExecutionTimeDesc,
}

impl From<JobOrder> for JobQueryOrder {
    fn from(value: JobOrder) -> Self {
        match value {
            JobOrder::CreatedAtAsc => Self::CreatedAtAsc,
            JobOrder::CreatedAtDesc => Self::CreatedAtDesc,
            JobOrder::TargetExecutionTimeAsc => Self::TargetExecutionTimeAsc,
            JobOrder::TargetExecutionTimeDesc => Self::TargetExecutionTimeDesc,
        }
    }
}