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::languages::Language;
|
||||||
use self::models::users::User;
|
use self::models::users::User;
|
||||||
use self::models::words::Word;
|
use self::models::words::Word;
|
||||||
|
|
||||||
use diesel::pg::PgConnection;
|
use diesel::pg::PgConnection;
|
||||||
use diesel::r2d2::{ConnectionManager, Pool, PooledConnection};
|
use diesel::r2d2::{ConnectionManager, Pool, PooledConnection};
|
||||||
|
use diesel::{insert_into, prelude::*};
|
||||||
|
|
||||||
use dotenvy::dotenv;
|
use dotenvy::dotenv;
|
||||||
|
use juniper::{graphql_value, DefaultScalarValue, FieldError, IntoFieldError};
|
||||||
use std::env;
|
use std::env;
|
||||||
|
use std::error::Error;
|
||||||
use tracing::info;
|
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 {
|
macro_rules! find_element {
|
||||||
($conn:expr,$dsl:ident,$type:ty,$value:expr,$errmsg:expr) => {
|
($conn:expr,$dsl:ident,$type:ty,$value:expr,$errmsg:expr) => {
|
||||||
if let Ok(val) = $conn {
|
if let Ok(val) = $conn {
|
||||||
@ -27,8 +68,6 @@ macro_rules! find_element {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
use diesel::prelude::*;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Database {
|
pub struct Database {
|
||||||
conn: Pool<ConnectionManager<PgConnection>>,
|
conn: Pool<ConnectionManager<PgConnection>>,
|
||||||
@ -59,17 +98,44 @@ impl Database {
|
|||||||
|
|
||||||
fn conn(
|
fn conn(
|
||||||
&self,
|
&self,
|
||||||
) -> Result<PooledConnection<ConnectionManager<PgConnection>>, ()> {
|
) -> Result<PooledConnection<ConnectionManager<PgConnection>>, DatabaseError>
|
||||||
self.conn
|
{
|
||||||
.get()
|
self.conn.get().map_err(|e| {
|
||||||
.map_err(|e| info!("Failed to connect to database: {:?}", 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;
|
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);
|
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> {
|
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> {
|
pub fn word_id(&self, id: &str) -> Option<Word> {
|
||||||
use self::schema::words::dsl;
|
use self::schema::words::dsl;
|
||||||
if let Ok(conn) = &mut self.conn() {
|
if let Ok(conn) = &mut self.conn() {
|
||||||
|
@ -5,8 +5,8 @@ use crate::graphql::Context;
|
|||||||
|
|
||||||
#[derive(Queryable, Insertable, Debug, Clone, PartialEq, Eq)]
|
#[derive(Queryable, Insertable, Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
id: String,
|
pub id: String,
|
||||||
username: String,
|
pub username: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[juniper::graphql_object(Context = Context)]
|
#[juniper::graphql_object(Context = Context)]
|
||||||
|
@ -1,16 +1,52 @@
|
|||||||
|
use juniper::FieldResult;
|
||||||
|
|
||||||
|
use crate::db::{models::users::User, DatabaseError};
|
||||||
|
|
||||||
use super::Context;
|
use super::Context;
|
||||||
|
|
||||||
pub struct Mutation;
|
pub struct Mutation;
|
||||||
|
|
||||||
#[juniper::graphql_object(Context = Context)]
|
#[juniper::graphql_object(Context = Context)]
|
||||||
impl Mutation {
|
impl Mutation {
|
||||||
fn api_version(
|
fn api_version(context: &Context) -> String {
|
||||||
context: &Context,
|
|
||||||
) -> String {
|
|
||||||
if context.user_auth {
|
if context.user_auth {
|
||||||
"0.1 (authentified)"
|
"0.1 (authentified)"
|
||||||
} else {
|
} else {
|
||||||
"0.1 (not authentified)"
|
"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 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::str::FromStr;
|
||||||
|
|
||||||
|
use std::convert::Into;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Query;
|
pub struct Query;
|
||||||
|
|
||||||
@ -16,6 +20,14 @@ impl Query {
|
|||||||
context.db.all_languages().unwrap()
|
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(
|
#[graphql(
|
||||||
description = "Retrieve a specific language from its name and its owner's id",
|
description = "Retrieve a specific language from its name and its owner's id",
|
||||||
arguments(
|
arguments(
|
||||||
|
Loading…
Reference in New Issue
Block a user