From 5ad5db0026d8dc58c7205ed09ed9a8d5f445d605 Mon Sep 17 00:00:00 2001 From: Franco Testagrossa Date: Sat, 27 Apr 2024 16:36:22 +0200 Subject: [PATCH] Resolve #2 --- Cargo.lock | 1 + Makefile | 13 ++++- backend/Cargo.toml | 1 + backend/backend.did | 1 + backend/src/github/api/get_fixed_by.rs | 76 ++++++++++++++++++++++++++ backend/src/github/api/get_issue.rs | 2 +- backend/src/github/client.rs | 9 +++ backend/src/github/utils.rs | 4 ++ backend/src/lib.rs | 10 ++++ 9 files changed, 115 insertions(+), 2 deletions(-) create mode 100644 backend/src/github/api/get_fixed_by.rs diff --git a/Cargo.lock b/Cargo.lock index 89aba1c..b19ebb5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -45,6 +45,7 @@ dependencies = [ "candid", "ic-cdk", "ic-cdk-macros", + "regex", "serde", "serde_bytes", "serde_json", diff --git a/Makefile b/Makefile index 09d061a..82d38f5 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ upgrade: build .PHONY: test .SILENT: test -test: install +test-1: install # Call the backend canister to get the GitHub issue and capture the output @echo "Calling get_gh_issue on backend canister..." @TMP_FILE=$$(mktemp); \ @@ -36,6 +36,17 @@ test: install cat $$TMP_FILE; \ rm -f $$TMP_FILE +.PHONY: test +.SILENT: test +test-2: install + # Call the backend canister to get the GitHub issue and capture the output + @echo "Calling get_gh_fixed_by on backend canister..." + @TMP_FILE=$$(mktemp); \ + dfx canister call backend get_gh_fixed_by '("${GITHUB_TOKEN}")' > $$TMP_FILE; \ + echo "get_gh_fixed_by response:"; \ + cat $$TMP_FILE; \ + rm -f $$TMP_FILE + .PHONY: clean .SILENT: clean clean: diff --git a/backend/Cargo.toml b/backend/Cargo.toml index 01cb9f9..67cb34b 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -15,3 +15,4 @@ ic-cdk-macros = "0.6.0" serde = "1.0.152" serde_json = "1.0.93" serde_bytes = "0.11.9" +regex = "1.5.4" \ No newline at end of file diff --git a/backend/backend.did b/backend/backend.did index 6b2d058..907e83a 100644 --- a/backend/backend.did +++ b/backend/backend.did @@ -10,4 +10,5 @@ type gh_token = text; service : { "get_gh_issue": (gh_token) -> (issue); + "get_gh_fixed_by": (gh_token) -> (text); } \ No newline at end of file diff --git a/backend/src/github/api/get_fixed_by.rs b/backend/src/github/api/get_fixed_by.rs new file mode 100644 index 0000000..1e6567a --- /dev/null +++ b/backend/src/github/api/get_fixed_by.rs @@ -0,0 +1,76 @@ +use ic_cdk::api::management_canister::http_request::{ + http_request, CanisterHttpRequestArgument, HttpMethod, +}; + +use super::super::utils::github_host; + +use std::collections::HashSet; + +use regex::Regex; + +// curl https://github.com/input-output-hk/hydra/issues/1370 +pub async fn get_fixed_by_impl(owner: String, repo: String, issue_nbr: i32) -> String { + // Setup the URL and its query parameters + let url = format!( + "https://{}/{}/{}/issues/{}", + github_host(), + owner, + repo, + issue_nbr + ); + + // Create the request argument + let request = CanisterHttpRequestArgument { + url: url.to_string(), + method: HttpMethod::GET, + body: None, + max_response_bytes: None, + transform: None, + headers: vec![], + }; + + // Make the HTTP request and wait for the response + match http_request(request).await { + Ok((response,)) => { + //We need to decode that Vec that is the body into readable text. + //To do this, we: + // 1. Call `String::from_utf8()` on response.body + // 3. We use a switch to explicitly call out both cases of decoding the Blob into ?Text + let str_body = String::from_utf8(response.body) + .expect("Transformed response is not UTF-8 encoded."); + + let fixed_by_lines = str_body.lines().fold(HashSet::new(), |mut set, line| { + if line.contains("Fixed by") { + set.insert(line.to_string()); + } + set + }); + + let result = fixed_by_lines.into_iter().collect::>().join(", "); + + if let Some(pull_request) = extract_pull_request(&result) { + return pull_request; + } + return "No PR".to_string(); + } + Err((rejection_code, message)) => { + panic!( + "The http_request resulted in an error. RejectionCode: {:?}, Error: {}", + rejection_code, message + ); + } + } +} + +fn extract_pull_request(html: &str) -> Option { + // Define a regular expression pattern to match the href attribute + let re = Regex::new(r#"]*?href="(.*?)"[^>]*?>"#).unwrap(); + + // Extract the href attribute from the HTML string + if let Some(captures) = re.captures(html) { + if let Some(href) = captures.get(1) { + return Some(href.as_str().to_string()); + } + } + None +} diff --git a/backend/src/github/api/get_issue.rs b/backend/src/github/api/get_issue.rs index 79cd64a..8e52167 100644 --- a/backend/src/github/api/get_issue.rs +++ b/backend/src/github/api/get_issue.rs @@ -29,7 +29,7 @@ pub async fn get_issue_impl( method: HttpMethod::GET, body: None, max_response_bytes: None, - transform: None, // We'll handle transformation separately + transform: None, headers: request_headers, }; diff --git a/backend/src/github/client.rs b/backend/src/github/client.rs index 73bd1d7..d98c1ae 100644 --- a/backend/src/github/client.rs +++ b/backend/src/github/client.rs @@ -1,3 +1,4 @@ +use super::api::get_fixed_by::get_fixed_by_impl; use super::api::get_issue::get_issue_impl; use super::utils::IssueResponse; @@ -18,4 +19,12 @@ impl GithubClient { ) .await } + pub async fn get_fixed_by(&self, issue_nbr: i32) -> String { + get_fixed_by_impl( + self.owner.clone(), + self.repo.clone(), + issue_nbr, + ) + .await + } } diff --git a/backend/src/github/utils.rs b/backend/src/github/utils.rs index 96121f2..6eb3403 100644 --- a/backend/src/github/utils.rs +++ b/backend/src/github/utils.rs @@ -16,6 +16,10 @@ pub fn github_api_host() -> String { return "api.github.com".to_string(); } +pub fn github_host() -> String { + return "github.com".to_string(); +} + pub fn mk_request_headers(github_token: String) -> Vec { return vec![ HttpHeader { diff --git a/backend/src/lib.rs b/backend/src/lib.rs index 9c120c8..7a5326c 100644 --- a/backend/src/lib.rs +++ b/backend/src/lib.rs @@ -1,6 +1,7 @@ mod github { pub mod api { pub mod get_issue; + pub mod get_fixed_by; } pub mod utils; pub mod client; @@ -16,4 +17,13 @@ async fn get_gh_issue(github_token: String) -> IssueResponse { let issue_nbr = 1404; let client = GithubClient{owner, repo, github_token}; return client.get_issue(issue_nbr).await; +} + +#[ic_cdk::update] +async fn get_gh_fixed_by(github_token: String) -> String { + let owner = "input-output-hk".to_string(); + let repo = "hydra".to_string(); + let issue_nbr = 1370; + let client = GithubClient{owner, repo, github_token}; + return client.get_fixed_by(issue_nbr).await; } \ No newline at end of file