refactored repo file to fit refactored auth

This commit is contained in:
Wyatt J. Miller 2021-12-18 15:07:46 -05:00
parent c2b6db25f7
commit cef93da327
5 changed files with 173 additions and 192 deletions

View File

@ -9,7 +9,7 @@ pub struct Configuration {
pub base_api: String, pub base_api: String,
pub base_url: String, pub base_url: String,
pub username: Option<String>, pub username: Option<String>,
pub password: Option<String> pub password: Option<String>,
} }
impl Configuration { impl Configuration {
@ -24,9 +24,7 @@ impl Configuration {
location.push(format!("{}/.config/gt/config.json", home_dir_env)); location.push(format!("{}/.config/gt/config.json", home_dir_env));
for i in location { for i in location {
settings.merge(File::with_name(&i) settings.merge(File::with_name(&i).required(false)).unwrap();
.required(false))
.unwrap();
} }
let config = settings let config = settings
@ -38,10 +36,7 @@ impl Configuration {
} }
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
fn set_location_linux( fn set_location_linux(location: &mut Vec<String>, home: String) -> Vec<String> {
location: &mut Vec<String>,
home: String
) -> Vec<String> {
location.push("config.json".to_string()); location.push("config.json".to_string());
location.push("/etc/gt/config.json".to_string()); location.push("/etc/gt/config.json".to_string());
location.push(format!("{}/.config/gt/config.json", home)); location.push(format!("{}/.config/gt/config.json", home));
@ -50,20 +45,14 @@ fn set_location_linux(
} }
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
fn set_location_macos( fn set_location_macos(location: &mut Vec<String>, home: String) -> Vec<String> {
location: &mut Vec<String>,
home: String
) -> Vec<String> {
location.push("config.json".to_string()); location.push("config.json".to_string());
location.to_vec() location.to_vec()
} }
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
fn set_location_windows( fn set_location_windows(location: &mut Vec<String>, home: String) -> Vec<String> {
location: &mut Vec<String>,
home: String
) -> Vec<String> {
location.push("config.json".to_string()); location.push("config.json".to_string());
location.to_vec() location.to_vec()

View File

@ -14,7 +14,6 @@ use clap::ArgMatches;
fn main() { fn main() {
let matches: ArgMatches = arg::get_args(); let matches: ArgMatches = arg::get_args();
let config = crate::config::Configuration::new(); let config = crate::config::Configuration::new();
let auth = request::Authentication::new(&config); let auth = request::Authentication::new(&config);
let request = auth.request_chooser(config.clone(), matches); let request = auth.request_chooser(config.clone(), matches);
@ -29,21 +28,21 @@ fn main() {
} }
if repo_matches.is_present("delete") { if repo_matches.is_present("delete") {
repo.delete_repo(&config, repo_matches); repo.delete_repo(&request);
} }
if repo_matches.is_present("fork") { if repo_matches.is_present("fork") {
repo.fork_repo(&config, repo_matches) repo.fork_repo(&request)
} }
if repo_matches.is_present("search") { if repo_matches.is_present("search") {
repo.search_repo(&config, repo_matches) repo.search_repo(&request)
} }
if repo_matches.is_present("list") { if repo_matches.is_present("list") {
repo.list_repo(&config) repo.list_repo(&request)
}
} }
},
("issue", Some(issue_matches)) => { ("issue", Some(issue_matches)) => {
let issue = issue::Issue::new(); let issue = issue::Issue::new();
@ -51,8 +50,7 @@ fn main() {
if issue_matches.is_present("create") { if issue_matches.is_present("create") {
issue.create_issue(&config, issue_matches); issue.create_issue(&config, issue_matches);
} }
}, }
_ => println!("Huh?") _ => println!("Huh?"),
} }
} }

View File

@ -1,11 +1,9 @@
use std::collections::HashMap; use std::collections::HashMap;
use clap::ArgMatches;
use colored::*; use colored::*;
use reqwest::{StatusCode, Client}; use reqwest::StatusCode;
use serde_derive::{Serialize, Deserialize}; use serde_derive::{Deserialize, Serialize};
use crate::config::Configuration;
use crate::request::Request; use crate::request::Request;
pub struct Repository; pub struct Repository;
@ -105,190 +103,196 @@ impl Repository {
pub fn create_repo(&self, request: &Request) { pub fn create_repo(&self, request: &Request) {
let client = &request.client; let client = &request.client;
let arg_value = request.arg_value.subcommand().1.unwrap().value_of("create").unwrap(); let arg_value = request
.arg_value
.subcommand()
.1
.unwrap()
.value_of("create")
.unwrap();
let mut map: HashMap<&str, &str> = HashMap::new(); let mut map: HashMap<&str, &str> = HashMap::new();
let url = format!("{request}/user/repos?token={api_token}", let url = format!(
"{request}/user/repos?token={api_token}",
request = request.url.as_ref().unwrap(), request = request.url.as_ref().unwrap(),
api_token = request api_token = request.authentication.credentials.1.as_ref().unwrap()
.authentication );
.credentials
.1.as_ref()
.unwrap());
map.insert("name", arg_value); map.insert("name", arg_value);
map.insert("readme", arg_value);
map.insert("description", arg_value);
let response = client.post(url.as_str()) let response = client.post(url.as_str()).json(&map).send();
.json(&map)
.send();
match response { match response {
Ok(mut repo) => { Ok(repo) => match repo.status() {
match repo.status() {
StatusCode::CREATED => { StatusCode::CREATED => {
let deserialized: RepositoryResponse = repo.json().unwrap(); let deserialized: RepositoryResponse = repo.json().unwrap();
println!("{}", "Repository successfully created!".green()); println!("{}", "Repository successfully created!".green());
println!("\tRepository name: {:0?}\n\tRepository owner: {:1?}\n\tRepository description: {:2?}", deserialized.name, deserialized.owner.unwrap().full_name, deserialized.description); println!("\tRepository name: {:0}\n\tRepository owner: {:1}\n\tRepository description: {:2}", deserialized.name, deserialized.owner.unwrap().full_name, deserialized.description);
},
StatusCode::CONFLICT => println!("{}", "Repository already exists!".red()),
StatusCode::UNPROCESSABLE_ENTITY => println!("{}", "Repository input validation failed!".red()),
_ => println!("Repository creation failed! HTTP status code: {}", repo.status().as_str())
} }
StatusCode::CONFLICT => println!("{}", "Repository already exists!".red()),
StatusCode::UNPROCESSABLE_ENTITY => {
println!("{}", "Repository input validation failed!".red())
}
_ => println!(
"Repository creation failed! HTTP status code: {}",
repo.status().as_str()
),
}, },
Err(e) => panic!(e) Err(e) => panic!("{}", e),
} }
} }
pub fn delete_repo(&self, config: &Configuration, arg: &ArgMatches) { pub fn delete_repo(&self, request: &Request) {
let client = Client::new(); let client = &request.client;
let arg_iter: Vec<&str> = arg.values_of("delete") let arg_value: Vec<&str> = request
.arg_value
.subcommand()
.1
.unwrap()
.values_of("delete")
.unwrap() .unwrap()
.collect(); .collect();
let url = format!(
r"{request}/repos/{owner}/{repo}?token={api_token}",
request = request.url.as_ref().unwrap(),
owner = request.authentication.credentials.0.as_ref().unwrap(),
repo = arg_value[1],
api_token = request.authentication.credentials.1.as_ref().unwrap(),
);
let url = format!("{base_url}{base_api}/repos/{username}/{repo_name}?token={api_token}", let response = client.delete(&url).send();
base_url = config.base_url,
base_api = config.base_api,
username = arg_iter[0],
repo_name = arg_iter[1],
api_token = config.api_token.as_ref().unwrap());
let response = client.delete(&url)
.send();
match response { match response {
Ok(repo) => { Ok(repo) => match repo.status() {
match repo.status() { StatusCode::NO_CONTENT => println!("{}", "Respository successfully deleted!".green()),
StatusCode::NO_CONTENT => println!("{}", "Repository successfully deleted!".green()), StatusCode::FORBIDDEN => println!("{}", "Repository deletion forbidden!".red()),
StatusCode::FORBIDDEN => println!("{}", "Forbidden to delete this repository!".red()), _ => println!(
StatusCode::NOT_FOUND => println!("{}", "Repository doesn't exist!".red()), "Repository deletion failed! Does the repository exist? HTTP status code: {}",
_ => println!("Repository deletion failed! HTTP status code: {}", repo.status().to_string()) repo.status().as_str()
} ),
}, },
Err(e) => panic!(e) Err(e) => panic!("{}", e),
} }
} }
pub fn fork_repo(&self, config: &Configuration, arg: &ArgMatches) { pub fn fork_repo(&self, request: &Request) {
let client = Client::new(); let client = &request.client;
let arg_item: Vec<&str> = arg.values_of("fork") let arg_item: Vec<&str> = request
.arg_value
.subcommand()
.1
.unwrap()
.values_of("fork")
.unwrap() .unwrap()
.collect(); .collect();
let mut map: HashMap<&str, &str> = HashMap::new(); let mut map: HashMap<&str, &str> = HashMap::new();
let user = config.username let user = request.authentication.credentials.0.as_ref().unwrap();
.clone()
.unwrap();
let url = format!(
let url = format!("{base_url}{base_api}/repos/{owner}/{repo}/forks?token={api_token}", "{request}/repos/{owner}/{repo}/forks?token={api_token}",
base_url = config.base_url, request = request.url.as_ref().unwrap(),
base_api = config.base_api,
owner = arg_item[0], owner = arg_item[0],
repo = arg_item[1], repo = arg_item[1],
api_token = config.api_token.as_ref().unwrap()); api_token = request.authentication.credentials.1.as_ref().unwrap(),
);
map.insert("name", user.as_str()); map.insert("name", user.as_str());
let response = client.post(url.as_str()) let response = client.post(url.as_str()).json(&map).send();
.json(&map)
.send();
match response { match response {
Ok(mut repo) => { Ok(repo) => match repo.status() {
match repo.status() {
StatusCode::ACCEPTED => { StatusCode::ACCEPTED => {
let deserialized: RepositoryResponse = repo.json().unwrap(); let deserialized: RepositoryResponse = repo.json().unwrap();
println!("{}", "Repository forked successfully".green()); println!("{}", "Repository forked successfully".green());
println!("\tOriginal repository name: {:0?}\n\tOriginal repository owner: {:1?}\n\tForked repository name: {:2?}\n\tForked repository owner: {:3?}", deserialized.name, arg_item[0], deserialized.name, deserialized.owner.unwrap().full_name); println!("\tOriginal repository name: {:0?}\n\tOriginal repository owner: {:1?}\n\tForked repository name: {:2?}\n\tForked repository owner: {:3?}", deserialized.name, arg_item[0], deserialized.name, deserialized.owner.unwrap().full_name);
}, }
StatusCode::INTERNAL_SERVER_ERROR => println!("{}", "Repository already forked!".red()), StatusCode::INTERNAL_SERVER_ERROR => println!("{}", "Repository already forked!".red()),
StatusCode::FORBIDDEN => println!("{}", "Repository fork forbidden!".red()), StatusCode::FORBIDDEN => println!("{}", "Repository fork forbidden!".red()),
StatusCode::UNPROCESSABLE_ENTITY => println!("{}", "Repository fork input validation failed!".red()), StatusCode::UNPROCESSABLE_ENTITY => println!("{}", "Repository fork input validation failed!".red()),
StatusCode::NOT_FOUND => println!("{}", "Repository not found!"), StatusCode::NOT_FOUND => println!("{}", "Repository not found!"),
_ => println!("Repository creation failed! HTTP status code: {}", repo.status().as_str()) _ => println!("Repository creation failed! HTTP status code: {}", repo.status().as_str()),
}
}, },
Err(e) => panic!(e) Err(e) => panic!("{}", e),
} }
} }
pub fn search_repo(&self, config: &Configuration, arg: &ArgMatches) { pub fn search_repo(&self, request: &Request) {
let client = Client::new(); let client = &request.client;
let arg_value = arg.value_of("search").unwrap(); let arg_value = request
let url = format!("{base_url}{base_api}/repos/search?q={query}&token={api_token}", .arg_value
base_url = config.base_url, .subcommand()
base_api = config.base_api, .1
.unwrap()
.value_of("search")
.unwrap();
let url = format!(
"{request}/repos/search?q={query}&token={api_token}",
request = request.url.as_ref().unwrap(),
query = arg_value, query = arg_value,
api_token = config.api_token.as_ref().unwrap()); api_token = request.authentication.credentials.1.as_ref().unwrap(),
);
let response = client.get(url.as_str()) let response = client.get(url.as_str()).send();
.send();
match response { match response {
Ok(mut repo) => { Ok(repo) => match repo.status() {
match repo.status() {
StatusCode::OK => { StatusCode::OK => {
let deserialized: MultipleRepositories = repo.json() let deserialized: MultipleRepositories = repo.json().unwrap();
.unwrap();
match deserialized.data.len() != 0 { match deserialized.data.len() != 0 {
true => { true => {
println!("{}", "List of repositories found:".green()); println!("{}", "List of repositories found:");
for (i, data) in deserialized.data.iter().enumerate() { for (i, data) in deserialized.data.iter().enumerate() {
println!("{}.\tRepository name: {:1?}\n\tRepository owner: {:2?}\n\tRepository description: {:3?}\n", i + 1, data.name, data.owner.as_ref().unwrap().full_name, data.description) println!("{}.\tRepository name: {:1}\n\tRepository owner: {:2}\n\tRepository description: {:3}\n", i + 1, data.name, data.owner.as_ref().unwrap().full_name, data.description)
} }
println!("Total number of repositories indexed: {}", deserialized.data.iter().count()) println!("Total number of repositories indexed: {}", deserialized.data.iter().count())
},
false => println!("{}", "Repository searched doesn't exist!".red())
} }
}, false => println!("{}", "Repository searched doesn't exist!".red()),
}
}
StatusCode::NOT_FOUND => println!("{}", "Repository searched doesn't exist!".red()), StatusCode::NOT_FOUND => println!("{}", "Repository searched doesn't exist!".red()),
StatusCode::UNPROCESSABLE_ENTITY => println!("{}", "Repository input validation failed!".red()), StatusCode::UNPROCESSABLE_ENTITY => println!("{}", "Repository input validation failed!".red()),
_ => println!("Repository search failed! HTTP status code: {}", repo.status().as_str()) _ => println!("Repository search failed! HTTP status code: {}", repo.status().as_str()),
}
}, },
Err(e) => panic!(e) Err(e) => panic!("{}", e),
} }
} }
pub fn list_repo(&self, config: &Configuration) { pub fn list_repo(&self, request: &Request) {
let client = Client::new(); let client = &request.client;
let url = format!("{base_url}{base_api}/repos/search?token={api_token}", let url = format!(
base_url = config.base_url, "{request}/repos/search?token={api_token}",
base_api = config.base_api, request = request.url.as_ref().unwrap(),
api_token = config.api_token.as_ref().unwrap()); api_token = request.authentication.credentials.1.as_ref().unwrap()
);
let response = client.get(url.as_str()) let response = client.get(url.as_str()).send();
.send();
match response { match response {
Ok(mut repo) => { Ok(repo) => match repo.status() {
match repo.status() {
StatusCode::OK => { StatusCode::OK => {
let deserialized: MultipleRepositories = repo.json() let deserialized: MultipleRepositories = repo.json().unwrap();
.unwrap();
match deserialized.data.len() != 0 { match deserialized.data.len() != 0 {
true => { true => {
println!("{}", "List of repositories found:".green()); println!("{}", "List of repositories found:");
for (i, data) in deserialized.data.iter().enumerate() { for (i, data) in deserialized.data.iter().enumerate() {
println!("{}.\tRepository name: {:1?}\n\tRepository owner: {:2?}\n\tRepository description: {:3?}\n", i + 1, data.name, data.owner.as_ref().unwrap().full_name, data.description) println!("{}.\tRepository name: {:1}\n\tRepository owner: {:2}\n\tRepository description: {:3}\n", i + 1, data.name, data.owner.as_ref().unwrap().full_name, data.description)
} }
println!("Total number of repositories indexed: {}", deserialized.data.iter().count()) println!("Total number of repositories indexed: {}", deserialized.data.iter().count())
}, },
false => println!("{}", "The authenticated user doesn't have any repositories. Why not create one?".yellow()) false => println!("{}", "The authenticated user doesn't have any repositories. Why not create one?".yellow())
} }
},
StatusCode::UNPROCESSABLE_ENTITY => println!("{}", "Repository input validation failed!".red()),
_ => println!("Repository search failed! HTTP status code: {}", repo.status().as_str())
} }
StatusCode::UNPROCESSABLE_ENTITY => println!("{}", "Repository input validation failed!".red()),
_ => println!("Repository search failed! HTTP status code: {}",repo.status().as_str()),
}, },
Err(e) => panic!(e) Err(e) => panic!("{}", e),
} }
} }
} }

View File

@ -1,7 +1,7 @@
use std::collections::HashMap; use std::collections::HashMap;
use clap::ArgMatches; use clap::ArgMatches;
use reqwest::Client; use reqwest::blocking::Client;
use crate::config::Configuration; use crate::config::Configuration;
@ -16,7 +16,7 @@ pub struct Request<'a> {
pub struct Authentication { pub struct Authentication {
pub basic: bool, pub basic: bool,
pub api_token: bool, pub api_token: bool,
pub credentials: (Option<String>, Option<String>) pub credentials: (Option<String>, Option<String>),
} }
impl<'a> Request<'a> { impl<'a> Request<'a> {
@ -24,13 +24,13 @@ impl<'a> Request<'a> {
pub fn with_basic_request( pub fn with_basic_request(
config: Configuration, config: Configuration,
arg: ArgMatches, arg: ArgMatches,
auth: Authentication auth: Authentication,
) -> Request { ) -> Request {
Request { Request {
client: Client::new(), client: Client::new(),
arg_value: arg, arg_value: arg,
map: HashMap::new(), map: HashMap::new(),
url: Some(format!("{:?}{:?}", config.base_url, config.base_api)), url: Some(format!("{}{}", config.base_url, config.base_api)),
authentication: auth, authentication: auth,
} }
} }
@ -39,19 +39,19 @@ impl<'a> Request<'a> {
pub fn with_api_request( pub fn with_api_request(
config: Configuration, config: Configuration,
arg: ArgMatches, arg: ArgMatches,
auth: Authentication auth: Authentication,
) -> Request { ) -> Request {
Request { Request {
client: Client::new(), client: Client::new(),
arg_value: arg, arg_value: arg,
map: HashMap::new(), map: HashMap::new(),
url: Some(format!("{:?}{:?}", config.base_url, config.base_api)), url: Some(format!("{}{}", config.base_url, config.base_api)),
authentication: auth, authentication: auth,
} }
} }
} }
impl Authentication{ impl Authentication {
/// Public constructor for getting authentication, provided by the configuration /// Public constructor for getting authentication, provided by the configuration
/// file. The most secure methods are checked first, filtering down to the least /// file. The most secure methods are checked first, filtering down to the least
/// secure methods. Currently, only two methods are supported: API token, and /// secure methods. Currently, only two methods are supported: API token, and
@ -61,33 +61,22 @@ impl Authentication{
// this is horror code, I know it // this is horror code, I know it
// match the damn thing // match the damn thing
// someone is going to take this and put it in r/badcode lol // someone is going to take this and put it in r/badcode lol
let basic_auth = config let basic_auth = config.password.as_ref().unwrap().to_string();
.password
.as_ref()
.unwrap()
.to_string();
let api_auth = config let api_auth = config.api_token.as_ref().unwrap().to_string();
.api_token
.as_ref()
.unwrap()
.to_string();
if api_auth.is_empty() { if api_auth.is_empty() {
if !(basic_auth.is_empty()) { if !(basic_auth.is_empty()) {
Authentication::with_basic(config Authentication::with_basic(
.username config.username.as_ref().unwrap().to_string(),
.as_ref() basic_auth,
.unwrap() )
.to_string(),
basic_auth)
} else { } else {
panic!("Must have some form of authentication! Exiting..."); panic!("Must have some form of authentication! Exiting...");
} }
} else { } else {
Authentication::with_api_token(api_auth) Authentication::with_api_token(config.username.as_ref().unwrap().to_string(), api_auth)
} }
} }
/// Private constructor once the public constructor figures out what kind of authentication /// Private constructor once the public constructor figures out what kind of authentication
@ -97,29 +86,25 @@ impl Authentication{
Authentication { Authentication {
basic: true, basic: true,
api_token: false, api_token: false,
credentials: (Some(user), Some(pass)) credentials: (Some(user), Some(pass)),
} }
} }
/// Private constructor once the public constructor figures out what kind of authentication /// Private constructor once the public constructor figures out what kind of authentication
/// is being used. This constructor uses the API token, a more secure way of authenticating /// is being used. This constructor uses the API token, a more secure way of authenticating
/// instead of using the basic username and password. /// instead of using the basic username and password.
fn with_api_token(api_token: String) -> Authentication { fn with_api_token(user: String, api_token: String) -> Authentication {
Authentication { Authentication {
basic: false, basic: false,
api_token: true, api_token: true,
credentials: (Some(api_token), None) credentials: (Some(user), Some(api_token)),
} }
} }
/// Public method that based on the what kind of authentication is being used, it can /// Public method that based on the what kind of authentication is being used, it can
/// determine what kind of requesting method is going to be used. See the Request /// determine what kind of requesting method is going to be used. See the Request
/// structure for more details. /// structure for more details.
pub fn request_chooser( pub fn request_chooser(self, config: Configuration, arg: ArgMatches<'static>) -> Request {
self,
config: Configuration,
arg: ArgMatches<'static>,
) -> Request {
if let true = self.api_token { if let true = self.api_token {
Request::with_api_request(config, arg.to_owned(), self) Request::with_api_request(config, arg.to_owned(), self)
} else { } else {

5
tests/test.rs Normal file
View File

@ -0,0 +1,5 @@
#[test]
fn test_add() {
let x = 2 + 2;
assert_eq!(x, 4);
}