Skip to content

Commit

Permalink
Refactor bounty to monolithic approach
Browse files Browse the repository at this point in the history
  • Loading branch information
ffakenz committed May 6, 2024
1 parent 1ae8604 commit db5077d
Show file tree
Hide file tree
Showing 8 changed files with 235 additions and 54 deletions.
85 changes: 85 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions backend/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ num-traits = "0.2.18"
futures = "0.3.30"
async-trait = "0.1.8"
regex = "1.5.4"
derive_builder = "0.20.0"
4 changes: 2 additions & 2 deletions backend/backend.did
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,15 @@ type DepositErr = variant {
TransferFailure : record { reason : text };
};

service : (authority: principal, github_issue_id: int32) -> {
service : (authority: principal) -> {
// GitHub Service
"get_issue": (GithubToken) -> (Issue);
"get_fixed_by": (GithubToken) -> (text);
"get_is_merged": (GithubToken) -> (text);
"get_merged_details": (GithubToken) -> (PrDetailsResponse);
// Bounty Service
"healthcheck": () -> (text);
"accept": (Contributor, github_pr_id: int32) -> ();
"accept": (Contributor, github_issue_id: int32, github_pr_id: int32) -> ();
"deposit": () -> (DepositReceipt);
}

Expand Down
63 changes: 49 additions & 14 deletions backend/src/bounty/api/accept.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,33 @@
use super::state::{Contributor, BOUNTY_STATE};
use super::state::{Contributor, PullRequest, BOUNTY_STATE};

pub fn accept_impl(contributor: Contributor, github_pr_id: i32) -> () {
// FIXME: check contributor is the owner of the PR.
pub fn accept_impl(contributor: Contributor, github_issue_id: i32, github_pr_id: i32) -> () {
BOUNTY_STATE.with(|state| {
if let Some(ref mut bounty_canister) = *state.borrow_mut() {
if bounty_canister.interested_contributors.contains_key(&github_pr_id) {
// do not update if the key is present.
// > The contributor is suppose to be the PR owner and the principal who called accept,
// > thus its fixed.
// FIXME: change response type to include a propper domain error.
let mut issue_exists = false;
let mut pr_exists = false;

if let Some(ref mut issue) = bounty_canister.github_issues.get_mut(&github_issue_id) {
issue_exists = true;
if !issue.bounty.accepted_prs.contains_key(&github_pr_id) {
let pr = PullRequest {
id: github_pr_id,
contributor,
};
issue.bounty.accepted_prs.insert(github_pr_id, pr);
pr_exists = true;
}
}

if !issue_exists {
// FIXME: change response type to include a proper domain error.
// The response should be a Result type (Either).
panic!("Can't accept an issue which does not exist.");
}

if !pr_exists {
// FIXME: change response type to include a proper domain error.
// The response should be a Result type (Either).
panic!("Can't accept twice");
} else {
// Add the contributor to the interested contributors list.
bounty_canister.interested_contributors.insert(github_pr_id, contributor);
}
}
});
Expand All @@ -30,15 +44,26 @@ mod test_accept {
let authority =
Principal::from_text("t2y5w-qp34w-qixaj-s67wp-syrei-5yqse-xbed6-z5nsd-fszmf-izgt2-lqe")
.unwrap();
init_impl(authority, 123);
init_impl(authority);
let github_issue_id = 123;
BOUNTY_STATE.with(|state| {
let bounty_canister = state.borrow();
if let Some(ref bounty_canister) = *bounty_canister {
assert_eq!(bounty_canister.interested_contributors.len(), 0);
assert_eq!(
bounty_canister
.github_issues
.get(&github_issue_id)
.unwrap()
.bounty
.accepted_prs
.len(),
0
);
} else {
panic!("Bounty canister state not initialized");
}
});

let contributor =
Principal::from_text("t2y5w-qp34w-qixaj-s67wp-syrei-5yqse-xbed6-z5nsd-fszmf-izgt2-lqe")
.unwrap();
Expand All @@ -48,12 +73,22 @@ mod test_accept {
address: contributor,
crypto_address: "contributor_address".to_string(),
},
github_issue_id,
github_pr_id,
);
BOUNTY_STATE.with(|state| {
let bounty_canister = state.borrow();
if let Some(ref bounty_canister) = *bounty_canister {
assert_eq!(bounty_canister.interested_contributors.len(), 1);
assert_eq!(
bounty_canister
.github_issues
.get(&github_issue_id)
.unwrap()
.bounty
.accepted_prs
.len(),
1
);
} else {
panic!("Bounty canister state not initialized");
}
Expand Down
80 changes: 62 additions & 18 deletions backend/src/bounty/api/claim.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use crate::bounty::api::state::{BOUNTY_STATE, Contributor};
use crate::bounty::api::state::{Contributor, BOUNTY_STATE};
use candid::{CandidType, Principal};
use serde::{Deserialize, Serialize};

use crate::provider::github::api::{get_issue::IssueResponse, get_merged_details::PrDetailsResponse};
use crate::provider::github::api::{
get_issue::IssueResponse, get_merged_details::PrDetailsResponse,
};
use crate::provider::github::client::IGithubClient;

#[derive(Debug, Serialize, Deserialize, CandidType)]
Expand All @@ -21,10 +23,18 @@ pub struct GithubClientMock {
#[cfg(test)]
#[async_trait::async_trait]
impl IGithubClient for GithubClientMock {
async fn get_issue(&self, issue_nbr: i32) -> IssueResponse { todo!() }
async fn get_fixed_by(&self, issue_nbr: i32) -> String { todo!() }
async fn get_is_merged(&self, pr_nbr: i32) -> String { todo!() }
async fn get_merged_details(&self, pr_nbr: i32) -> PrDetailsResponse { todo!() }
async fn get_issue(&self, issue_nbr: i32) -> IssueResponse {
todo!()
}
async fn get_fixed_by(&self, issue_nbr: i32) -> String {
todo!()
}
async fn get_is_merged(&self, pr_nbr: i32) -> String {
todo!()
}
async fn get_merged_details(&self, pr_nbr: i32) -> PrDetailsResponse {
todo!()
}
}

// FIXME: remove this annotation after finishing draft impl.
Expand All @@ -33,20 +43,23 @@ pub async fn claim_impl(
github_client: &dyn IGithubClient,
github_issue_id: i32,
github_pr_id: i32,
github_token: &str,
) -> Option<ClaimError> {
let contributor_opt: Option<Contributor> = BOUNTY_STATE.with(|state| {
match state.borrow().as_ref() {
Some(bounty_state) => {
// Access the interested_contributors HashMap from the BountyState
bounty_state.interested_contributors.get(&github_pr_id).cloned()
return bounty_state
.github_issues
.get(&github_pr_id)
.and_then(|issue| issue.bounty.accepted_prs.get(&github_pr_id))
.map(|pr| pr.contributor.clone())
}
None => panic!("Bounty canister state not initialized")
None => panic!("Bounty canister state not initialized"),
}
});
match contributor_opt {
None => Some(ClaimError::PRNotAccepted{github_pr_id})
, Some(contributor) => {
None => Some(ClaimError::PRNotAccepted { github_pr_id }),
Some(contributor) => {
let issue = github_client.get_issue(github_issue_id).await;
todo!()
}
Expand All @@ -63,12 +76,18 @@ mod test_claim {

use super::{claim_impl, ClaimError, GithubClientMock};

fn accept_contributor(principal: &str, crypto_address: &str, github_pr_id: i32) {
fn accept_contributor(
principal: &str,
crypto_address: &str,
github_issue_id: i32,
github_pr_id: i32,
) {
accept_impl(
Contributor {
address: Principal::from_text(principal).unwrap(),
crypto_address: crypto_address.to_string(),
},
github_issue_id,
github_pr_id,
);
}
Expand All @@ -79,14 +98,30 @@ mod test_claim {

let authority = Principal::from_text("rdmx6-jaaaa-aaaaa-aaadq-cai").unwrap();

init_impl(authority, github_issue_id);
init_impl(authority);

accept_contributor("mxzaz-hqaaa-aaaar-qaada-cai", "contributor_address_1", 1);
accept_contributor("n5wcd-faaaa-aaaar-qaaea-cai", "contributor_address_2", 2);
accept_contributor(
"mxzaz-hqaaa-aaaar-qaada-cai",
"contributor_address_1",
github_issue_id,
1,
);
accept_contributor(
"n5wcd-faaaa-aaaar-qaaea-cai",
"contributor_address_2",
github_issue_id,
2,
);

let github_client = GithubClientMock{principal: authority};
let github_client = GithubClientMock {
principal: authority,
};

let result = block_on(claim_impl(&github_client, github_issue_id, 2, "GithubToken"));
let result = block_on(claim_impl(
&github_client,
github_issue_id,
2,
));

match result {
None => assert!(true),
Expand All @@ -99,7 +134,16 @@ mod test_claim {
BOUNTY_STATE.with(|state| {
let bounty_canister = state.borrow();
if let Some(ref bounty_canister) = *bounty_canister {
assert_eq!(bounty_canister.winner.unwrap(), 2);
assert_eq!(
bounty_canister
.github_issues
.get(&github_issue_id)
.unwrap()
.bounty
.winner
.unwrap(),
2
);
} else {
panic!("Bounty canister state not initialized");
}
Expand Down
Loading

0 comments on commit db5077d

Please sign in to comment.