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_components(&self, distribution: &str) -> Result<Vec<String>>;
127 async fn get_distribution_release_architectures(
128 &self,
129 distribution: &str,
130 release: &str,
131 ) -> Result<Vec<String>>;
132
133 async fn get_distribution_release_components(
134 &self,
135 distribution: &str,
136 release: &str,
137 ) -> Result<Vec<String>>;
138
139 async fn get_distribution_release_component_architectures(
140 &self,
141 distribution: &str,
142 release: &str,
143 component: &str,
144 ) -> Result<Vec<String>>;
145
146 async fn get_public_keys(&self) -> Result<PublicKey>;
147}
148
149#[async_trait]
150pub trait PackageRestApi {
151 async fn submit_package_report(&self, report: &PackageReport) -> Result<()>;
152
153 async fn get_source_packages(
154 &self,
155 page: Option<&Page>,
156 origin_filter: Option<&OriginFilter>,
157 source_identity_filter: Option<&SourceIdentityFilter>,
158 ) -> Result<ResultPage<SourcePackage>>;
159
160 async fn get_source_package(&self, id: i32) -> Result<SourcePackage>;
161
162 async fn get_binary_packages(
163 &self,
164 page: Option<&Page>,
165 origin_filter: Option<&OriginFilter>,
166 binary_identity_filter: Option<&BinaryIdentityFilter>,
167 ) -> Result<ResultPage<BinaryPackage>>;
168
169 async fn get_binary_package(&self, id: i32) -> Result<BinaryPackage>;
170}
171
172#[async_trait]
173pub trait QueueRestApi {
174 async fn get_queued_jobs(
175 &self,
176 page: Option<&Page>,
177 origin_filter: Option<&OriginFilter>,
178 source_identity_filter: Option<&SourceIdentityFilter>,
179 ) -> Result<ResultPage<QueuedJob>>;
180
181 async fn request_rebuild(&self, request: QueueJobRequest) -> Result<()>;
182 async fn get_queued_job(&self, id: i32) -> Result<QueuedJob>;
183 async fn drop_queued_job(&self, id: i32) -> Result<()>;
184 async fn drop_queued_jobs(
185 &self,
186 origin_filter: Option<&OriginFilter>,
187 source_identity_filter: Option<&SourceIdentityFilter>,
188 ) -> Result<()>;
189 async fn request_work(&self, request: PopQueuedJobRequest) -> Result<JobAssignment>;
190 async fn ping_job(&self, id: i32) -> Result<()>;
191}
192
193#[async_trait]
194pub trait WorkerRestApi {
195 async fn get_workers(&self, page: Option<&Page>) -> Result<ResultPage<Worker>>;
196 async fn register_worker(&self, request: RegisterWorkerRequest) -> Result<()>;
197 async fn get_worker(&self, id: i32) -> Result<Worker>;
198 async fn unregister_worker(&self, id: i32) -> Result<()>;
199}
200
201#[async_trait]
202impl BuildRestApi for Client {
203 async fn get_builds(
204 &self,
205 page: Option<&Page>,
206 origin_filter: Option<&OriginFilter>,
207 source_identity_filter: Option<&SourceIdentityFilter>,
208 ) -> Result<ResultPage<Rebuild>> {
209 let records = self
210 .get(Cow::Borrowed("api/v1/builds"))
211 .query(&page)
212 .query(&origin_filter)
213 .query(&source_identity_filter)
214 .send()
215 .await?
216 .error_for_status()?
217 .json()
218 .await?;
219
220 Ok(records)
221 }
222
223 async fn submit_build_report(&self, request: RebuildReport) -> Result<()> {
224 self.post(Cow::Borrowed("api/v1/builds"))
225 .json(&request)
226 .send_encoded()
227 .await?
228 .error_for_status()?;
229
230 Ok(())
231 }
232
233 async fn get_build(&self, id: i32) -> Result<Rebuild> {
234 let record = self
235 .get(Cow::Owned(format!("api/v1/builds/{id}")))
236 .send()
237 .await?
238 .error_for_status()?
239 .json()
240 .await?;
241
242 Ok(record)
243 }
244
245 async fn get_build_log(&self, id: i32) -> Result<String> {
246 let data = self
247 .get(Cow::Owned(format!("api/v1/builds/{id}/log")))
248 .send()
249 .await?
250 .error_for_status()?
251 .text()
252 .await?;
253
254 Ok(data)
255 }
256
257 async fn get_build_artifacts(&self, id: i32) -> Result<Vec<RebuildArtifact>> {
258 let records = self
259 .get(Cow::Owned(format!("api/v1/builds/{id}/artifacts")))
260 .send()
261 .await?
262 .error_for_status()?
263 .json()
264 .await?;
265
266 Ok(records)
267 }
268
269 async fn get_build_artifact(&self, id: i32, artifact_id: i32) -> Result<RebuildArtifact> {
270 let record = self
271 .get(Cow::Owned(format!(
272 "api/v1/builds/{id}/artifacts/{artifact_id}"
273 )))
274 .send()
275 .await?
276 .error_for_status()?
277 .json()
278 .await?;
279
280 Ok(record)
281 }
282
283 async fn get_build_artifact_diffoscope(&self, id: i32, artifact_id: i32) -> Result<String> {
284 let data = self
285 .get(Cow::Owned(format!(
286 "api/v1/builds/{id}/artifacts/{artifact_id}/diffoscope"
287 )))
288 .send()
289 .await?
290 .error_for_status()?
291 .text()
292 .await?;
293
294 Ok(data)
295 }
296
297 async fn get_build_artifact_attestation(&self, id: i32, artifact_id: i32) -> Result<Vec<u8>> {
298 let data = self
299 .get(Cow::Owned(format!(
300 "api/v1/builds/{id}/artifacts/{artifact_id}/attestation"
301 )))
302 .send()
303 .await?
304 .error_for_status()?
305 .bytes()
306 .await?;
307
308 Ok(Vec::from(data))
309 }
310}
311
312#[async_trait]
313impl DashboardRestApi for Client {
314 async fn get_dashboard(&self, origin_filter: Option<&OriginFilter>) -> Result<DashboardState> {
315 let dashboard = self
316 .get(Cow::Borrowed("api/v1/dashboard"))
317 .query(&origin_filter)
318 .send()
319 .await?
320 .error_for_status()?
321 .json()
322 .await?;
323
324 Ok(dashboard)
325 }
326}
327
328#[async_trait]
329impl MetaRestApi for Client {
330 async fn get_distributions(&self) -> Result<Vec<String>> {
331 let results = self
332 .get(Cow::Borrowed("api/v1/meta/distributions"))
333 .send()
334 .await?
335 .error_for_status()?
336 .json()
337 .await?;
338
339 Ok(results)
340 }
341
342 async fn get_distribution_releases(&self, distribution: &str) -> Result<Vec<String>> {
343 let results = self
344 .get(Cow::Owned(format!(
345 "api/v1/meta/distributions/{distribution}/releases"
346 )))
347 .send()
348 .await?
349 .error_for_status()?
350 .json()
351 .await?;
352
353 Ok(results)
354 }
355
356 async fn get_distribution_architectures(&self, distribution: &str) -> Result<Vec<String>> {
357 let results = self
358 .get(Cow::Owned(format!(
359 "api/v1/meta/distributions/{distribution}/architectures"
360 )))
361 .send()
362 .await?
363 .error_for_status()?
364 .json()
365 .await?;
366
367 Ok(results)
368 }
369
370 async fn get_distribution_components(&self, distribution: &str) -> Result<Vec<String>> {
371 let results = self
372 .get(Cow::Owned(format!(
373 "api/v1/meta/distributions/{distribution}/components"
374 )))
375 .send()
376 .await?
377 .error_for_status()?
378 .json()
379 .await?;
380
381 Ok(results)
382 }
383
384 async fn get_distribution_release_architectures(
385 &self,
386 distribution: &str,
387 release: &str,
388 ) -> Result<Vec<String>> {
389 let results = self
390 .get(Cow::Owned(format!(
391 "api/v1/meta/distributions/{distribution}/releases/{release}/architectures"
392 )))
393 .send()
394 .await?
395 .error_for_status()?
396 .json()
397 .await?;
398
399 Ok(results)
400 }
401
402 async fn get_distribution_release_components(
403 &self,
404 distribution: &str,
405 release: &str,
406 ) -> Result<Vec<String>> {
407 let results = self
408 .get(Cow::Owned(format!(
409 "api/v1/meta/distributions/{distribution}/releases/{release}/components"
410 )))
411 .send()
412 .await?
413 .error_for_status()?
414 .json()
415 .await?;
416
417 Ok(results)
418 }
419
420 async fn get_distribution_release_component_architectures(
421 &self,
422 distribution: &str,
423 release: &str,
424 component: &str,
425 ) -> Result<Vec<String>> {
426 let results = self
427 .get(Cow::Owned(format!(
428 "api/v1/meta/distributions/{distribution}/releases/{release}/components/{component}/architectures"
429 )))
430 .send()
431 .await?
432 .error_for_status()?
433 .json()
434 .await?;
435
436 Ok(results)
437 }
438
439 async fn get_public_keys(&self) -> Result<PublicKey> {
440 let public_key = self
441 .get(Cow::Borrowed("api/v1/meta/public-keys"))
442 .send()
443 .await?
444 .error_for_status()?
445 .json()
446 .await?;
447
448 Ok(public_key)
449 }
450}
451
452#[async_trait]
453impl PackageRestApi for Client {
454 async fn submit_package_report(&self, report: &PackageReport) -> Result<()> {
455 self.post(Cow::Borrowed("api/v1/packages"))
456 .json(report)
457 .send_encoded()
458 .await?
459 .error_for_status()?;
460
461 Ok(())
462 }
463
464 async fn get_source_packages(
465 &self,
466 page: Option<&Page>,
467 origin_filter: Option<&OriginFilter>,
468 source_identity_filter: Option<&SourceIdentityFilter>,
469 ) -> Result<ResultPage<SourcePackage>> {
470 let records = self
471 .get(Cow::Borrowed("api/v1/packages/source"))
472 .query(&page)
473 .query(&origin_filter)
474 .query(&source_identity_filter)
475 .send()
476 .await?
477 .error_for_status()?
478 .json()
479 .await?;
480
481 Ok(records)
482 }
483
484 async fn get_source_package(&self, id: i32) -> Result<SourcePackage> {
485 let record = self
486 .get(Cow::Owned(format!("api/v1/packages/source/{id}")))
487 .send()
488 .await?
489 .error_for_status()?
490 .json()
491 .await?;
492
493 Ok(record)
494 }
495
496 async fn get_binary_packages(
497 &self,
498 page: Option<&Page>,
499 origin_filter: Option<&OriginFilter>,
500 binary_identity_filter: Option<&BinaryIdentityFilter>,
501 ) -> Result<ResultPage<BinaryPackage>> {
502 let records = self
503 .get(Cow::Borrowed("api/v1/packages/binary"))
504 .query(&page)
505 .query(&origin_filter)
506 .query(&binary_identity_filter)
507 .send()
508 .await?
509 .error_for_status()?
510 .json()
511 .await?;
512
513 Ok(records)
514 }
515
516 async fn get_binary_package(&self, id: i32) -> Result<BinaryPackage> {
517 let record = self
518 .get(Cow::Owned(format!("api/v1/packages/binary/{id}")))
519 .send()
520 .await?
521 .error_for_status()?
522 .json()
523 .await?;
524
525 Ok(record)
526 }
527}
528
529#[async_trait]
530impl QueueRestApi for Client {
531 async fn get_queued_jobs(
532 &self,
533 page: Option<&Page>,
534 origin_filter: Option<&OriginFilter>,
535 source_identity_filter: Option<&SourceIdentityFilter>,
536 ) -> Result<ResultPage<QueuedJob>> {
537 let records = self
538 .get(Cow::Borrowed("api/v1/queue"))
539 .query(&page)
540 .query(&origin_filter)
541 .query(&source_identity_filter)
542 .send()
543 .await?
544 .error_for_status()?
545 .json()
546 .await?;
547
548 Ok(records)
549 }
550
551 async fn request_rebuild(&self, request: QueueJobRequest) -> Result<()> {
552 self.post(Cow::Borrowed("api/v1/queue"))
553 .json(&request)
554 .send_encoded()
555 .await?
556 .error_for_status()?;
557
558 Ok(())
559 }
560
561 async fn get_queued_job(&self, id: i32) -> Result<QueuedJob> {
562 let record = self
563 .get(Cow::Owned(format!("api/v1/queue/{id}")))
564 .send()
565 .await?
566 .error_for_status()?
567 .json()
568 .await?;
569
570 Ok(record)
571 }
572
573 async fn drop_queued_job(&self, id: i32) -> Result<()> {
574 self.delete(Cow::Owned(format!("api/v1/queue/{id}")))
575 .send()
576 .await?
577 .error_for_status()?;
578
579 Ok(())
580 }
581
582 async fn drop_queued_jobs(
583 &self,
584 origin_filter: Option<&OriginFilter>,
585 source_identity_filter: Option<&SourceIdentityFilter>,
586 ) -> Result<()> {
587 self.delete(Cow::Borrowed("api/v1/queue"))
588 .query(&origin_filter)
589 .query(&source_identity_filter)
590 .send()
591 .await?
592 .error_for_status()?;
593
594 Ok(())
595 }
596
597 async fn request_work(&self, request: PopQueuedJobRequest) -> Result<JobAssignment> {
598 let record = self
599 .post(Cow::Borrowed("api/v1/queue/pop"))
600 .json(&request)
601 .send_encoded()
602 .await?
603 .error_for_status()?
604 .json()
605 .await?;
606
607 Ok(record)
608 }
609
610 async fn ping_job(&self, id: i32) -> Result<()> {
611 self.post(Cow::Owned(format!("api/v1/queue/{id}/ping")))
613 .header("Content-Length", 0)
614 .send()
615 .await?
616 .error_for_status()?;
617
618 Ok(())
619 }
620}
621
622#[async_trait]
623impl WorkerRestApi for Client {
624 async fn get_workers(&self, page: Option<&Page>) -> Result<ResultPage<Worker>> {
625 let workers = self
626 .get(Cow::Borrowed("api/v1/workers"))
627 .query(&page)
628 .send()
629 .await?
630 .error_for_status()?
631 .json()
632 .await?;
633
634 Ok(workers)
635 }
636
637 async fn register_worker(&self, request: RegisterWorkerRequest) -> Result<()> {
638 self.post(Cow::Borrowed("api/v1/workers"))
639 .json(&request)
640 .send_encoded()
641 .await?
642 .error_for_status()?;
643
644 Ok(())
645 }
646
647 async fn get_worker(&self, id: i32) -> Result<Worker> {
648 let worker = self
649 .get(Cow::Owned(format!("api/v1/workers/{id}")))
650 .send()
651 .await?
652 .error_for_status()?
653 .json()
654 .await?;
655
656 Ok(worker)
657 }
658
659 async fn unregister_worker(&self, id: i32) -> Result<()> {
660 self.delete(Cow::Owned(format!("api/v1/workers/{id}")))
661 .send()
662 .await?
663 .error_for_status()?;
664
665 Ok(())
666 }
667}