Manually add users to database, better handling of errors in API
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
Also manually remove users from database, and list them only as admin
This commit is contained in:
parent
8c62727ec9
commit
425e00acc1
118
src/db/mod.rs
118
src/db/mod.rs
@ -4,12 +4,53 @@ pub mod schema;
|
||||
use self::models::languages::Language;
|
||||
use self::models::users::User;
|
||||
use self::models::words::Word;
|
||||
|
||||
use diesel::pg::PgConnection;
|
||||
use diesel::r2d2::{ConnectionManager, Pool, PooledConnection};
|
||||
use diesel::{insert_into, prelude::*};
|
||||
|
||||
use dotenvy::dotenv;
|
||||
use juniper::{graphql_value, DefaultScalarValue, FieldError, IntoFieldError};
|
||||
use std::env;
|
||||
use std::error::Error;
|
||||
use tracing::info;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct DatabaseError {
|
||||
long: String,
|
||||
short: String,
|
||||
}
|
||||
|
||||
impl DatabaseError {
|
||||
pub fn new<S, T>(long: S, short: T) -> Self
|
||||
where
|
||||
T: ToString,
|
||||
S: ToString,
|
||||
{
|
||||
Self {
|
||||
long: long.to_string(),
|
||||
short: short.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for DatabaseError {}
|
||||
|
||||
impl std::fmt::Display for DatabaseError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.long)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoFieldError for DatabaseError {
|
||||
fn into_field_error(self) -> juniper::FieldError<DefaultScalarValue> {
|
||||
FieldError::new(
|
||||
self.long,
|
||||
graphql_value!({ "error": "Connection refused" }),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! find_element {
|
||||
($conn:expr,$dsl:ident,$type:ty,$value:expr,$errmsg:expr) => {
|
||||
if let Ok(val) = $conn {
|
||||
@ -27,8 +68,6 @@ macro_rules! find_element {
|
||||
};
|
||||
}
|
||||
|
||||
use diesel::prelude::*;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Database {
|
||||
conn: Pool<ConnectionManager<PgConnection>>,
|
||||
@ -59,17 +98,44 @@ impl Database {
|
||||
|
||||
fn conn(
|
||||
&self,
|
||||
) -> Result<PooledConnection<ConnectionManager<PgConnection>>, ()> {
|
||||
self.conn
|
||||
.get()
|
||||
.map_err(|e| info!("Failed to connect to database: {:?}", e))
|
||||
) -> Result<PooledConnection<ConnectionManager<PgConnection>>, DatabaseError>
|
||||
{
|
||||
self.conn.get().map_err(|e| {
|
||||
DatabaseError::new(
|
||||
format!("Failed to connect to database: {:?}", e),
|
||||
"Database connection error",
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn all_languages(&self) -> Result<Vec<Language>, ()> {
|
||||
pub fn all_languages(&self) -> Result<Vec<Language>, DatabaseError> {
|
||||
use self::schema::languages::dsl::languages;
|
||||
languages.load::<Language>(&mut self.conn()?).map_err(|e| {
|
||||
languages
|
||||
.load::<Language>(&mut self.conn()?)
|
||||
.map_err(|e| {
|
||||
info!("Failed to retrieve languages from database: {:?}", e);
|
||||
})
|
||||
.map_err(|e| {
|
||||
DatabaseError::new(
|
||||
format!("Failed to retrieve languages: {:?}", e),
|
||||
"Failed to retrieve languages",
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn all_users(&self) -> Result<Vec<User>, DatabaseError> {
|
||||
use self::schema::users::dsl::users;
|
||||
users
|
||||
.load::<User>(&mut self.conn()?)
|
||||
.map_err(|e| {
|
||||
info!("Failed to retrieve languages from database: {:?}", e);
|
||||
})
|
||||
.map_err(|e| {
|
||||
DatabaseError::new(
|
||||
format!("Failed to retrieve languages: {:?}", e),
|
||||
"Failed to retrieve languages",
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn language(&self, name: &str, owner: &str) -> Option<Language> {
|
||||
@ -105,6 +171,42 @@ impl Database {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn insert_user(
|
||||
&self,
|
||||
username: String,
|
||||
id: String,
|
||||
) -> Result<User, DatabaseError> {
|
||||
use self::schema::users::dsl::users;
|
||||
let user = User { id, username };
|
||||
match insert_into(users).values(user.clone()).execute(
|
||||
&mut self.conn().map_err(|e| {
|
||||
DatabaseError::new(
|
||||
format!("Failed to connect to the database: {:?}", e),
|
||||
"Connection error",
|
||||
)
|
||||
})?,
|
||||
) {
|
||||
Ok(_) => Ok(user),
|
||||
Err(e) => Err(DatabaseError {
|
||||
long: format!("Failed to insert user {:?}: {:?}", user, e),
|
||||
short: "Data insertion error".to_string(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn delete_user(&self, id: &str) -> Result<(), DatabaseError> {
|
||||
use self::schema::users::dsl::users;
|
||||
match diesel::delete(users.find(id.to_string()))
|
||||
.execute(&mut self.conn()?)
|
||||
{
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => Err(DatabaseError::new(
|
||||
format!("Failed to delete user {}: {:?}", id, e),
|
||||
"User deletion error",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn word_id(&self, id: &str) -> Option<Word> {
|
||||
use self::schema::words::dsl;
|
||||
if let Ok(conn) = &mut self.conn() {
|
||||
|
@ -5,8 +5,8 @@ use crate::graphql::Context;
|
||||
|
||||
#[derive(Queryable, Insertable, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct User {
|
||||
id: String,
|
||||
username: String,
|
||||
pub id: String,
|
||||
pub username: String,
|
||||
}
|
||||
|
||||
#[juniper::graphql_object(Context = Context)]
|
||||
|
@ -1,16 +1,52 @@
|
||||
use juniper::FieldResult;
|
||||
|
||||
use crate::db::{models::users::User, DatabaseError};
|
||||
|
||||
use super::Context;
|
||||
|
||||
pub struct Mutation;
|
||||
|
||||
#[juniper::graphql_object(Context = Context)]
|
||||
impl Mutation {
|
||||
fn api_version(
|
||||
context: &Context,
|
||||
) -> String {
|
||||
fn api_version(context: &Context) -> String {
|
||||
if context.user_auth {
|
||||
"0.1 (authentified)"
|
||||
} else {
|
||||
"0.1 (not authentified)"
|
||||
}.into()
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn db_only_new_user(
|
||||
context: &Context,
|
||||
username: String,
|
||||
id: String,
|
||||
admin_key: String,
|
||||
) -> FieldResult<User> {
|
||||
if admin_key == context.other_vars.admin_key {
|
||||
context
|
||||
.db
|
||||
.insert_user(username, id)
|
||||
.map_err(std::convert::Into::into)
|
||||
} else {
|
||||
Err(DatabaseError::new("Invalid admin key", "Invalid admin key")
|
||||
.into())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn db_only_delete_user(
|
||||
context: &Context,
|
||||
id: String,
|
||||
admin_key: String,
|
||||
) -> FieldResult<String> {
|
||||
if admin_key == context.other_vars.admin_key {
|
||||
match context.db.delete_user(&id) {
|
||||
Ok(_) => Ok("done".into()),
|
||||
Err(e) => Err(e.into()),
|
||||
}
|
||||
} else {
|
||||
Err(DatabaseError::new("Invalid admin key", "Invalid admin key")
|
||||
.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,12 @@
|
||||
use juniper::FieldResult;
|
||||
|
||||
use super::Context;
|
||||
use crate::db::models::{languages::Language, users::User, words::Word};
|
||||
use crate::db::{models::{languages::Language, users::User, words::Word}, DatabaseError};
|
||||
|
||||
use std::str::FromStr;
|
||||
|
||||
use std::convert::Into;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Query;
|
||||
|
||||
@ -16,6 +20,14 @@ impl Query {
|
||||
context.db.all_languages().unwrap()
|
||||
}
|
||||
|
||||
fn all_users(context: &Context, admin_key: String) -> FieldResult<Vec<User>> {
|
||||
if admin_key == context.other_vars.admin_key {
|
||||
context.db.all_users().map_err(Into::into)
|
||||
} else {
|
||||
Err(DatabaseError::new("Invalid admin key", "Invalid admin key").into())
|
||||
}
|
||||
}
|
||||
|
||||
#[graphql(
|
||||
description = "Retrieve a specific language from its name and its owner's id",
|
||||
arguments(
|
||||
|
Loading…
Reference in New Issue
Block a user