[snowpatch] [PATCH] Support pagination when using --count

Andrew Donnellan andrew.donnellan at au1.ibm.com
Fri Aug 10 16:21:03 AEST 2018


Add support for pagination when using --count. We extract the Link header
from the Patchwork response to find the correct URL for the next page.

The pagination is potentially racy, as new patches could come in and
disturb the ordering, but this is a limitation on the Patchwork side at
present and is unlikely to be a significant problem.

Closes: #17 ("Enable the ability to go to page 2 (and beyond) of Patchwork results")
Signed-off-by: Andrew Donnellan <andrew.donnellan at au1.ibm.com>
---
 src/main.rs      | 29 ++++++++++++++++-------------
 src/patchwork.rs | 41 ++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 56 insertions(+), 14 deletions(-)

diff --git a/src/main.rs b/src/main.rs
index b16bb31bb4b4..ba9e6dba1371 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -401,9 +401,6 @@ fn main() {
         return;
     }
 
-    // The number of patches tested so far.  If --count isn't provided, this is unused.
-    let mut patch_count = 0;
-
     /*
      * Poll Patchwork for new patches.
      * If the patch is standalone (not part of a series), apply it.
@@ -411,9 +408,19 @@ fn main() {
      * Spawn tests.
      */
     'daemon: loop {
-        let patch_list = patchwork
-            .get_patch_query(&args.flag_project)
-            .unwrap_or_else(|err| panic!("Failed to obtain patch list: {}", err));
+        let patch_list;
+
+        // TODO: This is hacky and we should refactor out daemon vs patch/count
+        // mode
+        if args.flag_count > 0 {
+            patch_list = patchwork
+                .get_patch_query_num(&args.flag_project, args.flag_count as usize)
+                .unwrap_or_else(|err| panic!("Failed to obtain patch list: {}", err));
+        } else {
+            patch_list = patchwork
+                .get_patch_query(&args.flag_project)
+                .unwrap_or_else(|err| panic!("Failed to obtain patch list: {}", err));
+        }
         info!("snowpatch is ready to test new revisions from Patchwork.");
         for patch in patch_list {
             // If it's already been tested, we can skip it
@@ -481,13 +488,9 @@ fn main() {
                     patchwork.post_test_result(result, &patch.checks).unwrap();
                 }
             }
-            if args.flag_count > 0 {
-                patch_count += 1;
-                debug!("Tested {} patches out of {}", patch_count, args.flag_count);
-                if patch_count >= args.flag_count {
-                    break 'daemon;
-                }
-            }
+        }
+        if args.flag_count > 0 {
+            break;
         }
         info!("Finished testing new revisions, sleeping.");
         thread::sleep(Duration::new(settings.patchwork.polling_interval * 60, 0));
diff --git a/src/patchwork.rs b/src/patchwork.rs
index c9c335472496..31ac8ef95911 100644
--- a/src/patchwork.rs
+++ b/src/patchwork.rs
@@ -25,7 +25,9 @@ use std::result::Result;
 use tempdir::TempDir;
 
 use reqwest;
-use reqwest::header::{qitem, Accept, Authorization, Basic, Connection, ContentType, Headers};
+use reqwest::header::{
+    qitem, Accept, Authorization, Basic, Connection, ContentType, Headers, Link, RelationType,
+};
 use reqwest::Client;
 use reqwest::Response;
 use reqwest::StatusCode;
@@ -306,6 +308,43 @@ impl PatchworkServer {
             .unwrap_or_else(|err| panic!("Failed to connect to Patchwork: {}", err)))
     }
 
+    fn get_next_link(&self, resp: &Response) -> Option<String> {
+        let next = resp.headers().get::<Link>()?;
+        for val in next.values() {
+            if let Some(rel) = val.rel() {
+                if rel.iter().any(|reltype| reltype == &RelationType::Next) {
+                    return Some(val.link().to_string());
+                }
+            }
+        }
+        None
+    }
+
+    pub fn get_patch_query_num(
+        &self,
+        project: &str,
+        num_patches: usize,
+    ) -> Result<Vec<Patch>, serde_json::Error> {
+        let mut list: Vec<Patch> = vec![];
+        let mut url = Some(format!(
+            "{}{}/patches/{}&project={}",
+            &self.url, PATCHWORK_API, PATCHWORK_QUERY, project
+        ));
+
+        while let Some(real_url) = url {
+            let resp = self.get_url(&real_url)
+                .unwrap_or_else(|err| panic!("Failed to connect to Patchwork: {}", err));
+            url = self.get_next_link(&resp);
+            let new_patches: Vec<Patch> = serde_json::from_reader(resp)?;
+            list.extend(new_patches);
+            if list.len() >= num_patches {
+                break;
+            }
+        }
+        list.truncate(num_patches);
+        Ok(list)
+    }
+
     pub fn get_patch_dependencies(&self, patch: &Patch) -> Vec<Patch> {
         // We assume the list of patches in a series are in order.
         let mut dependencies: Vec<Patch> = vec![];
-- 
2.11.0



More information about the snowpatch mailing list