use crate::author;
use crate::author::Author;
use crate::branch_detail;
use crate::branches;
use crate::commits;
use crate::errors::Error;
use crate::owner;
use crate::repos;
pub struct Client {
token: String,
}
impl Client {
pub fn new_from_env() -> Result<Client, Error> {
let token = match std::env::var("CODECOV_OWNER_TOKEN") {
Ok(token) => token,
Err(e) => return Err(Error::EnvError(e)),
};
Ok(Client::new(token))
}
pub fn new(token: String) -> Client {
Client { token }
}
fn auth_header_val(&self) -> String {
format!("bearer {}", self.token)
}
fn owner_endpoint(&self, owner: &owner::Owner) -> String {
let api_endpoint = "https://codecov.io/api/v2";
format!("{}/{}/{}", api_endpoint, owner.service, owner.username)
}
fn repos_endpoint(&self, author: &author::Author) -> String {
let owner_endpoint = self.owner_endpoint(&author.to_owner());
format!("{}/repos/{}", owner_endpoint, author.name)
}
pub fn get_all_repos(&self, owner: &owner::Owner) -> Result<Vec<repos::Repo>, Error> {
let mut repos = Vec::new();
let mut url = format!("{}/repos?page_size=100", self.owner_endpoint(owner));
loop {
let mut repo_list = self.get_repos_page(&url)?;
match &mut repo_list {
repos::ReposAPIResponse {
results,
next: Some(next_url),
..
} => {
repos.append(results);
url = next_url.to_string();
continue;
}
repos::ReposAPIResponse {
results,
next: None,
..
} => {
repos.append(results);
break;
}
}
}
Ok(repos)
}
fn get_repos_page(&self, url: &str) -> Result<repos::ReposAPIResponse, Error> {
self.api_request::<repos::ReposAPIResponse>(url)
}
fn api_raw_json(&self, url: &str) -> Result<serde_json::Value, Error> {
let client = reqwest::blocking::Client::new();
let req = client
.get(url)
.header("Authorization", self.auth_header_val());
let res = match req.send() {
Ok(res) => res,
Err(e) => return Err(Error::ReqwestError(e)),
};
let res = match res.json::<serde_json::Value>() {
Ok(res) => res,
Err(e) => return Err(Error::ReqwestError(e)),
};
Ok(res)
}
fn api_request<T: serde::de::DeserializeOwned + std::fmt::Debug>(
&self,
url: &str,
) -> Result<T, Error> {
let res = self.api_raw_json(url)?;
let data = match serde_json::from_value::<T>(res) {
Ok(data) => data,
Err(e) => return Err(Error::DeserializeError(e)),
};
Ok(data)
}
pub fn get_commits(
&self,
author: &author::Author,
) -> Result<commits::CommitsAPIResponse, Error> {
let url = format!("{}/commits", self.repos_endpoint(author));
let commits = self.api_request::<commits::CommitsAPIResponse>(&url)?;
Ok(commits)
}
pub fn get_branches(
&self,
author: &author::Author,
) -> Result<branches::BranchesAPIResponse, Error> {
let url = format!("{}/branches", self.repos_endpoint(author));
let branches = self.api_request::<branches::BranchesAPIResponse>(&url)?;
Ok(branches)
}
pub fn get_branch_detail(
&self,
author: &Author,
branch_name: &str,
) -> Result<branch_detail::BranchDetailAPIResponse, Error> {
let url = format!("{}/branches/{}", self.repos_endpoint(author), branch_name);
let branch_detail = self.api_request::<branch_detail::BranchDetailAPIResponse>(&url)?;
Ok(branch_detail)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_all_repos() {
let client = Client::new_from_env().unwrap();
let owner = owner::Owner::new("github", "codecov");
let repos = client.get_all_repos(&owner).unwrap();
assert!(!repos.is_empty());
}
#[test]
fn test_get_commits() {
let client = Client::new_from_env().unwrap();
let author = author::Author::new("github", "codecov", "codecov-demo");
let commits = client.get_commits(&author).unwrap();
assert!(!commits.results.is_empty());
}
#[test]
fn test_get_branches() {
let client = Client::new_from_env().unwrap();
let author = author::Author::new("github", "codecov", "codecov-demo");
let branches = client.get_branches(&author).unwrap();
assert!(!branches.results.is_empty());
}
#[test]
fn test_get_branch_detail() {
let client = Client::new_from_env().unwrap();
let author = author::Author::new("github", "codecov", "codecov-demo");
let branch_name = "main";
let branch_detail = client.get_branch_detail(&author, branch_name).unwrap();
match branch_detail {
branch_detail::BranchDetailAPIResponse::Success(detail) => {
assert_eq!(detail.name, branch_name);
assert!(detail.latest_coverage() >= 0.0);
}
_ => panic!("should be success"),
}
}
#[test]
fn test_get_branch_detail_not_found() {
let client = Client::new_from_env().unwrap();
let author = author::Author::new("github", "kitsuyui", "rust-codecov");
let branch_name = "aaaaaaaaaa";
let branch_detail = client.get_branch_detail(&author, branch_name).unwrap();
if let branch_detail::BranchDetailAPIResponse::Success(_) = branch_detail {
panic!("should be not found");
}
}
}