mirror of
https://github.com/trailbaseio/trailbase.git
synced 2026-01-06 01:40:12 -06:00
Cleanup: remove legacy query_one_row utility.
This commit is contained in:
@@ -58,4 +58,4 @@ trailbase-sqlean = { path = "vendor/sqlean", version = "0.0.2" }
|
||||
trailbase-extension = { path = "trailbase-extension", version = "0.2.0" }
|
||||
trailbase-sqlite = { path = "trailbase-sqlite", version = "0.2.0" }
|
||||
trailbase = { path = "trailbase-core", version = "0.1.0" }
|
||||
uuid = { version = "=1.12.1", default-features = false, features = ["std", "v4", "v7"] }
|
||||
uuid = { version = "=1.12.1", default-features = false, features = ["std", "v4", "v7", "serde"] }
|
||||
|
||||
@@ -131,19 +131,16 @@ pub async fn list_logs_handler(
|
||||
let table_metadata = TableMetadata::new(table.clone(), &[table]);
|
||||
let filter_where_clause = build_filter_where_clause(&table_metadata, filter_params)?;
|
||||
|
||||
let total_row_count = {
|
||||
let row = crate::util::query_one_row(
|
||||
conn,
|
||||
let total_row_count: i64 = conn
|
||||
.query_value(
|
||||
&format!(
|
||||
"SELECT COUNT(*) FROM {LOGS_TABLE_NAME} WHERE {clause}",
|
||||
clause = filter_where_clause.clause
|
||||
"SELECT COUNT(*) FROM {LOGS_TABLE_NAME} WHERE {where_clause}",
|
||||
where_clause = filter_where_clause.clause
|
||||
),
|
||||
filter_where_clause.params.clone(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
row.get::<i64>(0)?
|
||||
};
|
||||
.await?
|
||||
.unwrap_or(-1);
|
||||
|
||||
lazy_static! {
|
||||
static ref DEFAULT_ORDERING: Vec<(String, Order)> =
|
||||
|
||||
@@ -65,14 +65,11 @@ pub async fn list_rows_handler(
|
||||
let total_row_count = {
|
||||
let where_clause = &filter_where_clause.clause;
|
||||
let count_query = format!("SELECT COUNT(*) FROM '{table_name}' WHERE {where_clause}");
|
||||
let row = crate::util::query_one_row(
|
||||
state.conn(),
|
||||
&count_query,
|
||||
filter_where_clause.params.clone(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
row.get::<i64>(0)?
|
||||
state
|
||||
.conn()
|
||||
.query_value::<i64>(&count_query, filter_where_clause.params.clone())
|
||||
.await?
|
||||
.unwrap_or(-1)
|
||||
};
|
||||
|
||||
let cursor_column = table_or_view_metadata.record_pk_column();
|
||||
|
||||
@@ -79,17 +79,16 @@ pub async fn list_users_handler(
|
||||
// string.
|
||||
let filter_where_clause = build_filter_where_clause(&*table_metadata, filter_params)?;
|
||||
|
||||
let total_row_count = {
|
||||
let where_clause = &filter_where_clause.clause;
|
||||
let row = crate::util::query_one_row(
|
||||
conn,
|
||||
&format!("SELECT COUNT(*) FROM {USER_TABLE} WHERE {where_clause}"),
|
||||
let total_row_count: i64 = conn
|
||||
.query_value(
|
||||
&format!(
|
||||
"SELECT COUNT(*) FROM {USER_TABLE} WHERE {where_clause}",
|
||||
where_clause = filter_where_clause.clause
|
||||
),
|
||||
filter_where_clause.params.clone(),
|
||||
)
|
||||
.await?;
|
||||
|
||||
row.get::<i64>(0)?
|
||||
};
|
||||
.await?
|
||||
.unwrap_or(-1);
|
||||
|
||||
lazy_static! {
|
||||
static ref DEFAULT_ORDERING: Vec<(String, Order)> =
|
||||
|
||||
@@ -20,16 +20,24 @@ async fn get_avatar_url(state: &AppState, user: &DbUser) -> Option<String> {
|
||||
format!(r#"SELECT EXISTS(SELECT user FROM "{AVATAR_TABLE}" WHERE user = $1)"#);
|
||||
};
|
||||
|
||||
if let Ok(row) = crate::util::query_one_row(state.user_conn(), &QUERY, params!(user.id)).await {
|
||||
let has_avatar: bool = row.get(0).unwrap_or(false);
|
||||
if has_avatar {
|
||||
let site = state.site_url();
|
||||
let record_user_id = id_to_b64(&user.id);
|
||||
let col_name = "file";
|
||||
return Some(format!(
|
||||
"{site}/{RECORD_API_PATH}/{AVATAR_TABLE}/{record_user_id}/file/{col_name}"
|
||||
));
|
||||
}
|
||||
let has_avatar = state
|
||||
.user_conn()
|
||||
.query_value(&QUERY, params!(user.id))
|
||||
.await
|
||||
.map_err(|err| {
|
||||
log::debug!("avatar query broken?");
|
||||
return err;
|
||||
})
|
||||
.unwrap_or_default()
|
||||
.unwrap_or(false);
|
||||
|
||||
if has_avatar {
|
||||
let site = state.site_url();
|
||||
let record_user_id = id_to_b64(&user.id);
|
||||
let col_name = "file";
|
||||
return Some(format!(
|
||||
"{site}/{RECORD_API_PATH}/{AVATAR_TABLE}/{record_user_id}/file/{col_name}"
|
||||
));
|
||||
}
|
||||
|
||||
return None;
|
||||
|
||||
@@ -180,14 +180,10 @@ pub async fn force_password_reset(
|
||||
format!("UPDATE '{USER_TABLE}' SET password_hash = $1 WHERE email = $2 RETURNING id");
|
||||
}
|
||||
|
||||
let id: [u8; 16] = crate::util::query_one_row(
|
||||
user_conn,
|
||||
&UPDATE_PASSWORD_QUERY,
|
||||
params!(hashed_password, email),
|
||||
)
|
||||
.await?
|
||||
.get(0)
|
||||
.map_err(|_err| AuthError::NotFound)?;
|
||||
|
||||
return Ok(Uuid::from_bytes(id));
|
||||
return Ok(
|
||||
user_conn
|
||||
.query_value(&UPDATE_PASSWORD_QUERY, params!(hashed_password, email))
|
||||
.await?
|
||||
.ok_or(AuthError::NotFound)?,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -24,7 +24,6 @@ use crate::auth::user::{DbUser, User};
|
||||
use crate::constants::*;
|
||||
use crate::email::{testing::TestAsyncSmtpTransport, Mailer};
|
||||
use crate::extract::Either;
|
||||
use crate::util::query_one_row;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_auth_registration_reset_and_change_email() {
|
||||
@@ -144,15 +143,11 @@ async fn test_auth_registration_reset_and_change_email() {
|
||||
.decode::<TokenClaims>(&tokens.auth_token)
|
||||
.unwrap();
|
||||
|
||||
let session_exists: bool = query_one_row(
|
||||
conn,
|
||||
&session_exists_query,
|
||||
(user.uuid.into_bytes().to_vec(),),
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.get(0)
|
||||
.unwrap();
|
||||
let session_exists: bool = conn
|
||||
.query_value(&session_exists_query, (user.uuid.into_bytes().to_vec(),))
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert!(session_exists);
|
||||
|
||||
user
|
||||
@@ -213,15 +208,14 @@ async fn test_auth_registration_reset_and_change_email() {
|
||||
assert_eq!(mailer.get_logs().len(), 2);
|
||||
|
||||
// Steal the reset code.
|
||||
let reset_code: String = query_one_row(
|
||||
conn,
|
||||
&format!(r#"SELECT password_reset_code FROM "{USER_TABLE}" WHERE id = $1"#),
|
||||
(user.uuid.into_bytes().to_vec(),),
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.get(0)
|
||||
.unwrap();
|
||||
let reset_code: String = conn
|
||||
.query_value(
|
||||
&format!("SELECT password_reset_code FROM {USER_TABLE} WHERE id = $1"),
|
||||
params!(user.uuid.into_bytes().to_vec()),
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
let reset_email_body: String = String::from_utf8_lossy(
|
||||
"ed_printable::decode(
|
||||
@@ -272,15 +266,14 @@ async fn test_auth_registration_reset_and_change_email() {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let session_exists: bool = query_one_row(
|
||||
conn,
|
||||
&session_exists_query,
|
||||
(user.uuid.into_bytes().to_vec(),),
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.get(0)
|
||||
.unwrap();
|
||||
let session_exists: bool = conn
|
||||
.query_value(
|
||||
&session_exists_query,
|
||||
params!(user.uuid.into_bytes().to_vec()),
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert!(!session_exists);
|
||||
|
||||
let tokens = login_with_password(&state, &email, &new_password)
|
||||
@@ -326,15 +319,14 @@ async fn test_auth_registration_reset_and_change_email() {
|
||||
assert_eq!(mailer.get_logs().len(), 3);
|
||||
|
||||
// Steal the verification code.
|
||||
let email_verification_code: String = query_one_row(
|
||||
conn,
|
||||
&format!(r#"SELECT email_verification_code FROM "{USER_TABLE}" WHERE id = $1"#),
|
||||
params!(user.uuid.into_bytes()),
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.get(0)
|
||||
.unwrap();
|
||||
let email_verification_code: String = conn
|
||||
.query_value(
|
||||
&format!(r#"SELECT email_verification_code FROM "{USER_TABLE}" WHERE id = $1"#),
|
||||
params!(user.uuid.into_bytes()),
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert!(!email_verification_code.is_empty());
|
||||
|
||||
let verification_email_body: String = String::from_utf8_lossy(
|
||||
@@ -359,15 +351,14 @@ async fn test_auth_registration_reset_and_change_email() {
|
||||
.await
|
||||
.expect(&format!("CODE: '{email_verification_code}'"));
|
||||
|
||||
let db_email: String = query_one_row(
|
||||
conn,
|
||||
&format!(r#"SELECT email FROM "{USER_TABLE}" WHERE id = $1"#),
|
||||
params!(user.uuid.into_bytes()),
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.get(0)
|
||||
.unwrap();
|
||||
let db_email: String = conn
|
||||
.query_value(
|
||||
&format!(r#"SELECT email FROM "{USER_TABLE}" WHERE id = $1"#),
|
||||
params!(user.uuid.into_bytes()),
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(new_email, db_email);
|
||||
|
||||
@@ -415,15 +406,14 @@ async fn test_auth_registration_reset_and_change_email() {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let user_exists: bool = query_one_row(
|
||||
conn,
|
||||
&format!(r#"SELECT EXISTS(SELECT * FROM "{USER_TABLE}" WHERE id = $1)"#),
|
||||
params!(user.uuid.into_bytes()),
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.get(0)
|
||||
.unwrap();
|
||||
let user_exists: bool = conn
|
||||
.query_value(
|
||||
&format!(r#"SELECT EXISTS(SELECT * FROM "{USER_TABLE}" WHERE id = $1)"#),
|
||||
params!(user.uuid.into_bytes()),
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
assert!(!user_exists);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ use oauth2::{AuthorizationCode, StandardTokenResponse, TokenResponse};
|
||||
use serde::Deserialize;
|
||||
use tower_cookies::Cookies;
|
||||
use trailbase_sqlite::{named_params, params};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::auth::oauth::state::{OAuthState, ResponseType};
|
||||
use crate::auth::oauth::OAuthUser;
|
||||
@@ -206,7 +207,7 @@ pub(crate) async fn callback_from_external_auth_provider(
|
||||
async fn create_user_for_external_provider(
|
||||
conn: &trailbase_sqlite::Connection,
|
||||
user: &OAuthUser,
|
||||
) -> Result<uuid::Uuid, AuthError> {
|
||||
) -> Result<Uuid, AuthError> {
|
||||
if !user.verified {
|
||||
return Err(AuthError::Unauthorized);
|
||||
}
|
||||
@@ -223,24 +224,21 @@ async fn create_user_for_external_provider(
|
||||
);
|
||||
}
|
||||
|
||||
let row = crate::util::query_one_row(
|
||||
conn,
|
||||
&QUERY,
|
||||
named_params! {
|
||||
":provider_id": user.provider_id as i64,
|
||||
":provider_user_id": user.provider_user_id.clone(),
|
||||
":verified": user.verified as i64,
|
||||
":email": user.email.clone(),
|
||||
":avatar": user.avatar.clone(),
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
let id: Uuid = conn
|
||||
.query_value(
|
||||
&QUERY,
|
||||
named_params! {
|
||||
":provider_id": user.provider_id as i64,
|
||||
":provider_user_id": user.provider_user_id.clone(),
|
||||
":verified": user.verified as i64,
|
||||
":email": user.email.clone(),
|
||||
":avatar": user.avatar.clone(),
|
||||
},
|
||||
)
|
||||
.await?
|
||||
.ok_or_else(|| AuthError::Internal("query should return".into()))?;
|
||||
|
||||
return Ok(uuid::Uuid::from_bytes(
|
||||
row
|
||||
.get::<[u8; 16]>(0)
|
||||
.map_err(|err| AuthError::Internal(err.into()))?,
|
||||
));
|
||||
return Ok(id);
|
||||
}
|
||||
|
||||
async fn user_by_provider_id(
|
||||
@@ -258,7 +256,6 @@ async fn user_by_provider_id(
|
||||
&QUERY,
|
||||
params!(provider_id as i64, provider_user_id.to_string()),
|
||||
)
|
||||
.await
|
||||
.map_err(|err| AuthError::Internal(err.into()))?
|
||||
.await?
|
||||
.ok_or_else(|| AuthError::NotFound);
|
||||
}
|
||||
|
||||
@@ -150,11 +150,13 @@ pub async fn user_exists(state: &AppState, email: &str) -> Result<bool, AuthErro
|
||||
static ref QUERY: String =
|
||||
format!(r#"SELECT EXISTS(SELECT 1 FROM "{USER_TABLE}" WHERE email = $1)"#);
|
||||
};
|
||||
let row =
|
||||
crate::util::query_one_row(state.user_conn(), &QUERY, params!(email.to_string())).await?;
|
||||
return row
|
||||
.get::<bool>(0)
|
||||
.map_err(|err| AuthError::Internal(err.into()));
|
||||
return Ok(
|
||||
state
|
||||
.user_conn()
|
||||
.query_value(&QUERY, params!(email.to_string()))
|
||||
.await?
|
||||
.ok_or_else(|| AuthError::Internal("query should return".into()))?,
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) async fn is_admin(state: &AppState, user: &User) -> bool {
|
||||
|
||||
@@ -69,12 +69,12 @@ mod test {
|
||||
use crate::util::{b64_to_id, id_to_b64};
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_record_api_delete() -> Result<(), anyhow::Error> {
|
||||
let state = test_state(None).await?;
|
||||
async fn test_record_api_delete() {
|
||||
let state = test_state(None).await.unwrap();
|
||||
let conn = state.conn();
|
||||
|
||||
create_chat_message_app_tables(&state).await?;
|
||||
let room = add_room(conn, "room0").await?;
|
||||
create_chat_message_app_tables(&state).await.unwrap();
|
||||
let room = add_room(conn, "room0").await.unwrap();
|
||||
let password = "Secret!1!!";
|
||||
|
||||
// Register message table as api with moderator read access.
|
||||
@@ -100,54 +100,61 @@ mod test {
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let user_x_email = "user_x@test.com";
|
||||
let user_x = create_user_for_test(&state, user_x_email, password)
|
||||
.await?
|
||||
.await
|
||||
.unwrap()
|
||||
.into_bytes();
|
||||
|
||||
let user_x_token = login_with_password(&state, user_x_email, password).await?;
|
||||
let user_x_token = login_with_password(&state, user_x_email, password)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
add_user_to_room(conn, user_x, room).await?;
|
||||
add_user_to_room(conn, user_x, room).await.unwrap();
|
||||
|
||||
let user_y_email = "user_y@foo.baz";
|
||||
let _user_y = create_user_for_test(&state, user_y_email, password)
|
||||
.await?
|
||||
.await
|
||||
.unwrap()
|
||||
.into_bytes();
|
||||
|
||||
let user_y_token = login_with_password(&state, user_y_email, password).await?;
|
||||
let user_y_token = login_with_password(&state, user_y_email, password)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
{
|
||||
// User X can delete their own message.
|
||||
let id = add_message(&state, &user_x, &user_x_token.auth_token, &room).await?;
|
||||
delete_message(&state, &user_x_token.auth_token, &id).await?;
|
||||
assert_eq!(message_exists(conn, &id).await?, false);
|
||||
let id = add_message(&state, &user_x, &user_x_token.auth_token, &room)
|
||||
.await
|
||||
.unwrap();
|
||||
delete_message(&state, &user_x_token.auth_token, &id)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(message_exists(conn, &id).await, false);
|
||||
}
|
||||
|
||||
{
|
||||
// User Y cannot delete X's message.
|
||||
let id = add_message(&state, &user_x, &user_x_token.auth_token, &room).await?;
|
||||
let id = add_message(&state, &user_x, &user_x_token.auth_token, &room)
|
||||
.await
|
||||
.unwrap();
|
||||
let response = delete_message(&state, &user_y_token.auth_token, &id).await;
|
||||
assert!(response.is_err());
|
||||
assert_eq!(message_exists(conn, &id).await?, true);
|
||||
assert_eq!(message_exists(conn, &id).await, true);
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
async fn message_exists(
|
||||
conn: &trailbase_sqlite::Connection,
|
||||
id: &[u8; 16],
|
||||
) -> Result<bool, anyhow::Error> {
|
||||
let count: i64 = crate::util::query_one_row(
|
||||
conn,
|
||||
"SELECT COUNT(*) FROM message WHERE id = $1",
|
||||
params!(*id),
|
||||
)
|
||||
.await?
|
||||
.get(0)?;
|
||||
return Ok(count > 0);
|
||||
async fn message_exists(conn: &trailbase_sqlite::Connection, id: &[u8; 16]) -> bool {
|
||||
let count: i64 = conn
|
||||
.query_value("SELECT COUNT(*) FROM message WHERE id = $1", params!(*id))
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
return count > 0;
|
||||
}
|
||||
|
||||
async fn add_message(
|
||||
|
||||
@@ -251,14 +251,14 @@ mod test {
|
||||
use crate::records::test_utils::*;
|
||||
use crate::records::*;
|
||||
use crate::test::unpack_json_response;
|
||||
use crate::util::{id_to_b64, query_one_row};
|
||||
use crate::util::id_to_b64;
|
||||
|
||||
#[tokio::test]
|
||||
async fn ignores_extra_sql_parameters_test() -> Result<(), anyhow::Error> {
|
||||
async fn ignores_extra_sql_parameters_test() {
|
||||
// This test is actually just testing our SQL driver and making sure that we can overprovision
|
||||
// arguments. Specifically, we want to provide :user and :id arguments even if they're not
|
||||
// consumed by a user-provided access query.
|
||||
let state = test_state(None).await?;
|
||||
let state = test_state(None).await.unwrap();
|
||||
let conn = state.user_conn();
|
||||
|
||||
const EMAIL: &str = "foo@bar.baz";
|
||||
@@ -267,20 +267,21 @@ mod test {
|
||||
&format!(r#"INSERT INTO "{USER_TABLE}" (email) VALUES ($1)"#),
|
||||
trailbase_sqlite::params!(EMAIL),
|
||||
)
|
||||
.await?;
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
query_one_row(
|
||||
conn,
|
||||
&format!(r#"SELECT * from "{USER_TABLE}" WHERE email = :email"#),
|
||||
trailbase_sqlite::named_params! {
|
||||
":email": EMAIL,
|
||||
":unused": "unused",
|
||||
":foo": 42,
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
return Ok(());
|
||||
conn
|
||||
.query_row(
|
||||
&format!(r#"SELECT * from "{USER_TABLE}" WHERE email = :email"#),
|
||||
trailbase_sqlite::named_params! {
|
||||
":email": EMAIL,
|
||||
":unused": "unused",
|
||||
":foo": 42,
|
||||
},
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -3,7 +3,6 @@ mod tests {
|
||||
use trailbase_sqlite::params;
|
||||
|
||||
use crate::records::json_to_sql::JsonRow;
|
||||
use crate::util::query_one_row;
|
||||
use crate::AppState;
|
||||
|
||||
pub async fn create_chat_message_app_tables(state: &AppState) -> Result<(), anyhow::Error> {
|
||||
@@ -90,15 +89,15 @@ mod tests {
|
||||
conn: &trailbase_sqlite::Connection,
|
||||
name: &str,
|
||||
) -> Result<[u8; 16], anyhow::Error> {
|
||||
let room: [u8; 16] = query_one_row(
|
||||
conn,
|
||||
"INSERT INTO room (name) VALUES ($1) RETURNING id",
|
||||
params!(name.to_string()),
|
||||
)
|
||||
.await?
|
||||
.get(0)?;
|
||||
let room: uuid::Uuid = conn
|
||||
.query_value(
|
||||
"INSERT INTO room (name) VALUES ($1) RETURNING id",
|
||||
params!(name.to_string()),
|
||||
)
|
||||
.await?
|
||||
.ok_or(rusqlite::Error::QueryReturnedNoRows)?;
|
||||
|
||||
return Ok(room);
|
||||
return Ok(room.into_bytes());
|
||||
}
|
||||
|
||||
pub async fn add_user_to_room(
|
||||
@@ -121,15 +120,15 @@ mod tests {
|
||||
room: [u8; 16],
|
||||
message: &str,
|
||||
) -> Result<[u8; 16], anyhow::Error> {
|
||||
return Ok(
|
||||
query_one_row(
|
||||
conn,
|
||||
let id: uuid::Uuid = conn
|
||||
.query_value(
|
||||
"INSERT INTO message (_owner, room, data) VALUES ($1, $2, $3) RETURNING id",
|
||||
params!(user, room, message.to_string()),
|
||||
)
|
||||
.await?
|
||||
.get(0)?,
|
||||
);
|
||||
.ok_or(rusqlite::Error::QueryReturnedNoRows)?;
|
||||
|
||||
return Ok(id.into_bytes());
|
||||
}
|
||||
|
||||
pub fn json_row_from_value(value: serde_json::Value) -> Result<JsonRow, anyhow::Error> {
|
||||
|
||||
@@ -80,15 +80,15 @@ mod test {
|
||||
use crate::records::test_utils::*;
|
||||
use crate::records::*;
|
||||
use crate::test::unpack_json_response;
|
||||
use crate::util::{b64_to_id, id_to_b64, query_one_row};
|
||||
use crate::util::{b64_to_id, id_to_b64};
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_record_api_update() -> Result<(), anyhow::Error> {
|
||||
let state = test_state(None).await?;
|
||||
async fn test_record_api_update() {
|
||||
let state = test_state(None).await.unwrap();
|
||||
let conn = state.conn();
|
||||
|
||||
create_chat_message_app_tables(&state).await?;
|
||||
let room = add_room(conn, "room0").await?;
|
||||
create_chat_message_app_tables(&state).await.unwrap();
|
||||
let room = add_room(conn, "room0").await.unwrap();
|
||||
let password = "Secret!1!!";
|
||||
|
||||
// Register message table and api with moderator read access.
|
||||
@@ -116,23 +116,30 @@ mod test {
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let user_x_email = "user_x@test.com";
|
||||
let user_x = create_user_for_test(&state, user_x_email, password)
|
||||
.await?
|
||||
.await
|
||||
.unwrap()
|
||||
.into_bytes();
|
||||
|
||||
let user_x_token = login_with_password(&state, user_x_email, password).await?;
|
||||
let user_x_token = login_with_password(&state, user_x_email, password)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
add_user_to_room(conn, user_x, room).await?;
|
||||
add_user_to_room(conn, user_x, room).await.unwrap();
|
||||
|
||||
let user_y_email = "user_y@foo.baz";
|
||||
let _user_y = create_user_for_test(&state, user_y_email, password)
|
||||
.await?
|
||||
.await
|
||||
.unwrap()
|
||||
.into_bytes();
|
||||
|
||||
let user_y_token = login_with_password(&state, user_y_email, password).await?;
|
||||
let user_y_token = login_with_password(&state, user_y_email, password)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let create_json = serde_json::json!({
|
||||
"_owner": id_to_b64(&user_x),
|
||||
@@ -147,9 +154,11 @@ mod test {
|
||||
User::from_auth_token(&state, &user_x_token.auth_token),
|
||||
Either::Json(json_row_from_value(create_json).unwrap().into()),
|
||||
)
|
||||
.await?,
|
||||
.await
|
||||
.unwrap(),
|
||||
)
|
||||
.await?;
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(create_response.ids.len(), 1);
|
||||
let b64_id = create_response.ids[0].clone();
|
||||
@@ -170,13 +179,14 @@ mod test {
|
||||
|
||||
assert!(update_response.is_ok(), "{b64_id} {update_response:?}");
|
||||
|
||||
let message_text: String = query_one_row(
|
||||
conn,
|
||||
"SELECT data FROM message WHERE id = $1",
|
||||
params!(b64_to_id(&b64_id)?),
|
||||
)
|
||||
.await?
|
||||
.get(0)?;
|
||||
let message_text: String = conn
|
||||
.query_value(
|
||||
"SELECT data FROM message WHERE id = $1",
|
||||
params!(b64_to_id(&b64_id).unwrap()),
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
assert_eq!(updated_message_text, message_text);
|
||||
}
|
||||
|
||||
@@ -195,7 +205,5 @@ mod test {
|
||||
|
||||
assert!(update_response.is_err(), "{b64_id} {update_response:?}");
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,13 +137,14 @@ pub async fn init_app_state(
|
||||
});
|
||||
|
||||
if new_db {
|
||||
let num_admins: i64 = crate::util::query_one_row(
|
||||
app_state.user_conn(),
|
||||
&format!("SELECT COUNT(*) FROM {USER_TABLE} WHERE admin = TRUE"),
|
||||
(),
|
||||
)
|
||||
.await?
|
||||
.get(0)?;
|
||||
let num_admins: i64 = app_state
|
||||
.user_conn()
|
||||
.query_value(
|
||||
&format!("SELECT COUNT(*) FROM {USER_TABLE} WHERE admin = TRUE"),
|
||||
(),
|
||||
)
|
||||
.await?
|
||||
.unwrap_or(0);
|
||||
|
||||
if num_admins == 0 {
|
||||
let email = "admin@localhost".to_string();
|
||||
|
||||
@@ -560,13 +560,13 @@ pub async fn lookup_and_parse_table_schema(
|
||||
table_name: &str,
|
||||
) -> Result<Table, TableLookupError> {
|
||||
// Then get the actual table.
|
||||
let sql: String = crate::util::query_one_row(
|
||||
conn,
|
||||
&format!("SELECT sql FROM {SQLITE_SCHEMA_TABLE} WHERE type = 'table' AND name = $1"),
|
||||
params!(table_name.to_string()),
|
||||
)
|
||||
.await?
|
||||
.get(0)?;
|
||||
let sql: String = conn
|
||||
.query_value(
|
||||
&format!("SELECT sql FROM {SQLITE_SCHEMA_TABLE} WHERE type = 'table' AND name = $1"),
|
||||
params!(table_name.to_string()),
|
||||
)
|
||||
.await?
|
||||
.ok_or_else(|| trailbase_sqlite::Error::Rusqlite(rusqlite::Error::QueryReturnedNoRows))?;
|
||||
|
||||
let Some(stmt) = sqlite3_parse_into_statement(&sql)? else {
|
||||
return Err(TableLookupError::Missing);
|
||||
|
||||
@@ -58,19 +58,6 @@ pub(crate) fn assert_uuidv7_version(uuid: &Uuid) {
|
||||
#[cfg(not(debug_assertions))]
|
||||
pub(crate) fn assert_uuidv7_version(_uuid: &Uuid) {}
|
||||
|
||||
pub async fn query_one_row(
|
||||
conn: &trailbase_sqlite::Connection,
|
||||
sql: &str,
|
||||
params: impl trailbase_sqlite::Params + Send + 'static,
|
||||
) -> Result<trailbase_sqlite::Row, trailbase_sqlite::Error> {
|
||||
if let Some(row) = conn.query_row(sql, params).await? {
|
||||
return Ok(row);
|
||||
}
|
||||
return Err(trailbase_sqlite::Error::Rusqlite(
|
||||
rusqlite::Error::QueryReturnedNoRows,
|
||||
));
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn get_header(headers: &HeaderMap, header_name: impl AsHeaderName) -> Option<&str> {
|
||||
if let Some(header) = headers.get(header_name) {
|
||||
|
||||
@@ -128,4 +128,62 @@ mod test {
|
||||
.query_row("SELECT vec_f32('[0, 1, 2, 3]')", (), |_row| Ok(()))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_uuids() {
|
||||
let conn = crate::Connection::from_conn(connect_sqlite(None, None).unwrap()).unwrap();
|
||||
|
||||
conn
|
||||
.execute(
|
||||
r#"CREATE TABLE test (
|
||||
id BLOB PRIMARY KEY NOT NULL CHECK(is_uuid_v7(id)) DEFAULT(uuid_v7()),
|
||||
text TEXT
|
||||
)"#,
|
||||
(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// V4 fails
|
||||
assert!(conn
|
||||
.execute(
|
||||
"INSERT INTO test (id) VALUES (?1) ",
|
||||
crate::params!(uuid::Uuid::new_v4().into_bytes())
|
||||
)
|
||||
.await
|
||||
.is_err());
|
||||
|
||||
// V7 succeeds
|
||||
let id = uuid::Uuid::now_v7();
|
||||
assert!(conn
|
||||
.execute(
|
||||
"INSERT INTO test (id) VALUES (?1) ",
|
||||
crate::params!(id.into_bytes())
|
||||
)
|
||||
.await
|
||||
.is_ok());
|
||||
|
||||
let read_id: uuid::Uuid = conn
|
||||
.query_value("SELECT id FROM test LIMIT 1", ())
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(id, read_id);
|
||||
|
||||
let blob: Vec<u8> = conn
|
||||
.query_value("SELECT id FROM test LIMIT 1", ())
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(id, Uuid::from_slice(&blob).unwrap());
|
||||
|
||||
let arr = conn
|
||||
.query_value::<[u8; 16]>("SELECT id FROM test LIMIT 1", ())
|
||||
.await;
|
||||
|
||||
// FIXME: serde_rusqlite doesn't seem to be able to serialize blobs into [u8; N].
|
||||
assert!(arr.is_err());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user