This commit is contained in:
Philip (a-0) 2024-01-21 12:25:11 +01:00
parent d1b12e1562
commit 76f6a6b67b
10 changed files with 120 additions and 60 deletions

View file

@ -13,7 +13,14 @@ use jsonwebtoken::{decode, Header};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_with::chrono::{DateTime, Utc}; use serde_with::chrono::{DateTime, Utc};
use tracing::{debug, error, warn}; use tracing::{debug, error, warn};
use ubisync_lib::{api::app::{AppRegisterRequest, AppRegisterResponse, AppSetDefaultPotRequest, AppSetDefaultPotResponse, AppCreatePotRequest, AppCreatePotResponse, AppGetDefaultPotRequest, AppGetDefaultPotResponse}, types::PotId}; use ubisync_lib::{
api::app::{
AppCreatePotRequest, AppCreatePotResponse, AppGetDefaultPotRequest,
AppGetDefaultPotResponse, AppRegisterRequest, AppRegisterResponse, AppSetDefaultPotRequest,
AppSetDefaultPotResponse,
},
types::PotId,
};
use uuid::Uuid; use uuid::Uuid;
use crate::state::ApiState; use crate::state::ApiState;
@ -112,7 +119,13 @@ pub(super) async fn set_default_pot(
Json(body): Json<AppSetDefaultPotRequest>, Json(body): Json<AppSetDefaultPotRequest>,
) -> Response { ) -> Response {
match s.set_app_default_pot(&app_id.0, &body.pot_id) { match s.set_app_default_pot(&app_id.0, &body.pot_id) {
Ok(_) => (StatusCode::OK, Json { 0: AppSetDefaultPotResponse }).into_response(), Ok(_) => (
StatusCode::OK,
Json {
0: AppSetDefaultPotResponse,
},
)
.into_response(),
Err(e) => { Err(e) => {
debug!("Could not set default pot: {}", e); debug!("Could not set default pot: {}", e);
StatusCode::INTERNAL_SERVER_ERROR.into_response() StatusCode::INTERNAL_SERVER_ERROR.into_response()
@ -126,7 +139,13 @@ pub(super) async fn get_default_pot(
Json(_): Json<AppGetDefaultPotRequest>, Json(_): Json<AppGetDefaultPotRequest>,
) -> Response { ) -> Response {
match s.get_default_pot(&app_id.0) { match s.get_default_pot(&app_id.0) {
Ok(p) => (StatusCode::OK, Json {0: AppGetDefaultPotResponse { pot: p }}).into_response(), Ok(p) => (
StatusCode::OK,
Json {
0: AppGetDefaultPotResponse { pot: p },
},
)
.into_response(),
Err(e) => { Err(e) => {
warn!("No default pot found: {}", e); warn!("No default pot found: {}", e);
StatusCode::NOT_FOUND.into_response() StatusCode::NOT_FOUND.into_response()
@ -143,19 +162,25 @@ pub(super) async fn create_pot(
Ok(a) => a, Ok(a) => a,
Err(e) => { Err(e) => {
debug!("Failed to fetch app: {}", e); debug!("Failed to fetch app: {}", e);
return StatusCode::INTERNAL_SERVER_ERROR.into_response() return StatusCode::INTERNAL_SERVER_ERROR.into_response();
} }
}; };
let inferred_app_type = match body.app_type { let inferred_app_type = match body.app_type {
Some(t) => t, Some(t) => t,
None => app.app_type None => app.app_type,
}; };
match s.create_pot(&app_id.0, &inferred_app_type) { match s.create_pot(&app_id.0, &inferred_app_type) {
Ok(id) => { Ok(id) => {
// If this is the first pot for this app, set it as default // If this is the first pot for this app, set it as default
if app.default_pot.is_none() { if app.default_pot.is_none() {
match s.set_app_default_pot(&app_id.0, &id) { match s.set_app_default_pot(&app_id.0, &id) {
Ok(_) => (StatusCode::OK, Json { 0: AppCreatePotResponse {pot_id: id} }).into_response(), Ok(_) => (
StatusCode::OK,
Json {
0: AppCreatePotResponse { pot_id: id },
},
)
.into_response(),
Err(e) => { Err(e) => {
debug!("Failed to set default pot: {}", e); debug!("Failed to set default pot: {}", e);
StatusCode::INTERNAL_SERVER_ERROR.into_response() StatusCode::INTERNAL_SERVER_ERROR.into_response()
@ -164,9 +189,15 @@ pub(super) async fn create_pot(
} }
// Otherwise, just return // Otherwise, just return
else { else {
(StatusCode::OK, Json { 0: AppCreatePotResponse {pot_id: id} }).into_response() (
} StatusCode::OK,
Json {
0: AppCreatePotResponse { pot_id: id },
}, },
)
.into_response()
}
}
Err(e) => { Err(e) => {
debug!("Could not create pot: {}", e); debug!("Could not create pot: {}", e);
StatusCode::INTERNAL_SERVER_ERROR.into_response() StatusCode::INTERNAL_SERVER_ERROR.into_response()

View file

@ -19,7 +19,11 @@ use ubisync_lib::{
use super::app::AppId; use super::app::AppId;
pub(super) async fn get(Path(id): Path<ElementId>, app: Extension<AppId>, s: Extension<Arc<ApiState>>) -> Response { pub(super) async fn get(
Path(id): Path<ElementId>,
app: Extension<AppId>,
s: Extension<Arc<ApiState>>,
) -> Response {
let element = s.get_element(&id, &app); let element = s.get_element(&id, &app);
match element { match element {
Ok(el) => ( Ok(el) => (
@ -32,7 +36,7 @@ pub(super) async fn get(Path(id): Path<ElementId>, app: Extension<AppId>, s: Ext
Err(e) => { Err(e) => {
warn!("Could not get element: {}", e); warn!("Could not get element: {}", e);
StatusCode::NOT_FOUND.into_response() StatusCode::NOT_FOUND.into_response()
}, }
} }
} }
@ -47,8 +51,8 @@ pub(super) async fn create(
Ok(p) => p.id, Ok(p) => p.id,
Err(e) => { Err(e) => {
warn!("Element create request did not provide pot id, and no default pot for requesting app was found: {}", e); warn!("Element create request did not provide pot id, and no default pot for requesting app was found: {}", e);
return StatusCode::INTERNAL_SERVER_ERROR.into_response() return StatusCode::INTERNAL_SERVER_ERROR.into_response();
}, }
}, },
}; };

View file

@ -14,7 +14,10 @@ pub fn get_router(state: ApiState) -> Router {
Router::new() Router::new()
// authenticated routes // authenticated routes
.route("/app/pot", put(app::create_pot)) .route("/app/pot", put(app::create_pot))
.route("/app/pot/default", get(app::get_default_pot).post(app::set_default_pot)) .route(
"/app/pot/default",
get(app::get_default_pot).post(app::set_default_pot),
)
.route("/element", put(element::create)) .route("/element", put(element::create))
.route( .route(
"/element/:id", "/element/:id",

View file

@ -24,7 +24,7 @@ pub fn handle(state: &CommState, peer: &PeerId, message: Message) {
} }
MessageContent::RemoveElement { id } => { MessageContent::RemoveElement { id } => {
state.remove_element(id).expect("State failed"); state.remove_element(id).expect("State failed");
}, }
MessageContent::AddPot { id, app_type } => { MessageContent::AddPot { id, app_type } => {
state.add_pot(id, app_type).expect("State failed"); state.add_pot(id, app_type).expect("State failed");
//TODO: remove when setting default pot properly is possible //TODO: remove when setting default pot properly is possible

View file

@ -7,7 +7,7 @@ use serde_with::chrono::Utc;
use tracing::debug; use tracing::debug;
use ubisync_lib::{ use ubisync_lib::{
messages::MessageContent, messages::MessageContent,
types::{Element, ElementContent, ElementId, PotId, Pot}, types::{Element, ElementContent, ElementId, Pot, PotId},
}; };
use crate::{ use crate::{
@ -82,8 +82,7 @@ impl ApiState {
debug!("Wrote element content {{{}}}", &id.to_string()); debug!("Wrote element content {{{}}}", &id.to_string());
Ok(()) Ok(())
} } else {
else {
Err(Error::msg( Err(Error::msg(
"Element does not exist or app does not have access to it", "Element does not exist or app does not have access to it",
)) ))
@ -109,7 +108,10 @@ impl ApiState {
let pot_id = PotId::new(); let pot_id = PotId::new();
queries::apps::create_pot(self.db(), &pot_id, app_id, app_type)?; queries::apps::create_pot(self.db(), &pot_id, app_id, app_type)?;
self.state.send_to_peers(MessageContent::AddPot { id: pot_id.to_owned(), app_type: app_type.to_string() }); self.state.send_to_peers(MessageContent::AddPot {
id: pot_id.to_owned(),
app_type: app_type.to_string(),
});
Ok(pot_id) Ok(pot_id)
} }
@ -191,7 +193,11 @@ mod tests {
.create_element(&ElementContent::Text("Test-text".to_string()), &pot_id) .create_element(&ElementContent::Text("Test-text".to_string()), &pot_id)
.unwrap(); .unwrap();
state state
.write_element_content(&id, &app_id, &ElementContent::Text("Test-text 2".to_string())) .write_element_content(
&id,
&app_id,
&ElementContent::Text("Test-text 2".to_string()),
)
.unwrap(); .unwrap();
let el = state.get_element(&id, &app_id).unwrap(); let el = state.get_element(&id, &app_id).unwrap();
assert_eq!( assert_eq!(

View file

@ -92,8 +92,7 @@ impl CommState {
} }
} }
Ok(()) Ok(())
} } else {
else {
Err(Error::msg("Could not fetch list of all apps")) Err(Error::msg("Could not fetch list of all apps"))
} }
} }

View file

@ -79,8 +79,7 @@ pub fn get_all_ids(db: &DbInstance) -> anyhow::Result<Vec<AppId>> {
.filter_map(|row| { .filter_map(|row| {
if let [DataValue::Str(app_id)] = row.as_slice() { if let [DataValue::Str(app_id)] = row.as_slice() {
serde_json::from_str::<AppId>(app_id).ok() serde_json::from_str::<AppId>(app_id).ok()
} } else {
else {
None None
} }
}) })
@ -138,11 +137,17 @@ pub fn set_default_pot(db: &DbInstance, id: &AppId, pot: &PotId) -> anyhow::Resu
]; ];
match run_query!( match run_query!(
&db, ":update apps {id => default_pot}", &db,
":update apps {id => default_pot}",
params.clone(), params.clone(),
ScriptMutability::Mutable ScriptMutability::Mutable
) { ) {
Ok(_) => match run_query!(&db, ":put pot_memberships {pot_id = default_pot, app_id = id}", params, ScriptMutability::Mutable) { Ok(_) => match run_query!(
&db,
":put pot_memberships {pot_id = default_pot, app_id = id}",
params,
ScriptMutability::Mutable
) {
Ok(_) => Ok(()), Ok(_) => Ok(()),
Err(report) => bail!(report), Err(report) => bail!(report),
}, },
@ -206,22 +211,26 @@ pub fn create_pot(
match run_query!( match run_query!(
&db, &db,
":insert pots {id = pot_id => app_type}" ":insert pots {id = pot_id => app_type}",
,
params.clone(), params.clone(),
ScriptMutability::Mutable ScriptMutability::Mutable
) { ) {
Ok(_) => match run_query!(&db, ":insert pot_memberships {pot_id => app_id}", params, ScriptMutability::Mutable) { Ok(_) => match run_query!(
&db,
":insert pot_memberships {pot_id => app_id}",
params,
ScriptMutability::Mutable
) {
Ok(_) => Ok(()), Ok(_) => Ok(()),
Err(e) => { Err(e) => {
warn!("{:?}", e); warn!("{:?}", e);
bail!(e) bail!(e)
}, }
}, },
Err(e) => { Err(e) => {
warn!("{:?}", e); warn!("{:?}", e);
bail!(e) bail!(e)
}, }
} }
} }
@ -229,10 +238,13 @@ pub fn create_pot(
mod tests { mod tests {
use cozo::DbInstance; use cozo::DbInstance;
use serde_with::chrono::Utc; use serde_with::chrono::Utc;
use tracing::{Level, debug}; use tracing::{debug, Level};
use ubisync_lib::types::PotId; use ubisync_lib::types::PotId;
use crate::{state::{schema, queries::pots}, api::v0::app::AppId}; use crate::{
api::v0::app::AppId,
state::{queries::pots, schema},
};
#[test] #[test]
pub fn default_pot() { pub fn default_pot() {

View file

@ -6,11 +6,12 @@ use serde_json::Value;
use tracing::{debug, error}; use tracing::{debug, error};
use crate::{ use crate::{
api::v0::app::AppId,
run_query, run_query,
state::{Element, ElementContent, ElementId}, api::v0::app::AppId, state::{Element, ElementContent, ElementId},
}; };
use ubisync_lib::types::{MessageId, Tag, PotId}; use ubisync_lib::types::{MessageId, PotId, Tag};
pub fn add( pub fn add(
db: &DbInstance, db: &DbInstance,
@ -140,17 +141,22 @@ pub fn get_app_access(db: &DbInstance, id: &ElementId, app: &AppId) -> anyhow::R
"id".to_string(), "id".to_string(),
DataValue::Str(serde_json::to_string(&id)?.into()), DataValue::Str(serde_json::to_string(&id)?.into()),
); );
params.insert("app_id".to_string(), DataValue::Str(serde_json::to_string(&app)?.into())); params.insert(
"app_id".to_string(),
DataValue::Str(serde_json::to_string(&app)?.into()),
);
let result = db.run_script(" let result = db.run_script(
"
memberships[pot_id] := *pot_memberships{pot_id, app_id} memberships[pot_id] := *pot_memberships{pot_id, app_id}
?[id] := memberships[pot_id], *elements{id, pot: pot_id} ?[id] := memberships[pot_id], *elements{id, pot: pot_id}
", params.clone(), cozo::ScriptMutability::Immutable); ",
params.clone(),
cozo::ScriptMutability::Immutable,
);
match result { match result {
Ok(named_rows) => { Ok(named_rows) => Ok(named_rows.rows.len() > 0),
Ok(named_rows.rows.len() > 0)
},
Err(report) => bail!(report), Err(report) => bail!(report),
} }
} }

View file

@ -4,10 +4,7 @@ use anyhow::Error;
use cozo::{DataValue, DbInstance, ScriptMutability}; use cozo::{DataValue, DbInstance, ScriptMutability};
use ubisync_lib::types::{Pot, PotId}; use ubisync_lib::types::{Pot, PotId};
use crate::{ use crate::{api::v0::app::AppId, run_query};
api::v0::app::AppId,
run_query,
};
pub fn add(db: &DbInstance, id: &PotId, app_type: &str) -> anyhow::Result<()> { pub fn add(db: &DbInstance, id: &PotId, app_type: &str) -> anyhow::Result<()> {
let params = vec![ let params = vec![

View file

@ -23,13 +23,15 @@ async fn two_nodes_element_creation() {
ubi1.add_peer_from_id(ubi2.get_destination().unwrap().into()) ubi1.add_peer_from_id(ubi2.get_destination().unwrap().into())
.unwrap(); .unwrap();
let api_client1 = UbisyncClient::init("localhost", 9981, None, "App", "Long desc", "test-app-type") let api_client1 =
UbisyncClient::init("localhost", 9981, None, "App", "Long desc", "test-app-type")
.await .await
.unwrap() .unwrap()
.create_default_pot() .create_default_pot()
.await .await
.unwrap(); .unwrap();
let api_client2 = UbisyncClient::init("localhost", 9982, None, "App", "Long desc", "test-app-type") let api_client2 =
UbisyncClient::init("localhost", 9982, None, "App", "Long desc", "test-app-type")
.await .await
.unwrap(); .unwrap();