fix and chore: apply clippy lints and create_gist returns gist public ID

fixes #1
master
Aravinth Manivannan 2022-02-19 13:15:38 +05:30
parent 4a8b72c7d5
commit 96be68c734
Signed by: realaravinth
GPG Key ID: AD9F0F08E855ED88
13 changed files with 87 additions and 106 deletions

View File

@ -14,6 +14,8 @@
//! - [errors](crate::auth): error data structures used in this crate
//! - [ops](crate::ops): meta operations like connection pool creation, migrations and getting
//! connection from pool
use std::str::FromStr;
use serde::{Deserialize, Serialize};
pub mod errors;
@ -90,9 +92,13 @@ impl GistVisibility {
GistVisibility::Public => "public",
}
}
}
impl FromStr for GistVisibility {
type Err = DBError;
/// Convert [str] to [GistVisibility]
pub fn from_str(s: &str) -> DBResult<Self> {
fn from_str(s: &str) -> DBResult<Self> {
const PRIVATE: &str = GistVisibility::Private.to_str();
const PUBLIC: &str = GistVisibility::Public.to_str();
const UNLISTED: &str = GistVisibility::Unlisted.to_str();
@ -366,7 +372,7 @@ impl GistDatabase for Box<dyn GistDatabase> {
/// Trait to clone GistDatabase
pub trait CloneGistDatabase {
/// clone DB
fn clone_db<'a>(&self) -> Box<dyn GistDatabase>;
fn clone_db(&self) -> Box<dyn GistDatabase>;
}
impl<T> CloneGistDatabase for T

View File

@ -88,13 +88,13 @@ pub async fn gists_work<T: GistDatabase>(
db.username_register(&register_payload).await.unwrap();
let create_gist = CreateGist {
owner: username.into(),
owner: username,
description: Some("foo"),
public_id,
visibility: &GistVisibility::Public,
};
assert!(!db.gist_exists(&create_gist.public_id).await.unwrap());
assert!(!db.gist_exists(create_gist.public_id).await.unwrap());
// create gist
assert!(db.get_user_gists(username).await.unwrap().is_empty());
@ -104,9 +104,9 @@ pub async fn gists_work<T: GistDatabase>(
Some(DBError::GistIDTaken)
));
assert!(db.gist_exists(&create_gist.public_id).await.unwrap());
assert!(db.gist_exists(create_gist.public_id).await.unwrap());
// get gist
let db_gist = db.get_gist(&create_gist.public_id).await.unwrap();
let db_gist = db.get_gist(create_gist.public_id).await.unwrap();
assert_gists(&create_gist, &db_gist);
let mut gists = db.get_user_gists(username).await.unwrap();
@ -116,14 +116,14 @@ pub async fn gists_work<T: GistDatabase>(
// comment on gist
let create_comment = CreateGistComment {
owner: username.into(),
owner: username,
gist_public_id: create_gist.public_id,
comment: "foo".into(),
comment: "foo",
};
db.new_comment(&create_comment).await.unwrap();
// get all comments on gist
let mut comments = db
.get_comments_on_gist(&create_gist.public_id)
.get_comments_on_gist(create_gist.public_id)
.await
.unwrap();
assert!(comments.len() == 1);
@ -144,14 +144,14 @@ pub async fn gists_work<T: GistDatabase>(
// visibility filters
let create_unlisted_gist = CreateGist {
owner: username.into(),
owner: username,
description: Some("foo"),
public_id: &format!("{}unlisted", public_id),
visibility: &GistVisibility::Unlisted,
};
db.new_gist(&create_unlisted_gist).await.unwrap();
let create_private_gist = CreateGist {
owner: username.into(),
owner: username,
description: Some("foo"),
public_id: &format!("{}private", public_id),
visibility: &GistVisibility::Private,
@ -186,15 +186,15 @@ pub async fn gists_work<T: GistDatabase>(
}
// delete gist
db.delete_gist(username, &create_gist.public_id)
db.delete_gist(username, create_gist.public_id)
.await
.unwrap();
assert!(matches!(
db.get_gist(&create_gist.public_id).await.err().unwrap(),
db.get_gist(create_gist.public_id).await.err().unwrap(),
DBError::GistNotFound
));
assert!(db
.get_comments_on_gist(&create_gist.public_id)
.get_comments_on_gist(create_gist.public_id)
.await
.unwrap()
.is_empty());
@ -269,7 +269,7 @@ pub async fn duplicate_secret_guard_works<T: GistDatabase>(
}
/// check if duplicate username and duplicate email guards are working on update workflows
#[allow(clippy::too_many_arguments)]
pub async fn duplicate_username_and_email<T: GistDatabase>(
db: &T,
username: &str,

View File

@ -2,6 +2,7 @@
//! # `libadmin` database operations implemented using sqlx postgres
//!
//! [`GistDatabase`](GistDatabase) is implemented on [Database].
use std::str::FromStr;
use db_core::dev::*;
@ -364,7 +365,7 @@ impl GistDatabase for Database {
Error::RowNotFound => DBError::GistNotFound,
e => DBError::DBError(Box::new(e)),
})?;
res.to_gist()
res.into_gist()
}
/// Retrieve gists belonging to user from database
@ -393,7 +394,7 @@ impl GistDatabase for Database {
let mut gists = Vec::with_capacity(res.len());
for r in res.drain(..) {
gists.push(r.to_gist()?);
gists.push(r.into_gist()?);
}
Ok(gists)
}
@ -429,7 +430,7 @@ impl GistDatabase for Database {
let mut gists = Vec::with_capacity(res.len());
for r in res.drain(..) {
gists.push(r.to_gist()?);
gists.push(r.into_gist()?);
}
Ok(gists)
}
@ -465,7 +466,7 @@ impl GistDatabase for Database {
let mut gists = Vec::with_capacity(res.len());
for r in res.drain(..) {
gists.push(r.to_gist()?);
gists.push(r.into_gist()?);
}
Ok(gists)
}
@ -611,7 +612,7 @@ struct InnerGist {
}
impl InnerGist {
fn to_gist(self) -> DBResult<Gist> {
fn into_gist(self) -> DBResult<Gist> {
Ok(Gist {
owner: self.owner.unwrap(),
description: self.description,

View File

@ -1,4 +1,5 @@
use db_core::dev::*;
use std::str::FromStr;
use sqlx::sqlite::SqlitePool;
use sqlx::sqlite::SqlitePoolOptions;
@ -327,7 +328,7 @@ impl GistDatabase for Database {
Error::RowNotFound => DBError::GistNotFound,
e => DBError::DBError(Box::new(e)),
})?;
res.to_gist()
res.into_gist()
}
/// Retrieve gists belonging to user from database
@ -356,7 +357,7 @@ impl GistDatabase for Database {
let mut gists = Vec::with_capacity(res.len());
for r in res.drain(..) {
gists.push(r.to_gist()?);
gists.push(r.into_gist()?);
}
Ok(gists)
}
@ -392,7 +393,7 @@ impl GistDatabase for Database {
let mut gists = Vec::with_capacity(res.len());
for r in res.drain(..) {
gists.push(r.to_gist()?);
gists.push(r.into_gist()?);
}
Ok(gists)
}
@ -428,7 +429,7 @@ impl GistDatabase for Database {
let mut gists = Vec::with_capacity(res.len());
for r in res.drain(..) {
gists.push(r.to_gist()?);
gists.push(r.into_gist()?);
}
Ok(gists)
}
@ -577,7 +578,7 @@ struct InnerGist {
}
impl InnerGist {
fn to_gist(self) -> DBResult<Gist> {
fn into_gist(self) -> DBResult<Gist> {
Ok(Gist {
owner: self.owner,
description: self.description,

View File

@ -21,10 +21,8 @@ use actix_web::test;
use super::*;
use crate::api::v1::ROUTES;
use crate::data::api::v1::account::*;
use crate::data::api::v1::auth::Password;
use crate::data::Data;
use crate::errors::*;
use crate::*;
use crate::tests::*;

View File

@ -38,7 +38,7 @@ impl CreateGistRequest {
pub fn to_create_gist<'a>(&'a self, owner: &'a str) -> CreateGist<'a> {
CreateGist {
owner,
description: self.description.as_ref().map(|s| s.as_str()),
description: self.description.as_deref(),
visibility: &self.visibility,
}
}
@ -50,6 +50,12 @@ pub fn services(cfg: &mut web::ServiceConfig) {
cfg.service(post_comment);
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CreateGistResp {
/// public ID
pub id: String,
}
#[my_codegen::post(
path = "crate::V1_API_ROUTES.gist.new",
wrap = "super::get_auth_middleware()"
@ -70,9 +76,8 @@ async fn new(
&payload.files,
)
.await?;
Ok(HttpResponse::TemporaryRedirect()
.insert_header((http::header::LOCATION, gist.id))
.finish())
let resp = CreateGistResp { id: gist.id };
Ok(HttpResponse::Ok().json(&resp))
}
#[my_codegen::get(path = "crate::V1_API_ROUTES.gist.get_file")]
@ -212,22 +217,18 @@ mod tests {
.to_request(),
)
.await;
assert_eq!(create_gist_resp.status(), StatusCode::TEMPORARY_REDIRECT);
assert_eq!(create_gist_resp.status(), StatusCode::OK);
let gist_id: CreateGistResp = test::read_body_json(create_gist_resp).await;
let gist_id = gist_id.id;
let gist_id = create_gist_resp
.headers()
.get(http::header::LOCATION)
.unwrap()
.to_str()
.unwrap();
data.gist_created_test_helper(db, gist_id, NAME).await;
data.gist_files_written_helper(db, gist_id, &files).await;
data.gist_created_test_helper(db, &gist_id, NAME).await;
data.gist_files_written_helper(db, &gist_id, &files).await;
// get gists
// 1. Public gists
let mut get_file_path = GetFilePath {
username: NAME.into(),
gist: gist_id.into(),
gist: gist_id.clone(),
file: "".into(),
};
for file in files.iter() {
@ -280,16 +281,11 @@ mod tests {
.to_request(),
)
.await;
assert_eq!(create_gist_resp.status(), StatusCode::TEMPORARY_REDIRECT);
assert_eq!(create_gist_resp.status(), StatusCode::OK);
let unlisted: CreateGistResp = test::read_body_json(create_gist_resp).await;
let unlisted = unlisted.id;
let unlisted = create_gist_resp
.headers()
.get(http::header::LOCATION)
.unwrap()
.to_str()
.unwrap();
get_file_path.gist = unlisted.into();
get_file_path.gist = unlisted.clone();
for file in one_file.iter() {
// requesting user is owner
get_file_path.file = file.filename.clone();
@ -335,16 +331,11 @@ mod tests {
.to_request(),
)
.await;
assert_eq!(create_gist_resp.status(), StatusCode::TEMPORARY_REDIRECT);
assert_eq!(create_gist_resp.status(), StatusCode::OK);
let private: CreateGistResp = test::read_body_json(create_gist_resp).await;
let private = private.id;
let private = create_gist_resp
.headers()
.get(http::header::LOCATION)
.unwrap()
.to_str()
.unwrap();
get_file_path.gist = private.into();
get_file_path.gist = private.clone();
for file in one_file.iter() {
get_file_path.file = file.filename.clone();
let path = V1_API_ROUTES.gist.get_file_route(&get_file_path);
@ -376,14 +367,14 @@ mod tests {
println!("testing comments");
let mut create_comment = PostCommentPath {
username: NAME2.into(),
gist: gist_id.into(),
gist: gist_id.clone(),
};
let mut comment = PostCommentRequest { comment: "".into() };
println!("empty comment");
// empty comment
data.bad_post_req_test(
&db,
db,
NAME,
PASSWORD,
V1_API_ROUTES.gist.post_comment,
@ -398,17 +389,17 @@ mod tests {
let post_comment_path = V1_API_ROUTES.gist.get_post_comment_route(&create_comment);
println!("gist not found");
data.bad_post_req_test(
&db,
db,
NAME,
PASSWORD,
V1_API_ROUTES.gist.post_comment,
&post_comment_path,
&comment,
ServiceError::GistNotFound,
)
.await;
println!("comment OK");
create_comment.gist = gist_id.into();
create_comment.gist = gist_id;
let post_comment_path = V1_API_ROUTES.gist.get_post_comment_route(&create_comment);
let resp = test::call_service(
&app,
@ -420,7 +411,7 @@ mod tests {
assert_eq!(resp.status(), StatusCode::OK);
println!("comment OK");
create_comment.gist = unlisted.into();
create_comment.gist = unlisted;
let post_comment_path = V1_API_ROUTES.gist.get_post_comment_route(&create_comment);
let resp = test::call_service(
&app,
@ -432,7 +423,7 @@ mod tests {
assert_eq!(resp.status(), StatusCode::OK);
println!("comment OK");
create_comment.gist = private.into();
create_comment.gist = private.clone();
let post_comment_path = V1_API_ROUTES.gist.get_post_comment_route(&create_comment);
let resp = test::call_service(
&app,
@ -445,10 +436,10 @@ mod tests {
// commenting on private gist
println!("private gist, not OK");
create_comment.gist = private.into();
create_comment.gist = private.clone();
let post_comment_path = V1_API_ROUTES.gist.get_post_comment_route(&create_comment);
data.bad_post_req_test(
&db,
db,
NAME2,
PASSWORD,
&post_comment_path,

View File

@ -103,7 +103,7 @@ mod tests {
for (db, data) in config.iter() {
let app = get_app!(data, db).await;
let resp = get_request!(&app, &V1_API_ROUTES.meta.health);
let resp = get_request!(&app, V1_API_ROUTES.meta.health);
assert_eq!(resp.status(), StatusCode::OK);
let health: Health = test::read_body_json(resp).await;

View File

@ -15,7 +15,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
//! V1 API Routes
use actix_auth_middleware::{Authentication, GetLoginRoute};
use actix_auth_middleware::GetLoginRoute;
use serde::*;
use super::meta::routes::Meta;
@ -168,9 +168,9 @@ impl Routes {
}
}
pub fn get_auth_middleware() -> Authentication<Routes> {
Authentication::with_identity(ROUTES)
}
//pub fn get_auth_middleware() -> Authentication<Routes> {
// Authentication::with_identity(ROUTES)
//}
impl GetLoginRoute for Routes {
fn get_login_route(&self, src: Option<&str>) -> String {

View File

@ -15,8 +15,6 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
//! Account management utility datastructures and methods
use core::panic;
use db_core::prelude::*;
use serde::{Deserialize, Serialize};

View File

@ -73,6 +73,12 @@ impl From<&'_ TreeEntry<'_>> for GitFileMode {
}
}
impl From<TreeEntry<'_>> for GitFileMode {
fn from(t: TreeEntry) -> Self {
GitFileMode::from(t.filemode() as isize)
}
}
pub struct Gist {
pub id: String,
pub repository: git2::Repository,
@ -194,7 +200,7 @@ impl Data {
let escaped_filename = escape_spaces(&file.filename);
match &file.content {
FileType::Dir(dir_contents) => unimplemented!(),
FileType::Dir(_dir_contents) => unimplemented!(),
FileType::File(f) => {
let obj = odb.write(ObjectType::Blob, f.as_bytes()).unwrap();
tree_builder
@ -260,8 +266,6 @@ impl Data {
let head = repo.head().unwrap();
let tree = head.peel_to_tree().unwrap();
let entry = tree.get_path(Path::new(path)).unwrap();
GitFileMode::Regular as i32;
fn read_file(id: Oid, repo: &git2::Repository) -> FileType {
let blob = repo.find_blob(id).unwrap();
FileType::File(ContentType::from_blob(&blob))
@ -271,8 +275,9 @@ impl Data {
let tree = repo.find_tree(id).unwrap();
let mut items = Vec::with_capacity(tree.len());
for item in tree.iter() {
println!("{:?}", &item.name());
println!("{:?}", item.name());
if let Some(name) = item.name() {
#[allow(clippy::needless_borrow)]
let mode: GitFileMode = (&item).into();
let file = match mode {
GitFileMode::Dir => read_dir(item.id(), repo),
@ -292,7 +297,7 @@ impl Data {
}
FileType::Dir(items)
}
let mode: GitFileMode = (&entry).into();
let mode: GitFileMode = entry.clone().into();
if let Some(name) = entry.name() {
let file = match mode {
GitFileMode::Dir => read_dir(entry.id(), repo),
@ -337,7 +342,7 @@ pub mod tests {
) {
let path = self.get_repository_path(gist_id);
assert!(path.exists());
assert!(db.gist_exists(&gist_id).await.unwrap());
assert!(db.gist_exists(gist_id).await.unwrap());
let repo = Repository::open(&path).unwrap();
assert!(repo.is_bare());
assert_eq!(db.get_gist(gist_id).await.unwrap().owner, owner);
@ -351,7 +356,7 @@ pub mod tests {
) {
for file in files.iter() {
let content = self
.read_file(db, GistID::ID(&gist_id), &escape_spaces(&file.filename))
.read_file(db, GistID::ID(gist_id), &escape_spaces(&file.filename))
.await
.unwrap();
let req_escaped_file = FileInfo {

View File

@ -47,7 +47,7 @@ impl Data {
#[cfg(not(tarpaulin_include))]
/// create new instance of app data
pub fn new(settings: Option<Settings>) -> Arc<Self> {
let settings = settings.unwrap_or(Settings::new().unwrap());
let settings = settings.unwrap_or_else(|| Settings::new().unwrap());
let creds = Self::get_creds();
let c = creds.clone();

View File

@ -20,8 +20,6 @@ use std::time::Duration;
use tokio::spawn;
use tokio::time::sleep;
use db_core::GistDatabase;
use crate::data::api::v1::auth::Register;
use crate::db::BoxDB;
use crate::*;
@ -90,21 +88,17 @@ mod tests {
async fn postgrest_demo_works() {
let (db, data) = sqlx_postgres::get_data().await;
let (db2, _) = sqlx_postgres::get_data().await;
demo_account_works(data, &db, db2).await;
demo_account_works(data, &db, &db2).await;
}
#[actix_rt::test]
async fn sqlite_demo_works() {
let (db, data) = sqlx_sqlite::get_data().await;
let (db2, _) = sqlx_sqlite::get_data().await;
demo_account_works(data, &db, db2).await;
demo_account_works(data, &db, &db2).await;
}
async fn demo_account_works(
data: Arc<Data>,
db: &Box<dyn GistDatabase>,
db2: Box<dyn GistDatabase>,
) {
async fn demo_account_works(data: Arc<Data>, db: &BoxDB, db2: &BoxDB) {
let _ = data.delete_user(db, DEMO_USER, DEMO_PASSWORD).await;
let data = AppData::new(data);
let duration = Duration::from_secs(DURATION);
@ -121,7 +115,7 @@ mod tests {
// deletion works
assert!(super::delete_demo_user(db, &data).await.is_ok());
assert!(!data.username_exists(db, DEMO_USER).await.unwrap().exists);
run(db2, data.clone(), duration).await.unwrap();
run(db2.clone(), data.clone(), duration).await.unwrap();
sleep(Duration::from_secs(DURATION)).await;
assert!(data.username_exists(db, DEMO_USER).await.unwrap().exists);

View File

@ -14,12 +14,6 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
use std::path::Path;
use tokio::fs;
use crate::errors::*;
/// Get random string of specific length
pub(crate) fn get_random(len: usize) -> String {
use rand::{distributions::Alphanumeric, rngs::ThreadRng, thread_rng, Rng};
@ -34,13 +28,6 @@ pub(crate) fn get_random(len: usize) -> String {
.collect::<String>()
}
pub async fn create_dir_all_if_not_exists(path: &Path) -> ServiceResult<()> {
if !path.exists() {
fs::create_dir_all(&path).await?;
}
Ok(())
}
pub fn escape_spaces(name: &str) -> String {
if name.contains(' ') {
name.replace(' ', "\\ ")