wip: add base64 encoding to impl, add custom errors to impl
Some checks failed
Rust / build (push) Failing after 7m58s
Some checks failed
Rust / build (push) Failing after 7m58s
This commit is contained in:
parent
8e85ceebaf
commit
02c62b7ba6
87
src/issue.rs
87
src/issue.rs
@ -2,11 +2,12 @@ use std::collections::HashMap;
|
|||||||
|
|
||||||
use colored::*;
|
use colored::*;
|
||||||
use reqwest::blocking::Client;
|
use reqwest::blocking::Client;
|
||||||
use reqwest::{StatusCode, Response};
|
use reqwest::{Response, StatusCode};
|
||||||
use serde_derive::{Serialize, Deserialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use crate::request::{Request, Authentication, AuthenticationType};
|
use crate::error;
|
||||||
|
use crate::request::{AuthenticationType, Request};
|
||||||
use crate::util;
|
use crate::util;
|
||||||
|
|
||||||
pub struct Issue;
|
pub struct Issue;
|
||||||
@ -44,6 +45,12 @@ pub struct IssueResponse {
|
|||||||
pub repository: Repository,
|
pub repository: Repository,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize)]
|
||||||
|
pub struct IssueErrorResponse {
|
||||||
|
pub message: String,
|
||||||
|
pub url: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
pub id: i64,
|
pub id: i64,
|
||||||
@ -67,30 +74,18 @@ pub struct Repository {
|
|||||||
pub full_name: String,
|
pub full_name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
// trait IssueRequest {
|
|
||||||
// fn client() -> &'static Client;
|
|
||||||
// fn url_request_decision() -> String;
|
|
||||||
// fn arg_value_decision() -> Vec<&'static str>;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// impl IssueRequest for Issue {
|
|
||||||
// fn client() -> &'static Client {
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
impl Issue {
|
impl Issue {
|
||||||
pub fn new() -> Issue {
|
pub fn new() -> Issue {
|
||||||
Issue {}
|
Issue {}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_issue(&self, request: &Request) {
|
pub fn create_issue(&self, request: &Request) -> Result<(), error::ErrorKind> {
|
||||||
let issue_title: String;
|
let issue_title: String;
|
||||||
let issue_description: String;
|
let issue_description: String;
|
||||||
let _issue_assignee: Option<String>;
|
let _issue_assignee: Option<String>;
|
||||||
let _issue_milestone: Option<String>;
|
let _issue_milestone: Option<String>;
|
||||||
let _issue_tags: Option<String>;
|
let _issue_tags: Option<String>;
|
||||||
|
|
||||||
let client = &request.client;
|
let client = &request.client;
|
||||||
let arg_value: Vec<&str> = request
|
let arg_value: Vec<&str> = request
|
||||||
.arg_value
|
.arg_value
|
||||||
@ -119,51 +114,60 @@ impl Issue {
|
|||||||
let response = client.post(&url).json(&map).send();
|
let response = client.post(&url).json(&map).send();
|
||||||
|
|
||||||
match response {
|
match response {
|
||||||
Ok(repo) => {
|
Ok(repo) => match repo.status() {
|
||||||
match repo.status() {
|
StatusCode::CREATED => println!("{}", "Issue successfully created!".green()),
|
||||||
StatusCode::CREATED => println!("{}", "Issue successfully created!".green()),
|
StatusCode::FORBIDDEN => println!("{}", "Issue creation forbidden!".red()),
|
||||||
StatusCode::FORBIDDEN => println!("{}", "Issue creation forbidden!".red()),
|
StatusCode::UNPROCESSABLE_ENTITY => {
|
||||||
StatusCode::UNPROCESSABLE_ENTITY => println!("{}", "Issue input validation failed!".red()),
|
println!("{}", "Issue input validation failed!".red())
|
||||||
_ => println!(),
|
|
||||||
}
|
}
|
||||||
|
_ => println!(),
|
||||||
},
|
},
|
||||||
Err(e) => panic!("{}", e),
|
Err(e) => panic!("{}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn list_issue(&self, request: &Request) {
|
pub fn list_issue(&self, request: &Request) -> Result<(), error::ErrorKind> {
|
||||||
let client = &request.client;
|
let client = &request.client;
|
||||||
let url = self.url_request_decision(&request);
|
let url = self.url_request_decision(&request);
|
||||||
let response = client.get(&url).send();
|
let response = client.get(&url).send();
|
||||||
|
|
||||||
// TODO: fix this to match context
|
// TODO: fix this to match context
|
||||||
match response {
|
match response {
|
||||||
Ok(repo) => {
|
Ok(repo) => match repo.status() {
|
||||||
match repo.status() {
|
StatusCode::CREATED => {
|
||||||
StatusCode::CREATED => println!("{}", "Issue successfully created!".green()),
|
println!("{}", "Issue successfully created!".green());
|
||||||
StatusCode::FORBIDDEN => println!("{}", "Issue creation forbidden!".red()),
|
Ok(())
|
||||||
StatusCode::UNPROCESSABLE_ENTITY => println!("{}", "Issue input validation failed!".red()),
|
|
||||||
_ => println!(),
|
|
||||||
}
|
}
|
||||||
|
StatusCode::FORBIDDEN => {
|
||||||
|
let deserialized = repo.json().unwrap();
|
||||||
|
Err(error::ErrorKind::ForbiddenRequest(deserialized.message))
|
||||||
|
}
|
||||||
|
StatusCode::UNPROCESSABLE_ENTITY => {
|
||||||
|
let deserialized = repo.json().unwrap();
|
||||||
|
Err(error::ErrorKind::UnprocessiableRequest(
|
||||||
|
deserialized.message,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
_ => Err(error::ErrorKind::Other),
|
||||||
},
|
},
|
||||||
Err(e) => panic!("{}", e),
|
Err(e) => panic!("{}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn search_issue(&self, request: &Request) {
|
pub fn search_issue(&self, request: &Request) -> Result<(), error::ErrorKind> {
|
||||||
let client = &request.client;
|
let client = &request.client;
|
||||||
let url = self.url_request_decision(&request);
|
let url = self.url_request_decision(&request);
|
||||||
let response = client.get(&url).send();
|
let response = client.get(&url).send();
|
||||||
|
|
||||||
// TODO: fix this to match context
|
// TODO: fix this to match context
|
||||||
match response {
|
match response {
|
||||||
Ok(repo) => {
|
Ok(repo) => match repo.status() {
|
||||||
match repo.status() {
|
StatusCode::CREATED => println!("{}", "Issue successfully created!".green()),
|
||||||
StatusCode::CREATED => println!("{}", "Issue successfully created!".green()),
|
StatusCode::FORBIDDEN => println!("{}", "Issue creation forbidden!".red()),
|
||||||
StatusCode::FORBIDDEN => println!("{}", "Issue creation forbidden!".red()),
|
StatusCode::UNPROCESSABLE_ENTITY => {
|
||||||
StatusCode::UNPROCESSABLE_ENTITY => println!("{}", "Issue input validation failed!".red()),
|
println!("{}", "Issue input validation failed!".red())
|
||||||
_ => println!(),
|
|
||||||
}
|
}
|
||||||
|
_ => println!(),
|
||||||
},
|
},
|
||||||
Err(e) => panic!("{}", e),
|
Err(e) => panic!("{}", e),
|
||||||
}
|
}
|
||||||
@ -179,7 +183,7 @@ impl Issue {
|
|||||||
.values_of("search")
|
.values_of("search")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
// TODO: fix the url formatting for basic auth
|
// TODO: fix the url formatting for basic auth
|
||||||
match &request.authentication.auth_type {
|
match &request.authentication.auth_type {
|
||||||
AuthenticationType::ApiToken => {
|
AuthenticationType::ApiToken => {
|
||||||
@ -190,7 +194,7 @@ impl Issue {
|
|||||||
repo = arg_value[1],
|
repo = arg_value[1],
|
||||||
api_token = request.authentication.credentials.1.as_ref().unwrap()
|
api_token = request.authentication.credentials.1.as_ref().unwrap()
|
||||||
)
|
)
|
||||||
},
|
}
|
||||||
AuthenticationType::BasicAuth => {
|
AuthenticationType::BasicAuth => {
|
||||||
format!(
|
format!(
|
||||||
"{request}/repos/{owner}/{repo}/issues?token={api_token}",
|
"{request}/repos/{owner}/{repo}/issues?token={api_token}",
|
||||||
@ -199,10 +203,9 @@ impl Issue {
|
|||||||
repo = arg_value[1],
|
repo = arg_value[1],
|
||||||
api_token = request.authentication.credentials.1.as_ref().unwrap()
|
api_token = request.authentication.credentials.1.as_ref().unwrap()
|
||||||
)
|
)
|
||||||
},
|
}
|
||||||
// this case _shouldn't_ happen but ya know
|
// this case _shouldn't_ happen but ya know
|
||||||
AuthenticationType::None => panic!("idk what happened man you wrote the code 🤷")
|
AuthenticationType::None => panic!("idk what happened man you wrote the code 🤷"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
mod arg;
|
mod arg;
|
||||||
mod config;
|
mod config;
|
||||||
|
mod error;
|
||||||
mod generate;
|
mod generate;
|
||||||
mod issue;
|
mod issue;
|
||||||
mod pr;
|
mod pr;
|
||||||
|
38
src/repo.rs
38
src/repo.rs
@ -7,6 +7,7 @@ use serde_derive::{Deserialize, Serialize};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::Configuration,
|
config::Configuration,
|
||||||
|
error,
|
||||||
request::Request,
|
request::Request,
|
||||||
util::{self, ErrorKind},
|
util::{self, ErrorKind},
|
||||||
};
|
};
|
||||||
@ -112,7 +113,7 @@ impl Repository {
|
|||||||
Repository {}
|
Repository {}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_repo(&self, request: &Request) {
|
pub fn create_repo(&self, request: &Request) -> Result<(), error::ErrorKind> {
|
||||||
let client = &request.client;
|
let client = &request.client;
|
||||||
let arg_value = request
|
let arg_value = request
|
||||||
.arg_value
|
.arg_value
|
||||||
@ -141,33 +142,30 @@ impl Repository {
|
|||||||
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);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
StatusCode::BAD_REQUEST => {
|
StatusCode::BAD_REQUEST => {
|
||||||
// TODO: implement error handling for the deserialization
|
// TODO: implement error handling for the deserialization
|
||||||
let deserialized: RepositoryErrorResponse = repo.json().unwrap();
|
let deserialized: RepositoryErrorResponse = repo.json().unwrap();
|
||||||
println!(
|
Err(error::ErrorKind::BadRequest(deserialized.message))
|
||||||
"{}",
|
}
|
||||||
util::bad_response_message(&deserialized.message, ErrorKind::BadRequest)
|
StatusCode::CONFLICT => {
|
||||||
.red()
|
let deserialized: RepositoryErrorResponse = repo.json().unwrap();
|
||||||
);
|
Err(error::ErrorKind::Conflict(deserialized.message))
|
||||||
}
|
}
|
||||||
StatusCode::CONFLICT => println!(
|
|
||||||
"{}",
|
|
||||||
"Repository has the same name or already exists!".red()
|
|
||||||
),
|
|
||||||
StatusCode::UNPROCESSABLE_ENTITY => {
|
StatusCode::UNPROCESSABLE_ENTITY => {
|
||||||
println!("{}", "Repository input validation failed!".red())
|
let deserialized: RepositoryErrorResponse = repo.json().unwrap();
|
||||||
|
Err(error::ErrorKind::UnprocessiableRequest(
|
||||||
|
deserialized.message,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
_ => println!(
|
_ => Err(error::ErrorKind::Other),
|
||||||
"Repository creation failed! HTTP status code: {}",
|
|
||||||
repo.status().as_str()
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
Err(e) => panic!("{}", e),
|
Err(_e) => Err(error::ErrorKind::Other),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_repo(&self, request: &Request) {
|
pub fn delete_repo(&self, request: &Request) -> Result<(), error::ErrorKind> {
|
||||||
let client = &request.client;
|
let client = &request.client;
|
||||||
let arg_value: Vec<&str> = request
|
let arg_value: Vec<&str> = request
|
||||||
.arg_value
|
.arg_value
|
||||||
@ -205,7 +203,7 @@ impl Repository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fork_repo(&self, request: &Request) {
|
pub fn fork_repo(&self, request: &Request) -> Result<(), error::ErrorKind> {
|
||||||
let client = &request.client;
|
let client = &request.client;
|
||||||
let arg_item: Vec<&str> = request
|
let arg_item: Vec<&str> = request
|
||||||
.arg_value
|
.arg_value
|
||||||
@ -259,7 +257,7 @@ impl Repository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn search_repo(&self, request: &Request) {
|
pub fn search_repo(&self, request: &Request) -> Result<(), error::ErrorKind> {
|
||||||
let client = &request.client;
|
let client = &request.client;
|
||||||
let arg_value = request
|
let arg_value = request
|
||||||
.arg_value
|
.arg_value
|
||||||
@ -312,7 +310,7 @@ impl Repository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn list_repo(&self, request: &Request) {
|
pub fn list_repo(&self, request: &Request) -> Result<(), error::ErrorKind> {
|
||||||
let client = &request.client;
|
let client = &request.client;
|
||||||
let url = format!(
|
let url = format!(
|
||||||
"{request}/repos/search?token={api_token}",
|
"{request}/repos/search?token={api_token}",
|
||||||
|
78
src/user.rs
78
src/user.rs
@ -1,6 +1,7 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
error,
|
||||||
request::Request,
|
request::Request,
|
||||||
util::{self, ErrorKind},
|
util::{self, ErrorKind},
|
||||||
};
|
};
|
||||||
@ -47,7 +48,7 @@ impl User {
|
|||||||
User {}
|
User {}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_user(&self, request: &Request) {
|
pub fn create_user(&self, request: &Request) -> Result<(), error::ErrorKind> {
|
||||||
let client = &request.client;
|
let client = &request.client;
|
||||||
let mut user_create_input: HashMap<String, String> = HashMap::new();
|
let mut user_create_input: HashMap<String, String> = HashMap::new();
|
||||||
let url = format!(
|
let url = format!(
|
||||||
@ -65,6 +66,7 @@ impl User {
|
|||||||
user_create_input.insert("password".to_string(), password);
|
user_create_input.insert("password".to_string(), password);
|
||||||
|
|
||||||
let response = client.post(url.as_str()).json(&user_create_input).send();
|
let response = client.post(url.as_str()).json(&user_create_input).send();
|
||||||
|
// TODO: match return type
|
||||||
match response {
|
match response {
|
||||||
Ok(repo) => match repo.status() {
|
Ok(repo) => match repo.status() {
|
||||||
StatusCode::CREATED => {
|
StatusCode::CREATED => {
|
||||||
@ -74,33 +76,23 @@ impl User {
|
|||||||
"\tFull Name: {:0}\n\tUsername: {:1}\n\tEmail: {:2}\n",
|
"\tFull Name: {:0}\n\tUsername: {:1}\n\tEmail: {:2}\n",
|
||||||
deserialized.full_name, deserialized.login, deserialized.email
|
deserialized.full_name, deserialized.login, deserialized.email
|
||||||
);
|
);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
StatusCode::BAD_REQUEST => {
|
StatusCode::BAD_REQUEST => {
|
||||||
let deserialized: UserErrorResponse = repo.json().unwrap();
|
let deserialized: UserErrorResponse = repo.json().unwrap();
|
||||||
println!(
|
Err(error::ErrorKind::BadRequest(deserialized.message))
|
||||||
"{}",
|
|
||||||
util::bad_response_message(&deserialized.message, ErrorKind::BadRequest)
|
|
||||||
.red()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
StatusCode::FORBIDDEN => {
|
StatusCode::FORBIDDEN => {
|
||||||
let deserialized: UserErrorResponse = repo.json().unwrap();
|
let deserialized: UserErrorResponse = repo.json().unwrap();
|
||||||
println!(
|
Err(error::ErrorKind::ForbiddenRequest(deserialized.message))
|
||||||
"{}",
|
|
||||||
util::bad_response_message(
|
|
||||||
&deserialized.message,
|
|
||||||
ErrorKind::ForbiddenRequest
|
|
||||||
)
|
|
||||||
.red()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
_ => println!("¯\\_(ツ)_/¯"),
|
_ => Err(error::ErrorKind::Other),
|
||||||
},
|
},
|
||||||
Err(e) => println!("{}", e),
|
Err(_e) => Err(error::ErrorKind::Other),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn list_user(&self, request: &Request) {
|
pub fn list_user(&self, request: &Request) -> Result<(), error::ErrorKind> {
|
||||||
let client = &request.client;
|
let client = &request.client;
|
||||||
|
|
||||||
let url = format!(
|
let url = format!(
|
||||||
@ -127,40 +119,30 @@ impl User {
|
|||||||
"Total number of users indexed: {}",
|
"Total number of users indexed: {}",
|
||||||
deserialized.data.len()
|
deserialized.data.len()
|
||||||
);
|
);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
false => {
|
||||||
|
println!("{}", "No users found :(");
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
false => println!("{}", "No users found :("),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StatusCode::BAD_REQUEST => {
|
StatusCode::BAD_REQUEST => {
|
||||||
let deserialized: UserErrorResponse = repo.json().unwrap(); // TODO: handle
|
let deserialized: UserErrorResponse = repo.json().unwrap(); // TODO: handle
|
||||||
println!(
|
Err(error::ErrorKind::BadRequest(deserialized.message))
|
||||||
"{}",
|
|
||||||
util::bad_response_message(
|
|
||||||
&deserialized.message,
|
|
||||||
ErrorKind::BadRequest
|
|
||||||
)
|
|
||||||
.red()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
StatusCode::FORBIDDEN => {
|
StatusCode::FORBIDDEN => {
|
||||||
let deserialzed: UserErrorResponse = repo.json().unwrap(); // TODO: handle errs
|
let deserialzed: UserErrorResponse = repo.json().unwrap(); // TODO: handle errs
|
||||||
println!(
|
Err(error::ErrorKind::ForbiddenRequest(deserialzed.message))
|
||||||
"{}",
|
|
||||||
util::bad_response_message(
|
|
||||||
&deserialzed.message,
|
|
||||||
ErrorKind::ForbiddenRequest
|
|
||||||
)
|
|
||||||
.red()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
_ => println!("huh?"),
|
_ => Err(error::ErrorKind::Other),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => panic!("{}", e),
|
Err(_e) => Err(error::ErrorKind::Other),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn search_user(&self, request: &Request) {
|
pub fn search_user(&self, request: &Request) -> Result<(), error::ErrorKind> {
|
||||||
let client = &request.client;
|
let client = &request.client;
|
||||||
let arg_value = request
|
let arg_value = request
|
||||||
.arg_value
|
.arg_value
|
||||||
@ -181,13 +163,14 @@ impl User {
|
|||||||
|
|
||||||
match response {
|
match response {
|
||||||
Ok(repo) => match repo.status() {
|
Ok(repo) => match repo.status() {
|
||||||
_ => println!(""),
|
StatusCode::OK => Ok(()),
|
||||||
|
_ => Err(error::ErrorKind::Other),
|
||||||
},
|
},
|
||||||
Err(e) => panic!("{}", e),
|
Err(_e) => Err(error::ErrorKind::Other),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_user(&self, request: &Request) {
|
pub fn delete_user(&self, request: &Request) -> Result<(), error::ErrorKind> {
|
||||||
let client = &request.client;
|
let client = &request.client;
|
||||||
let arg_value = request
|
let arg_value = request
|
||||||
.arg_value
|
.arg_value
|
||||||
@ -207,21 +190,16 @@ impl User {
|
|||||||
match response {
|
match response {
|
||||||
Ok(repo) => match repo.status() {
|
Ok(repo) => match repo.status() {
|
||||||
StatusCode::NO_CONTENT => {
|
StatusCode::NO_CONTENT => {
|
||||||
println!("User successfully deleted!")
|
println!("User successfully deleted!");
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
StatusCode::FORBIDDEN => {
|
StatusCode::FORBIDDEN => {
|
||||||
let deserialized: UserErrorResponse = repo.json().unwrap();
|
let deserialized: UserErrorResponse = repo.json().unwrap();
|
||||||
println!(
|
Err(error::ErrorKind::ForbiddenRequest(deserialized.message))
|
||||||
"{}",
|
|
||||||
util::bad_response_message(
|
|
||||||
&deserialized.message,
|
|
||||||
ErrorKind::ForbiddenRequest
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
_ => println!(""),
|
_ => Err(error::ErrorKind::Other),
|
||||||
},
|
},
|
||||||
Err(e) => panic!("{}", e),
|
Err(_e) => Err(error::ErrorKind::Other),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user