diff --git a/obsctl/src/main.rs b/obsctl/src/main.rs index dcb0cff..63c7eca 100644 --- a/obsctl/src/main.rs +++ b/obsctl/src/main.rs @@ -44,7 +44,7 @@ async fn monitor(client: Client, opts: Package) -> Result<()> { let p = client.project(opts.project).package(opts.package.clone()); let mut last: Vec = Vec::new(); loop { - let result = p.result().await?; + let result = p.result(Default::default()).await?; for r in result.results { let data = MonitorData::from_result(r, &opts.package); diff --git a/open-build-service-api/examples/obsapi.rs b/open-build-service-api/examples/obsapi.rs index aff93eb..30e6185 100644 --- a/open-build-service-api/examples/obsapi.rs +++ b/open-build-service-api/examples/obsapi.rs @@ -1,7 +1,7 @@ use anyhow::{Context, Result}; use clap::Parser; use futures::prelude::*; -use open_build_service_api::{Client, PackageLogStreamOptions}; +use open_build_service_api::{BuildTargerSpec, Client, PackageLogStreamOptions}; use oscrc::Oscrc; use std::path::PathBuf; use tokio::io::AsyncWriteExt; @@ -25,6 +25,10 @@ struct Package { struct BuildResult { project: String, package: Option, + #[arg(long, short)] + repository: Option, + #[arg(long, short)] + arch: Option, } async fn jobstatus(client: Client, opts: PackageFull) -> Result<()> { @@ -70,11 +74,15 @@ async fn list(client: Client, opts: Package) -> Result<()> { async fn result(client: Client, opts: BuildResult) -> Result<()> { let p = client.project(opts.project); + let result_options = BuildTargerSpec { + repository: opts.repository, + arch: opts.arch, + }; if let Some(package) = opts.package { let p = p.package(package); - println!("{:#?}", p.result().await); + println!("{:#?}", p.result(result_options).await); } else { - println!("{:#?}", p.result().await); + println!("{:#?}", p.result(result_options).await); } Ok(()) diff --git a/open-build-service-api/src/lib.rs b/open-build-service-api/src/lib.rs index f94fd28..2cbff15 100644 --- a/open-build-service-api/src/lib.rs +++ b/open-build-service-api/src/lib.rs @@ -365,8 +365,8 @@ impl<'de> Deserialize<'de> for BranchStatus { } } -#[derive(Deserialize, Debug)] -pub struct PackageBuildMetaDisable { +#[derive(Deserialize, Debug, Default)] +pub struct BuildTargerSpec { #[serde(default)] pub repository: Option, #[serde(default)] @@ -376,7 +376,7 @@ pub struct PackageBuildMetaDisable { #[derive(Deserialize, Debug, Default)] pub struct PackageBuildMeta { #[serde(rename = "disable")] - pub disabled: Vec, + pub disabled: Vec, } #[derive(Deserialize, Debug)] @@ -1031,14 +1031,25 @@ impl<'a> PackageBuilder<'a> { self.client.post_request(u).await } - pub async fn result(&self) -> Result { + pub async fn result(&self, filter: BuildTargerSpec) -> Result { let mut u = self.client.base.clone(); u.path_segments_mut() .map_err(|_| Error::InvalidUrl)? .push("build") .push(&self.project) .push("_result"); - u.query_pairs_mut().append_pair("package", &self.package); + + { + let mut q = u.query_pairs_mut(); + q.append_pair("package", &self.package); + if let Some(repository) = &filter.repository { + q.append_pair("repository", repository); + } + if let Some(arch) = &filter.arch { + q.append_pair("arch", arch); + } + } + self.client.request(u).await } } @@ -1088,13 +1099,24 @@ impl<'a> ProjectBuilder<'a> { self.client.request(u).await } - pub async fn result(&self) -> Result { + pub async fn result(&self, filter: BuildTargerSpec) -> Result { let mut u = self.client.base.clone(); u.path_segments_mut() .map_err(|_| Error::InvalidUrl)? .push("build") .push(&self.project) .push("_result"); + + { + let mut q = u.query_pairs_mut(); + if let Some(repository) = &filter.repository { + q.append_pair("repository", repository); + } + if let Some(arch) = &filter.arch { + q.append_pair("arch", arch); + } + } + self.client.request(u).await } diff --git a/open-build-service-api/tests/integration.rs b/open-build-service-api/tests/integration.rs index 0189ad0..625b329 100644 --- a/open-build-service-api/tests/integration.rs +++ b/open-build-service-api/tests/integration.rs @@ -1046,7 +1046,7 @@ async fn test_build_results() { .project(TEST_PROJECT.to_owned()) .package(TEST_PACKAGE_2.to_owned()); - let results = project.result().await.unwrap(); + let results = project.result(Default::default()).await.unwrap(); let (arch1_repo, arch2_repo) = get_results_by_arch(results); assert_eq!(arch1_repo.project, TEST_PROJECT); @@ -1070,7 +1070,23 @@ async fn test_build_results() { assert_eq!(package2_status.details.as_ref().unwrap(), details); assert!(package2_status.dirty); - let results = package_2.result().await.unwrap(); + // Test project filter + let results = project + .result(BuildTargerSpec { + repository: Some(TEST_REPO.to_owned()), + arch: Some(TEST_ARCH_1.to_owned()), + }) + .await + .unwrap(); + + let arch1_repo = &results.results[0]; + assert_eq!(arch1_repo.project, TEST_PROJECT); + assert_eq!(arch1_repo.repository, TEST_REPO); + assert_eq!(arch1_repo.arch, TEST_ARCH_1); + assert_eq!(arch1_repo.code, RepositoryCode::Building); + assert_eq!(results.results.len(), 1); + + let results = package_2.result(Default::default()).await.unwrap(); let (arch1_repo, arch2_repo) = get_results_by_arch(results); assert_eq!(arch1_repo.statuses.len(), 0); @@ -1089,7 +1105,7 @@ async fn test_build_results() { MockBuildStatus::new(MockPackageCode::Broken), ); - let results = project.result().await.unwrap(); + let results = project.result(Default::default()).await.unwrap(); let (arch1_repo, _) = get_results_by_arch(results); let package2_arch2 = arch1_repo @@ -1100,7 +1116,7 @@ async fn test_build_results() { assert_eq!(package2_arch2.package, TEST_PACKAGE_2); assert_eq!(package2_arch2.code, PackageCode::Broken); - let results = package_2.result().await.unwrap(); + let results = package_2.result(Default::default()).await.unwrap(); let (arch1_repo, arch2_repo) = get_results_by_arch(results); assert_eq!(arch1_repo.statuses.len(), 1); @@ -1108,6 +1124,22 @@ async fn test_build_results() { assert_eq!(arch1_repo.statuses[0].package, TEST_PACKAGE_2); assert_eq!(arch2_repo.statuses[0].package, TEST_PACKAGE_2); + + // Test package filter + let results = package_2 + .result(BuildTargerSpec { + repository: Some(TEST_REPO.to_owned()), + arch: Some(TEST_ARCH_2.to_owned()), + }) + .await + .unwrap(); + + let arch2_repo = &results.results[0]; + assert_eq!(arch2_repo.project, TEST_PROJECT); + assert_eq!(arch2_repo.repository, TEST_REPO); + assert_eq!(arch2_repo.code, RepositoryCode::Broken); + assert_eq!(arch2_repo.statuses.len(), 1); + assert_eq!(results.results.len(), 1); } #[tokio::test] diff --git a/open-build-service-mock/src/api/build.rs b/open-build-service-mock/src/api/build.rs index ee2cddb..abe0605 100644 --- a/open-build-service-mock/src/api/build.rs +++ b/open-build-service-mock/src/api/build.rs @@ -270,9 +270,15 @@ impl Respond for BuildResultsResponder { let project_name = components.nth_back(1).unwrap(); let mut package_filters = vec![]; + let mut arch_filter = None; + let mut repository_filter = None; for (key, value) in request.url.query_pairs() { - ensure!(key == "package", unknown_parameter(&key)); - package_filters.push(value); + match key.as_ref() { + "package" => package_filters.push(value), + "arch" => arch_filter = Some(value), + "repository" => repository_filter = Some(value), + _ => return unknown_parameter(&key).into_response(), + }; } let projects = self.mock.projects().read().unwrap(); @@ -293,8 +299,13 @@ impl Respond for BuildResultsResponder { // these are computed. .with_attribute(("state", "3ff37f67d60b76bd0491a5243311ba81")) .write_inner_content(|writer| { - for (repo_name, arches) in &project.repos { - for (arch, repo) in arches { + for (repo_name, arches) in project.repos.iter().filter(|(repo_name, _)| { + repository_filter.as_ref().map_or(true, |f| f == *repo_name) + }) { + for (arch, repo) in arches + .iter() + .filter(|(arch, _)| arch_filter.as_ref().map_or(true, |f| f == *arch)) + { let result_xml = writer.create_element("result").with_attributes([ ("project", project_name), ("repository", repo_name.as_str()),