refactor: introduce user changeset
This commit is contained in:
parent
128fca8138
commit
cd20457f67
15
.sqlx/query-348365fd1e76ebfcfa065d1ea7e22cd7cbcd2e981f75fce75f29f1c4fbfc3df5.json
generated
Normal file
15
.sqlx/query-348365fd1e76ebfcfa065d1ea7e22cd7cbcd2e981f75fce75f29f1c4fbfc3df5.json
generated
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "UPDATE user_ SET locked = $1 WHERE id = $2;",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Bool",
|
||||||
|
"Int4"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "348365fd1e76ebfcfa065d1ea7e22cd7cbcd2e981f75fce75f29f1c4fbfc3df5"
|
||||||
|
}
|
15
.sqlx/query-3e246c54d31804140272a6fc2e3c241c17b086ff219d6084684a0f2b7c31eeed.json
generated
Normal file
15
.sqlx/query-3e246c54d31804140272a6fc2e3c241c17b086ff219d6084684a0f2b7c31eeed.json
generated
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "UPDATE user_ SET receiveNotifications = $1 WHERE id = $2;",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Bool",
|
||||||
|
"Int4"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "3e246c54d31804140272a6fc2e3c241c17b086ff219d6084684a0f2b7c31eeed"
|
||||||
|
}
|
16
.sqlx/query-868bbdcb65f0ee862f221b7e3d1a4f4dbc4d818315d1713a110c4ad7acd09e3e.json
generated
Normal file
16
.sqlx/query-868bbdcb65f0ee862f221b7e3d1a4f4dbc4d818315d1713a110c4ad7acd09e3e.json
generated
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "UPDATE user_ SET password = $1, salt = $2 WHERE id = $3;",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text",
|
||||||
|
"Text",
|
||||||
|
"Int4"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "868bbdcb65f0ee862f221b7e3d1a4f4dbc4d818315d1713a110c4ad7acd09e3e"
|
||||||
|
}
|
41
.sqlx/query-fd2f782d28612d969aa20eb35ea8da4bfdba6f059dbd45b510122210807ac5b6.json
generated
Normal file
41
.sqlx/query-fd2f782d28612d969aa20eb35ea8da4bfdba6f059dbd45b510122210807ac5b6.json
generated
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"db_name": "PostgreSQL",
|
||||||
|
"query": "UPDATE user_ SET name = $1, email = $2, role = $3, function = $4, areaId = $5 WHERE id = $6;",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Text",
|
||||||
|
"Text",
|
||||||
|
{
|
||||||
|
"Custom": {
|
||||||
|
"name": "role",
|
||||||
|
"kind": {
|
||||||
|
"Enum": [
|
||||||
|
"staff",
|
||||||
|
"areamanager",
|
||||||
|
"admin"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Custom": {
|
||||||
|
"name": "function",
|
||||||
|
"kind": {
|
||||||
|
"Enum": [
|
||||||
|
"posten",
|
||||||
|
"fuehrungsassistent",
|
||||||
|
"wachhabender"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Int4",
|
||||||
|
"Int4"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "fd2f782d28612d969aa20eb35ea8da4bfdba6f059dbd45b510122210807ac5b6"
|
||||||
|
}
|
16
Cargo.lock
generated
16
Cargo.lock
generated
@ -1245,6 +1245,18 @@ version = "0.15.7"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
|
checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dummy"
|
||||||
|
version = "0.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b3ee4e39146145f7dd28e6c85ffdce489d93c0d9c88121063b8aacabbd9858d2"
|
||||||
|
dependencies = [
|
||||||
|
"darling",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.96",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.13.0"
|
version = "1.13.0"
|
||||||
@ -1347,6 +1359,7 @@ checksum = "aef603df4ba9adbca6a332db7da6f614f21eafefbaf8e087844e452fdec152d0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"deunicode",
|
"deunicode",
|
||||||
|
"dummy",
|
||||||
"rand",
|
"rand",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1549,6 +1562,8 @@ checksum = "f4bd1d7843e437a4caf1d6a9112ba1ee9635b09d909af22aa4e6ec01fe971e22"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"compact_str",
|
"compact_str",
|
||||||
"garde_derive",
|
"garde_derive",
|
||||||
|
"once_cell",
|
||||||
|
"regex",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1560,6 +1575,7 @@ checksum = "a0636cbdc03994db48fc89a0ce7765bd68d08bd8a7a68cb9a36bcde96790f413"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
"regex",
|
||||||
"syn 2.0.96",
|
"syn 2.0.96",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ brass-macros = { path = "../macros" }
|
|||||||
brass-config = { path = "../config" }
|
brass-config = { path = "../config" }
|
||||||
actix-http = "3.9.0"
|
actix-http = "3.9.0"
|
||||||
rinja = "0.3.5"
|
rinja = "0.3.5"
|
||||||
garde = { version = "0.21.0", features = ["derive"] }
|
garde = { version = "0.21.0", features = ["derive", "email"] }
|
||||||
maud = "0.26.0"
|
maud = "0.26.0"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
@ -40,5 +40,5 @@ change-detection = "1.2.0"
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
insta = { version = "1.41.1", features = ["yaml", "filters"] }
|
insta = { version = "1.41.1", features = ["yaml", "filters"] }
|
||||||
fake = { version = "3.0.1", features = ["chrono"]}
|
fake = { version = "3.0.1", features = ["chrono", "derive"]}
|
||||||
regex = "1.11.1"
|
regex = "1.11.1"
|
||||||
|
@ -123,20 +123,7 @@ async fn handle_password_change_request(
|
|||||||
|
|
||||||
let (hash, salt) = generate_salt_and_hash_plain_password(password).unwrap();
|
let (hash, salt) = generate_salt_and_hash_plain_password(password).unwrap();
|
||||||
|
|
||||||
User::update(
|
User::update_password(pool, user_id, &hash, &salt).await?;
|
||||||
pool,
|
|
||||||
user_id,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
Some(&hash),
|
|
||||||
Some(&salt),
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
if let Some(token) = token {
|
if let Some(token) = token {
|
||||||
token.delete(pool).await?;
|
token.delete(pool).await?;
|
||||||
|
@ -1,24 +1,16 @@
|
|||||||
use actix_web::{http::header::LOCATION, web, HttpResponse, Responder};
|
use actix_web::{http::header::LOCATION, web, HttpResponse, Responder};
|
||||||
use brass_macros::db_test;
|
use garde::Validate;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::Deserialize;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
endpoints::IdPath,
|
endpoints::IdPath,
|
||||||
models::{Function, Role, User},
|
models::{Area, Role, User, UserChangeset},
|
||||||
utils::ApplicationError,
|
utils::ApplicationError,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[derive(Deserialize)]
|
||||||
use crate::utils::test_helper::{test_post, DbTestContext, RequestConfig, StatusCode};
|
#[cfg_attr(test, derive(serde::Serialize))]
|
||||||
#[cfg(test)]
|
|
||||||
use fake::{
|
|
||||||
faker::{internet::raw::SafeEmail, name::raw::Name},
|
|
||||||
locales::EN,
|
|
||||||
Fake,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize)]
|
|
||||||
pub struct EditUserForm {
|
pub struct EditUserForm {
|
||||||
email: String,
|
email: String,
|
||||||
name: String,
|
name: String,
|
||||||
@ -50,146 +42,100 @@ pub async fn post_edit(
|
|||||||
return Err(ApplicationError::Unauthorized);
|
return Err(ApplicationError::Unauthorized);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut changed = false;
|
let area_id = form.area.unwrap_or(user_in_db.area_id);
|
||||||
|
if Area::read_by_id(pool.get_ref(), area_id).await?.is_none() {
|
||||||
let email = if user_in_db.email != form.email {
|
return Err(ApplicationError::Unauthorized);
|
||||||
changed = true;
|
|
||||||
Some(form.email.as_str())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
let name = if user_in_db.name != form.name {
|
|
||||||
changed = true;
|
|
||||||
Some(form.name.as_str())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
let role = if user_in_db.role as u8 != form.role {
|
|
||||||
if let Ok(r) = Role::try_from(form.role) {
|
|
||||||
changed = true;
|
|
||||||
Some(r)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
let function = if user_in_db.function as u8 != form.function {
|
|
||||||
if let Ok(f) = Function::try_from(form.function) {
|
|
||||||
changed = true;
|
|
||||||
Some(f)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
let area = if user.role == Role::Admin
|
|
||||||
&& form.area.is_some()
|
|
||||||
&& user_in_db.area_id != form.area.unwrap()
|
|
||||||
{
|
|
||||||
changed = true;
|
|
||||||
Some(form.area.unwrap())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
if changed {
|
|
||||||
User::update(
|
|
||||||
pool.get_ref(),
|
|
||||||
path.id,
|
|
||||||
email,
|
|
||||||
name,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
role,
|
|
||||||
function,
|
|
||||||
area,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let changeset = UserChangeset {
|
||||||
|
name: form.name.clone(),
|
||||||
|
email: form.email.clone(),
|
||||||
|
role: form.role.try_into()?,
|
||||||
|
function: form.function.try_into()?,
|
||||||
|
area_id,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(e) = changeset.validate() {
|
||||||
|
return Ok(HttpResponse::BadRequest().body(e.to_string()));
|
||||||
|
};
|
||||||
|
|
||||||
|
User::update(pool.get_ref(), user_in_db.id, changeset).await?;
|
||||||
|
|
||||||
Ok(HttpResponse::Found()
|
Ok(HttpResponse::Found()
|
||||||
.insert_header((LOCATION, "/users"))
|
.insert_header((LOCATION, "/users"))
|
||||||
.insert_header(("HX-LOCATION", "/users"))
|
.insert_header(("HX-LOCATION", "/users"))
|
||||||
.finish())
|
.finish())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[db_test]
|
#[cfg(test)]
|
||||||
async fn works_when_user_is_admin(context: &DbTestContext) {
|
mod tests {
|
||||||
User::create(
|
use super::*;
|
||||||
&context.db_pool,
|
use crate::{models::*, utils::test_helper::*};
|
||||||
&Name(EN).fake::<String>(),
|
use brass_macros::db_test;
|
||||||
&SafeEmail(EN).fake::<String>(),
|
use fake::{
|
||||||
Role::Staff,
|
faker::{internet::en::SafeEmail, name::en::Name},
|
||||||
Function::Posten,
|
Fake, Faker,
|
||||||
1,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
crate::models::Area::create(&context.db_pool, "Süd")
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let app = context.app().await;
|
|
||||||
let config = RequestConfig {
|
|
||||||
uri: "/users/edit/1".to_string(),
|
|
||||||
role: Role::Admin,
|
|
||||||
function: crate::models::Function::Posten,
|
|
||||||
user_area: 1,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let new_name: String = Name(EN).fake();
|
#[db_test]
|
||||||
let new_mail: String = SafeEmail(EN).fake();
|
async fn works_when_user_is_admin(context: &DbTestContext) {
|
||||||
|
User::create(&context.db_pool, Faker.fake()).await.unwrap();
|
||||||
|
|
||||||
let form = EditUserForm {
|
Area::create(&context.db_pool, "Süd").await.unwrap();
|
||||||
name: new_name.clone(),
|
|
||||||
email: new_mail.clone(),
|
|
||||||
role: Role::AreaManager as u8,
|
|
||||||
function: Function::Fuehrungsassistent as u8,
|
|
||||||
area: Some(2),
|
|
||||||
};
|
|
||||||
|
|
||||||
let response = test_post(&context.db_pool, app, &config, form).await;
|
let app = context.app().await;
|
||||||
assert_eq!(StatusCode::FOUND, response.status());
|
let config = RequestConfig {
|
||||||
|
uri: "/users/edit/1".to_string(),
|
||||||
|
role: Role::Admin,
|
||||||
|
function: Function::Posten,
|
||||||
|
user_area: 1,
|
||||||
|
};
|
||||||
|
|
||||||
let updated_user = User::read_by_id(&context.db_pool, 1)
|
let new_name: String = Name().fake();
|
||||||
.await
|
let new_mail: String = SafeEmail().fake();
|
||||||
.unwrap()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(new_name, updated_user.name);
|
let form = EditUserForm {
|
||||||
assert_eq!(new_mail, updated_user.email);
|
name: new_name.clone(),
|
||||||
assert_eq!(Role::AreaManager, updated_user.role);
|
email: new_mail.clone(),
|
||||||
assert_eq!(Function::Fuehrungsassistent, updated_user.function);
|
role: Role::AreaManager as u8,
|
||||||
assert_eq!(2, updated_user.area_id);
|
function: Function::Fuehrungsassistent as u8,
|
||||||
}
|
area: Some(2),
|
||||||
|
};
|
||||||
#[db_test]
|
|
||||||
async fn cant_edit_oneself(context: &DbTestContext) {
|
let response = test_post(&context.db_pool, app, &config, form).await;
|
||||||
let app = context.app().await;
|
assert_eq!(StatusCode::FOUND, response.status());
|
||||||
let config = RequestConfig {
|
|
||||||
uri: "/users/edit/1".to_string(),
|
let updated_user = User::read_by_id(&context.db_pool, 1)
|
||||||
role: Role::Admin,
|
.await
|
||||||
function: crate::models::Function::Posten,
|
.unwrap()
|
||||||
user_area: 1,
|
.unwrap();
|
||||||
};
|
|
||||||
|
assert_eq!(new_name, updated_user.name);
|
||||||
let form = EditUserForm {
|
assert_eq!(new_mail, updated_user.email);
|
||||||
name: "".to_string(),
|
assert_eq!(Role::AreaManager, updated_user.role);
|
||||||
email: "".to_string(),
|
assert_eq!(Function::Fuehrungsassistent, updated_user.function);
|
||||||
role: Role::AreaManager as u8,
|
assert_eq!(2, updated_user.area_id);
|
||||||
function: Function::Fuehrungsassistent as u8,
|
}
|
||||||
area: Some(1),
|
|
||||||
};
|
#[db_test]
|
||||||
|
async fn cant_edit_oneself(context: &DbTestContext) {
|
||||||
let response = test_post(&context.db_pool, app, &config, form).await;
|
let app = context.app().await;
|
||||||
assert_eq!(StatusCode::BAD_REQUEST, response.status());
|
let config = RequestConfig {
|
||||||
|
uri: "/users/edit/1".to_string(),
|
||||||
|
role: Role::Admin,
|
||||||
|
function: Function::Posten,
|
||||||
|
user_area: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
let form = EditUserForm {
|
||||||
|
name: "".to_string(),
|
||||||
|
email: "".to_string(),
|
||||||
|
role: Role::AreaManager as u8,
|
||||||
|
function: Function::Fuehrungsassistent as u8,
|
||||||
|
area: Some(1),
|
||||||
|
};
|
||||||
|
|
||||||
|
let response = test_post(&context.db_pool, app, &config, form).await;
|
||||||
|
assert_eq!(StatusCode::BAD_REQUEST, response.status());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
use actix_web::{http::header::LOCATION, web, HttpResponse, Responder};
|
use actix_web::{http::header::LOCATION, web, HttpResponse, Responder};
|
||||||
|
use garde::Validate;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
mail::Mailer,
|
mail::Mailer,
|
||||||
models::{Function, Registration, Role, User},
|
models::{Function, Registration, Role, User, UserChangeset},
|
||||||
utils::ApplicationError,
|
utils::ApplicationError,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -37,13 +38,21 @@ pub async fn post_new(
|
|||||||
let role = Role::try_from(form.role)?;
|
let role = Role::try_from(form.role)?;
|
||||||
let function = Function::try_from(form.function)?;
|
let function = Function::try_from(form.function)?;
|
||||||
|
|
||||||
let id = User::create(
|
let changeset = UserChangeset {
|
||||||
pool.get_ref(),
|
name: form.name.clone(),
|
||||||
&form.name,
|
email: form.email.clone(),
|
||||||
&form.email,
|
|
||||||
role,
|
role,
|
||||||
function,
|
function,
|
||||||
area_id,
|
area_id
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Err(e) = changeset.validate() {
|
||||||
|
return Ok(HttpResponse::BadRequest().body(e.to_string()));
|
||||||
|
};
|
||||||
|
|
||||||
|
let id = User::create(
|
||||||
|
pool.get_ref(),
|
||||||
|
changeset
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
@ -25,28 +25,19 @@ pub async fn post(
|
|||||||
}
|
}
|
||||||
|
|
||||||
let user = if user.id != path.id {
|
let user = if user.id != path.id {
|
||||||
User::read_by_id(pool.get_ref(), path.id).await.unwrap().unwrap()
|
User::read_by_id(pool.get_ref(), path.id)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.unwrap()
|
||||||
} else {
|
} else {
|
||||||
user.into_inner()
|
user.into_inner()
|
||||||
};
|
};
|
||||||
|
|
||||||
match query.field.as_str() {
|
match query.field.as_str() {
|
||||||
"locked" => {
|
"locked" => {
|
||||||
User::update(
|
User::update_locked(pool.get_ref(), user.id, !user.locked)
|
||||||
pool.get_ref(),
|
.await
|
||||||
user.id,
|
.unwrap();
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
Some(!user.locked),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
if !user.locked {
|
if !user.locked {
|
||||||
return HttpResponse::Ok().body(format!(
|
return HttpResponse::Ok().body(format!(
|
||||||
@ -79,18 +70,10 @@ pub async fn post(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"receiveNotifications" => {
|
"receiveNotifications" => {
|
||||||
User::update(
|
User::update_receive_notifications(
|
||||||
pool.get_ref(),
|
pool.get_ref(),
|
||||||
user.id,
|
user.id,
|
||||||
None,
|
!user.receive_notifications,
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
Some(!user.receive_notifications),
|
|
||||||
None,
|
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -12,6 +12,7 @@ mod password_reset;
|
|||||||
mod registration;
|
mod registration;
|
||||||
mod role;
|
mod role;
|
||||||
mod user;
|
mod user;
|
||||||
|
mod user_changeset;
|
||||||
mod vehicle;
|
mod vehicle;
|
||||||
mod vehicle_assignement;
|
mod vehicle_assignement;
|
||||||
|
|
||||||
@ -33,6 +34,7 @@ pub use password_reset::{NoneToken, PasswordReset, Token};
|
|||||||
pub use registration::Registration;
|
pub use registration::Registration;
|
||||||
pub use role::Role;
|
pub use role::Role;
|
||||||
pub use user::User;
|
pub use user::User;
|
||||||
|
pub use user_changeset::UserChangeset;
|
||||||
pub use vehicle::Vehicle;
|
pub use vehicle::Vehicle;
|
||||||
pub use vehicle_assignement::VehicleAssignement;
|
pub use vehicle_assignement::VehicleAssignement;
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use sqlx::PgPool;
|
use sqlx::PgPool;
|
||||||
|
|
||||||
use super::{Area, Function, Result, Role};
|
use super::{Area, Function, Result, Role, UserChangeset};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct User {
|
pub struct User {
|
||||||
@ -20,28 +20,22 @@ pub struct User {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl User {
|
impl User {
|
||||||
pub async fn create(
|
pub async fn create(pool: &PgPool, changeset: UserChangeset) -> Result<i32> {
|
||||||
pool: &PgPool,
|
|
||||||
name: &str,
|
|
||||||
email: &str,
|
|
||||||
role: Role,
|
|
||||||
function: Function,
|
|
||||||
area_id: i32,
|
|
||||||
) -> Result<i32> {
|
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
r#"
|
r#"
|
||||||
INSERT INTO user_ (name, email, role, function, areaId)
|
INSERT INTO user_ (name, email, role, function, areaId)
|
||||||
VALUES ($1, $2, $3, $4, $5)
|
VALUES ($1, $2, $3, $4, $5)
|
||||||
RETURNING id;
|
RETURNING id;
|
||||||
"#,
|
"#,
|
||||||
name,
|
changeset.name,
|
||||||
email,
|
changeset.email,
|
||||||
role as Role,
|
changeset.role as Role,
|
||||||
function as Function,
|
changeset.function as Function,
|
||||||
area_id
|
changeset.area_id
|
||||||
)
|
)
|
||||||
.fetch_one(pool)
|
.fetch_one(pool)
|
||||||
.await.map(|r| r.id)
|
.await
|
||||||
|
.map(|r| r.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create_with_password(
|
pub async fn create_with_password(
|
||||||
@ -54,8 +48,6 @@ impl User {
|
|||||||
function: Function,
|
function: Function,
|
||||||
area_id: i32,
|
area_id: i32,
|
||||||
) -> Result<i32> {
|
) -> Result<i32> {
|
||||||
|
|
||||||
|
|
||||||
sqlx::query!(
|
sqlx::query!(
|
||||||
r#"
|
r#"
|
||||||
INSERT INTO user_ (name, email, password, salt, role, function, areaId)
|
INSERT INTO user_ (name, email, password, salt, role, function, areaId)
|
||||||
@ -71,7 +63,8 @@ impl User {
|
|||||||
area_id
|
area_id
|
||||||
)
|
)
|
||||||
.fetch_one(pool)
|
.fetch_one(pool)
|
||||||
.await.map(|r| r.id)
|
.await
|
||||||
|
.map(|r| r.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn read_by_id(pool: &PgPool, id: i32) -> Result<Option<User>> {
|
pub async fn read_by_id(pool: &PgPool, id: i32) -> Result<Option<User>> {
|
||||||
@ -286,72 +279,55 @@ impl User {
|
|||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn update(
|
pub async fn update(pool: &PgPool, id: i32, changeset: UserChangeset) -> Result<()> {
|
||||||
|
sqlx::query!(
|
||||||
|
"UPDATE user_ SET name = $1, email = $2, role = $3, function = $4, areaId = $5 WHERE id = $6;",
|
||||||
|
changeset.name,
|
||||||
|
changeset.email,
|
||||||
|
changeset.role as Role,
|
||||||
|
changeset.function as Function,
|
||||||
|
changeset.area_id,
|
||||||
|
id
|
||||||
|
)
|
||||||
|
.execute(pool)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn update_password(pool: &PgPool, id: i32, password: &str, salt: &str) -> Result<()> {
|
||||||
|
sqlx::query!(
|
||||||
|
"UPDATE user_ SET password = $1, salt = $2 WHERE id = $3;",
|
||||||
|
password,
|
||||||
|
salt,
|
||||||
|
id
|
||||||
|
)
|
||||||
|
.execute(pool)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn update_locked(pool: &PgPool, id: i32, locked: bool) -> Result<()> {
|
||||||
|
sqlx::query!("UPDATE user_ SET locked = $1 WHERE id = $2;", locked, id)
|
||||||
|
.execute(pool)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn update_receive_notifications(
|
||||||
pool: &PgPool,
|
pool: &PgPool,
|
||||||
id: i32,
|
id: i32,
|
||||||
email: Option<&str>,
|
receive_notifications: bool,
|
||||||
name: Option<&str>,
|
|
||||||
password: Option<&str>,
|
|
||||||
salt: Option<&str>,
|
|
||||||
role: Option<Role>,
|
|
||||||
function: Option<Function>,
|
|
||||||
area_id: Option<i32>,
|
|
||||||
receive_notifications: Option<bool>,
|
|
||||||
locked: Option<bool>,
|
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let mut query_builder = sqlx::QueryBuilder::new("UPDATE user_ SET ");
|
sqlx::query!(
|
||||||
let mut separated = query_builder.separated(", ");
|
"UPDATE user_ SET receiveNotifications = $1 WHERE id = $2;",
|
||||||
|
receive_notifications,
|
||||||
if let Some(email) = email {
|
id
|
||||||
separated.push("email = ");
|
)
|
||||||
separated.push_bind_unseparated(email);
|
.execute(pool)
|
||||||
}
|
.await?;
|
||||||
|
|
||||||
if let Some(name) = name {
|
|
||||||
separated.push("name = ");
|
|
||||||
separated.push_bind_unseparated(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(password) = password {
|
|
||||||
separated.push("password = ");
|
|
||||||
separated.push_bind_unseparated(password);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(salt) = salt {
|
|
||||||
separated.push("salt = ");
|
|
||||||
separated.push_bind_unseparated(salt);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(role) = role {
|
|
||||||
separated.push("role = ");
|
|
||||||
separated.push_bind_unseparated(role as Role);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(function) = function {
|
|
||||||
separated.push("function = ");
|
|
||||||
separated.push_bind_unseparated(function as Function);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(area_id) = area_id {
|
|
||||||
separated.push("areaId = ");
|
|
||||||
separated.push_bind_unseparated(area_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(receive_notifications) = receive_notifications {
|
|
||||||
separated.push("receiveNotifications = ");
|
|
||||||
separated.push_bind_unseparated(receive_notifications);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(locked) = locked {
|
|
||||||
separated.push("locked = ");
|
|
||||||
separated.push_bind_unseparated(locked);
|
|
||||||
}
|
|
||||||
|
|
||||||
query_builder.push(" WHERE id = ");
|
|
||||||
query_builder.push_bind(id);
|
|
||||||
query_builder.push(";");
|
|
||||||
|
|
||||||
query_builder.build().execute(pool).await?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
24
web/src/models/user_changeset.rs
Normal file
24
web/src/models/user_changeset.rs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#[cfg(test)]
|
||||||
|
use fake::{faker::internet::en::SafeEmail, faker::name::en::Name, Dummy};
|
||||||
|
|
||||||
|
use garde::Validate;
|
||||||
|
|
||||||
|
use super::{Function, Role};
|
||||||
|
|
||||||
|
#[derive(Validate)]
|
||||||
|
#[cfg_attr(test, derive(Dummy))]
|
||||||
|
#[garde(allow_unvalidated)]
|
||||||
|
pub struct UserChangeset {
|
||||||
|
#[cfg_attr(test, dummy(faker = "Name()"))]
|
||||||
|
pub name: String,
|
||||||
|
#[garde(email)]
|
||||||
|
#[cfg_attr(test, dummy(faker = "SafeEmail()"))]
|
||||||
|
pub email: String,
|
||||||
|
#[cfg_attr(test, dummy(expr = "Role::Staff"))]
|
||||||
|
pub role: Role,
|
||||||
|
#[cfg_attr(test, dummy(expr = "Function::Posten"))]
|
||||||
|
pub function: Function,
|
||||||
|
/// check before: must exist and user can create other user for this area
|
||||||
|
#[cfg_attr(test, dummy(expr = "1"))]
|
||||||
|
pub area_id: i32,
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user