rebuilderd_common/api/v1/
mod.rs1mod models;
2
3use crate::api::{Client, ZstdRequestBuilder};
4use crate::errors::*;
5use async_trait::async_trait;
6pub use models::*;
7use std::borrow::Cow;
8
9#[cfg(feature = "diesel")]
10use diesel::{
11 deserialize::FromSql,
12 serialize::{IsNull, Output, ToSql},
13 sql_types::Integer,
14 sqlite::{Sqlite, SqliteValue},
15 {AsExpression, FromSqlRow},
16};
17use serde::{Deserialize, Serialize};
18
19#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Copy, Clone)]
43#[cfg_attr(feature = "diesel", derive(FromSqlRow, AsExpression))]
44#[cfg_attr(feature = "diesel", diesel(sql_type = Integer))]
45#[cfg_attr(feature = "diesel", diesel(check_for_backend(diesel::sqlite::Sqlite)))]
46pub struct Priority(i32);
47
48impl Priority {
49 const DEFAULT_QUEUE_PRIORITY: i32 = 1;
52
53 const DEFAULT_RETRY_PRIORITY: i32 = Self::DEFAULT_QUEUE_PRIORITY + 1;
56
57 const DEFAULT_MANUAL_PRIORITY: i32 = Self::DEFAULT_QUEUE_PRIORITY - 1;
60
61 pub fn retry() -> Self {
62 Priority(Self::DEFAULT_RETRY_PRIORITY)
63 }
64
65 pub fn manual() -> Self {
66 Priority(Self::DEFAULT_MANUAL_PRIORITY)
67 }
68}
69
70impl Default for Priority {
71 fn default() -> Self {
72 Priority(Self::DEFAULT_QUEUE_PRIORITY)
73 }
74}
75
76#[cfg(feature = "diesel")]
77impl FromSql<Integer, Sqlite> for Priority {
78 fn from_sql(bytes: SqliteValue) -> diesel::deserialize::Result<Self> {
79 let value = <i32 as FromSql<Integer, Sqlite>>::from_sql(bytes)?;
80 Ok(Priority(value))
81 }
82}
83
84#[cfg(feature = "diesel")]
85impl ToSql<Integer, Sqlite> for Priority {
86 fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Sqlite>) -> diesel::serialize::Result {
87 out.set_value(self.0);
88 Ok(IsNull::No)
89 }
90}
91
92impl From<i32> for Priority {
93 fn from(value: i32) -> Self {
94 Priority(value)
95 }
96}
97
98#[async_trait]
99pub trait BuildRestApi {
100 async fn get_builds(
101 &self,
102 page: Option<&Page>,
103 origin_filter: Option<&OriginFilter>,
104 source_identity_filter: Option<&SourceIdentityFilter>,
105 ) -> Result<ResultPage<Rebuild>>;
106
107 async fn submit_build_report(&self, request: RebuildReport) -> Result<()>;
108 async fn get_build(&self, id: i32) -> Result<Rebuild>;
109 async fn get_build_log(&self, id: i32) -> Result<String>;
110 async fn get_build_artifacts(&self, id: i32) -> Result<Vec<RebuildArtifact>>;
111 async fn get_build_artifact(&self, id: i32, artifact_id: i32) -> Result<RebuildArtifact>;
112 async fn get_build_artifact_diffoscope(&self, id: i32, artifact_id: i32) -> Result<String>;
113 async fn get_build_artifact_attestation(&self, id: i32, artifact_id: i32) -> Result<Vec<u8>>;
114}
115
116#[async_trait]
117pub trait DashboardRestApi {
118 async fn get_dashboard(&self, origin_filter: Option<&OriginFilter>) -> Result<DashboardState>;
119}
120
121#[async_trait]
122pub trait MetaRestApi {
123 async fn get_distributions(&self) -> Result<Vec<String>>;
124 async fn get_distribution_releases(&self, distribution: &str) -> Result<Vec<String>>;
125 async fn get_distribution_architectures(&self, distribution: &str) -> Result<Vec<String>>;
126 async fn get_distribution_release_architectures(
127 &self,
128 distribution: &str,
129 release: &str,
130 ) -> Result<Vec<String>>;
131
132 async fn get_public_keys(&self) -> Result<PublicKey>;
133}
134
135#[async_trait]
136pub trait PackageRestApi {
137 async fn submit_package_report(&self, report: &PackageReport) -> Result<()>;
138
139 async fn get_source_packages(
140 &self,
141 page: Option<&Page>,
142 origin_filter: Option<&OriginFilter>,
143 source_identity_filter: Option<&SourceIdentityFilter>,
144 ) -> Result<ResultPage<SourcePackage>>;
145
146 async fn get_source_package(&self, id: i32) -> Result<SourcePackage>;
147
148 async fn get_binary_packages(
149 &self,
150 page: Option<&Page>,
151 origin_filter: Option<&OriginFilter>,
152 binary_identity_filter: Option<&BinaryIdentityFilter>,
153 ) -> Result<ResultPage<BinaryPackage>>;
154
155 async fn get_binary_package(&self, id: i32) -> Result<BinaryPackage>;
156}
157
158#[async_trait]
159pub trait QueueRestApi {
160 async fn get_queued_jobs(
161 &self,
162 page: Option<&Page>,
163 origin_filter: Option<&OriginFilter>,
164 source_identity_filter: Option<&SourceIdentityFilter>,
165 ) -> Result<ResultPage<QueuedJob>>;
166
167 async fn request_rebuild(&self, request: QueueJobRequest) -> Result<()>;
168 async fn get_queued_job(&self, id: i32) -> Result<QueuedJob>;
169 async fn drop_queued_job(&self, id: i32) -> Result<()>;
170 async fn drop_queued_jobs(
171 &self,
172 origin_filter: Option<&OriginFilter>,
173 source_identity_filter: Option<&SourceIdentityFilter>,
174 ) -> Result<()>;
175 async fn request_work(&self, request: PopQueuedJobRequest) -> Result<JobAssignment>;
176 async fn ping_job(&self, id: i32) -> Result<()>;
177}
178
179#[async_trait]
180pub trait WorkerRestApi {
181 async fn get_workers(&self, page: Option<&Page>) -> Result<ResultPage<Worker>>;
182 async fn register_worker(&self, request: RegisterWorkerRequest) -> Result<()>;
183 async fn get_worker(&self, id: i32) -> Result<Worker>;
184 async fn unregister_worker(&self, id: i32) -> Result<()>;
185}
186
187#[async_trait]
188impl BuildRestApi for Client {
189 async fn get_builds(
190 &self,
191 page: Option<&Page>,
192 origin_filter: Option<&OriginFilter>,
193 source_identity_filter: Option<&SourceIdentityFilter>,
194 ) -> Result<ResultPage<Rebuild>> {
195 let records = self
196 .get(Cow::Borrowed("api/v1/builds"))
197 .query(&page)
198 .query(&origin_filter)
199 .query(&source_identity_filter)
200 .send()
201 .await?
202 .error_for_status()?
203 .json()
204 .await?;
205
206 Ok(records)
207 }
208
209 async fn submit_build_report(&self, request: RebuildReport) -> Result<()> {
210 self.post(Cow::Borrowed("api/v1/builds"))
211 .json(&request)
212 .send_encoded()
213 .await?
214 .error_for_status()?;
215
216 Ok(())
217 }
218
219 async fn get_build(&self, id: i32) -> Result<Rebuild> {
220 let record = self
221 .get(Cow::Owned(format!("api/v1/builds/{id}")))
222 .send()
223 .await?
224 .error_for_status()?
225 .json()
226 .await?;
227
228 Ok(record)
229 }
230
231 async fn get_build_log(&self, id: i32) -> Result<String> {
232 let data = self
233 .get(Cow::Owned(format!("api/v1/builds/{id}/log")))
234 .send()
235 .await?
236 .error_for_status()?
237 .text()
238 .await?;
239
240 Ok(data)
241 }
242
243 async fn get_build_artifacts(&self, id: i32) -> Result<Vec<RebuildArtifact>> {
244 let records = self
245 .get(Cow::Owned(format!("api/v1/builds/{id}/artifacts")))
246 .send()
247 .await?
248 .error_for_status()?
249 .json()
250 .await?;
251
252 Ok(records)
253 }
254
255 async fn get_build_artifact(&self, id: i32, artifact_id: i32) -> Result<RebuildArtifact> {
256 let record = self
257 .get(Cow::Owned(format!(
258 "api/v1/builds/{id}/artifacts/{artifact_id}"
259 )))
260 .send()
261 .await?
262 .error_for_status()?
263 .json()
264 .await?;
265
266 Ok(record)
267 }
268
269 async fn get_build_artifact_diffoscope(&self, id: i32, artifact_id: i32) -> Result<String> {
270 let data = self
271 .get(Cow::Owned(format!(
272 "api/v1/builds/{id}/artifacts/{artifact_id}/diffoscope"
273 )))
274 .send()
275 .await?
276 .error_for_status()?
277 .text()
278 .await?;
279
280 Ok(data)
281 }
282
283 async fn get_build_artifact_attestation(&self, id: i32, artifact_id: i32) -> Result<Vec<u8>> {
284 let data = self
285 .get(Cow::Owned(format!(
286 "api/v1/builds/{id}/artifacts/{artifact_id}/attestation"
287 )))
288 .send()
289 .await?
290 .error_for_status()?
291 .bytes()
292 .await?;
293
294 Ok(Vec::from(data))
295 }
296}
297
298#[async_trait]
299impl DashboardRestApi for Client {
300 async fn get_dashboard(&self, origin_filter: Option<&OriginFilter>) -> Result<DashboardState> {
301 let dashboard = self
302 .get(Cow::Borrowed("api/v1/dashboard"))
303 .query(&origin_filter)
304 .send()
305 .await?
306 .error_for_status()?
307 .json()
308 .await?;
309
310 Ok(dashboard)
311 }
312}
313
314#[async_trait]
315impl MetaRestApi for Client {
316 async fn get_distributions(&self) -> Result<Vec<String>> {
317 let results = self
318 .get(Cow::Borrowed("api/v1/meta/distributions"))
319 .send()
320 .await?
321 .error_for_status()?
322 .json()
323 .await?;
324
325 Ok(results)
326 }
327
328 async fn get_distribution_releases(&self, distribution: &str) -> Result<Vec<String>> {
329 let results = self
330 .get(Cow::Owned(format!(
331 "api/v1/meta/distributions/{distribution}/releases"
332 )))
333 .send()
334 .await?
335 .error_for_status()?
336 .json()
337 .await?;
338
339 Ok(results)
340 }
341
342 async fn get_distribution_architectures(&self, distribution: &str) -> Result<Vec<String>> {
343 let results = self
344 .get(Cow::Owned(format!(
345 "api/v1/meta/distributions/{distribution}/architectures"
346 )))
347 .send()
348 .await?
349 .error_for_status()?
350 .json()
351 .await?;
352
353 Ok(results)
354 }
355
356 async fn get_distribution_release_architectures(
357 &self,
358 distribution: &str,
359 release: &str,
360 ) -> Result<Vec<String>> {
361 let results = self
362 .get(Cow::Owned(format!(
363 "api/v1/meta/distributions/{distribution}/releases/{release}/architectures"
364 )))
365 .send()
366 .await?
367 .error_for_status()?
368 .json()
369 .await?;
370
371 Ok(results)
372 }
373
374 async fn get_public_keys(&self) -> Result<PublicKey> {
375 let public_key = self
376 .get(Cow::Borrowed("api/v1/meta/public-keys"))
377 .send()
378 .await?
379 .error_for_status()?
380 .json()
381 .await?;
382
383 Ok(public_key)
384 }
385}
386
387#[async_trait]
388impl PackageRestApi for Client {
389 async fn submit_package_report(&self, report: &PackageReport) -> Result<()> {
390 self.post(Cow::Borrowed("api/v1/packages"))
391 .json(report)
392 .send_encoded()
393 .await?
394 .error_for_status()?;
395
396 Ok(())
397 }
398
399 async fn get_source_packages(
400 &self,
401 page: Option<&Page>,
402 origin_filter: Option<&OriginFilter>,
403 source_identity_filter: Option<&SourceIdentityFilter>,
404 ) -> Result<ResultPage<SourcePackage>> {
405 let records = self
406 .get(Cow::Borrowed("api/v1/packages/source"))
407 .query(&page)
408 .query(&origin_filter)
409 .query(&source_identity_filter)
410 .send()
411 .await?
412 .error_for_status()?
413 .json()
414 .await?;
415
416 Ok(records)
417 }
418
419 async fn get_source_package(&self, id: i32) -> Result<SourcePackage> {
420 let record = self
421 .get(Cow::Owned(format!("api/v1/packages/source/{id}")))
422 .send()
423 .await?
424 .error_for_status()?
425 .json()
426 .await?;
427
428 Ok(record)
429 }
430
431 async fn get_binary_packages(
432 &self,
433 page: Option<&Page>,
434 origin_filter: Option<&OriginFilter>,
435 binary_identity_filter: Option<&BinaryIdentityFilter>,
436 ) -> Result<ResultPage<BinaryPackage>> {
437 let records = self
438 .get(Cow::Borrowed("api/v1/packages/binary"))
439 .query(&page)
440 .query(&origin_filter)
441 .query(&binary_identity_filter)
442 .send()
443 .await?
444 .error_for_status()?
445 .json()
446 .await?;
447
448 Ok(records)
449 }
450
451 async fn get_binary_package(&self, id: i32) -> Result<BinaryPackage> {
452 let record = self
453 .get(Cow::Owned(format!("api/v1/packages/binary/{id}")))
454 .send()
455 .await?
456 .error_for_status()?
457 .json()
458 .await?;
459
460 Ok(record)
461 }
462}
463
464#[async_trait]
465impl QueueRestApi for Client {
466 async fn get_queued_jobs(
467 &self,
468 page: Option<&Page>,
469 origin_filter: Option<&OriginFilter>,
470 source_identity_filter: Option<&SourceIdentityFilter>,
471 ) -> Result<ResultPage<QueuedJob>> {
472 let records = self
473 .get(Cow::Borrowed("api/v1/queue"))
474 .query(&page)
475 .query(&origin_filter)
476 .query(&source_identity_filter)
477 .send()
478 .await?
479 .error_for_status()?
480 .json()
481 .await?;
482
483 Ok(records)
484 }
485
486 async fn request_rebuild(&self, request: QueueJobRequest) -> Result<()> {
487 self.post(Cow::Borrowed("api/v1/queue"))
488 .json(&request)
489 .send_encoded()
490 .await?
491 .error_for_status()?;
492
493 Ok(())
494 }
495
496 async fn get_queued_job(&self, id: i32) -> Result<QueuedJob> {
497 let record = self
498 .get(Cow::Owned(format!("api/v1/queue/{id}")))
499 .send()
500 .await?
501 .error_for_status()?
502 .json()
503 .await?;
504
505 Ok(record)
506 }
507
508 async fn drop_queued_job(&self, id: i32) -> Result<()> {
509 self.delete(Cow::Owned(format!("api/v1/queue/{id}")))
510 .send()
511 .await?
512 .error_for_status()?;
513
514 Ok(())
515 }
516
517 async fn drop_queued_jobs(
518 &self,
519 origin_filter: Option<&OriginFilter>,
520 source_identity_filter: Option<&SourceIdentityFilter>,
521 ) -> Result<()> {
522 self.delete(Cow::Borrowed("api/v1/queue"))
523 .query(&origin_filter)
524 .query(&source_identity_filter)
525 .send()
526 .await?
527 .error_for_status()?;
528
529 Ok(())
530 }
531
532 async fn request_work(&self, request: PopQueuedJobRequest) -> Result<JobAssignment> {
533 let record = self
534 .post(Cow::Borrowed("api/v1/queue/pop"))
535 .json(&request)
536 .send_encoded()
537 .await?
538 .error_for_status()?
539 .json()
540 .await?;
541
542 Ok(record)
543 }
544
545 async fn ping_job(&self, id: i32) -> Result<()> {
546 self.post(Cow::Owned(format!("api/v1/queue/{id}/ping")))
548 .header("Content-Length", 0)
549 .send()
550 .await?
551 .error_for_status()?;
552
553 Ok(())
554 }
555}
556
557#[async_trait]
558impl WorkerRestApi for Client {
559 async fn get_workers(&self, page: Option<&Page>) -> Result<ResultPage<Worker>> {
560 let workers = self
561 .get(Cow::Borrowed("api/v1/workers"))
562 .query(&page)
563 .send()
564 .await?
565 .error_for_status()?
566 .json()
567 .await?;
568
569 Ok(workers)
570 }
571
572 async fn register_worker(&self, request: RegisterWorkerRequest) -> Result<()> {
573 self.post(Cow::Borrowed("api/v1/workers"))
574 .json(&request)
575 .send_encoded()
576 .await?
577 .error_for_status()?;
578
579 Ok(())
580 }
581
582 async fn get_worker(&self, id: i32) -> Result<Worker> {
583 let worker = self
584 .get(Cow::Owned(format!("api/v1/workers/{id}")))
585 .send()
586 .await?
587 .error_for_status()?
588 .json()
589 .await?;
590
591 Ok(worker)
592 }
593
594 async fn unregister_worker(&self, id: i32) -> Result<()> {
595 self.delete(Cow::Owned(format!("api/v1/workers/{id}")))
596 .send()
597 .await?
598 .error_for_status()?;
599
600 Ok(())
601 }
602}