mirror of
https://git.dirksys.ovh/dirk/bankserver.git
synced 2025-12-20 02:59:20 +01:00
implement custom data
This commit is contained in:
parent
efa8332f33
commit
975fc1d567
6
migrations/000002_user_data.sql
Normal file
6
migrations/000002_user_data.sql
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
create table user_data(
|
||||||
|
owner uuid not null references users(id) on delete cascade,
|
||||||
|
"user" uuid not null references users(id) on delete cascade,
|
||||||
|
data jsonb not null,
|
||||||
|
primary key (owner, "user")
|
||||||
|
);
|
||||||
@ -125,6 +125,42 @@ paths:
|
|||||||
$ref: '#/components/responses/Unauthorized'
|
$ref: '#/components/responses/Unauthorized'
|
||||||
default:
|
default:
|
||||||
$ref: '#/components/responses/Default'
|
$ref: '#/components/responses/Default'
|
||||||
|
/api/users/@me/data:
|
||||||
|
get:
|
||||||
|
operationId: self-get-data
|
||||||
|
summary: User data
|
||||||
|
tags:
|
||||||
|
- Users
|
||||||
|
security:
|
||||||
|
- bearer: []
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Ok
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema: {}
|
||||||
|
401:
|
||||||
|
$ref: '#/components/responses/Unauthorized'
|
||||||
|
default:
|
||||||
|
$ref: '#/components/responses/Default'
|
||||||
|
post:
|
||||||
|
operationId: self-set-data
|
||||||
|
summary: Set User data
|
||||||
|
tags:
|
||||||
|
- Users
|
||||||
|
security:
|
||||||
|
- bearer: []
|
||||||
|
requestBody:
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema: {}
|
||||||
|
responses:
|
||||||
|
200:
|
||||||
|
description: Ok
|
||||||
|
401:
|
||||||
|
$ref: '#/components/responses/Unauthorized'
|
||||||
|
default:
|
||||||
|
$ref: '#/components/responses/Default'
|
||||||
/api/users/@me/balance:
|
/api/users/@me/balance:
|
||||||
get:
|
get:
|
||||||
operationId: self-get-balance
|
operationId: self-get-balance
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
use std::{borrow::Cow, sync::Arc};
|
use std::sync::Arc;
|
||||||
|
|
||||||
use axum::{
|
use axum::{
|
||||||
Router,
|
Router,
|
||||||
|
|||||||
@ -21,6 +21,7 @@ pub(super) fn router() -> Router<Arc<AppState>> {
|
|||||||
Router::new()
|
Router::new()
|
||||||
.route("/{target}", get(user_info))
|
.route("/{target}", get(user_info))
|
||||||
.route("/@me/balance", get(user_balance))
|
.route("/@me/balance", get(user_balance))
|
||||||
|
.route("/@me/data", get(get_user_data).post(set_user_data))
|
||||||
.route("/@me/accounts", get(user_accounts))
|
.route("/@me/accounts", get(user_accounts))
|
||||||
.route("/@me/transactions", get(me_transaction_history))
|
.route("/@me/transactions", get(me_transaction_history))
|
||||||
.route("/", get(list_users))
|
.route("/", get(list_users))
|
||||||
@ -100,6 +101,32 @@ pub async fn user_accounts(EState(state): State, auth: Auth) -> Result<Json<User
|
|||||||
Ok(Json(UserAccounts { result }))
|
Ok(Json(UserAccounts { result }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(skip(state))]
|
||||||
|
pub async fn get_user_data(
|
||||||
|
EState(state): State,
|
||||||
|
auth: Auth,
|
||||||
|
) -> Result<Json<Option<serde_json::Value>>, Error> {
|
||||||
|
let user = auth.user_id();
|
||||||
|
let conn = state.conn().await?;
|
||||||
|
let data = Users::get_data(&conn, user).await?;
|
||||||
|
Ok(Json(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[instrument(skip(state))]
|
||||||
|
pub async fn set_user_data(
|
||||||
|
EState(state): State,
|
||||||
|
auth: Auth,
|
||||||
|
Json(body): Json<Option<serde_json::Value>>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let user = auth.user_id();
|
||||||
|
let mut conn = state.conn().await?;
|
||||||
|
match body {
|
||||||
|
Some(data) => Users::set_data(&mut conn, user, data).await?,
|
||||||
|
None => Users::delete_data(&mut conn, user).await?,
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|||||||
@ -120,4 +120,38 @@ impl Users {
|
|||||||
let info = client.query_opt(&stmt, &[&id]).await?.map(user_from_row);
|
let info = client.query_opt(&stmt, &[&id]).await?.map(user_from_row);
|
||||||
Ok(info)
|
Ok(info)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_data(
|
||||||
|
client: &impl GenericClient,
|
||||||
|
user: Uuid,
|
||||||
|
) -> Result<Option<serde_json::Value>, tokio_postgres::Error> {
|
||||||
|
let stmt = client
|
||||||
|
.prepare_cached("select data from user_data where owner = $1 and \"user\" = $1")
|
||||||
|
.await?;
|
||||||
|
let res = client.query_opt(&stmt, &[&user]).await?;
|
||||||
|
Ok(res.map(|row| row.get(0)))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn set_data(
|
||||||
|
client: &mut impl GenericClient,
|
||||||
|
user: Uuid,
|
||||||
|
data: serde_json::Value,
|
||||||
|
) -> Result<(), tokio_postgres::Error> {
|
||||||
|
let stmt = client
|
||||||
|
.prepare_cached("insert into user_data(owner, \"user\", data) values ($1, $1, $2) on conflict (owner, \"user\") do update set data = $2")
|
||||||
|
.await?;
|
||||||
|
client.execute(&stmt, &[&user, &data]).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn delete_data(
|
||||||
|
client: &mut impl GenericClient,
|
||||||
|
user: Uuid,
|
||||||
|
) -> Result<(), tokio_postgres::Error> {
|
||||||
|
let stmt = client
|
||||||
|
.prepare_cached("delete from user_data where \"owner\" = $1 and user = $2")
|
||||||
|
.await?;
|
||||||
|
client.execute(&stmt, &[&user]).await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
23
tests/integration/user_data.hurl
Normal file
23
tests/integration/user_data.hurl
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
POST {{host}}/api/login
|
||||||
|
{
|
||||||
|
"name": "user1",
|
||||||
|
"password": "this-is-a-password"
|
||||||
|
}
|
||||||
|
HTTP 200
|
||||||
|
|
||||||
|
[Captures]
|
||||||
|
token: jsonpath "$.token"
|
||||||
|
|
||||||
|
POST {{host}}/api/users/@me/data
|
||||||
|
Authorization: Bearer {{token}}
|
||||||
|
{
|
||||||
|
"hello": "world"
|
||||||
|
}
|
||||||
|
HTTP 200
|
||||||
|
|
||||||
|
GET {{host}}/api/users/@me/data
|
||||||
|
Authorization: Bearer {{token}}
|
||||||
|
HTTP 200
|
||||||
|
|
||||||
|
[Asserts]
|
||||||
|
jsonpath "$.hello" == "world"
|
||||||
Loading…
x
Reference in New Issue
Block a user