ubisync/ubisync/src/state/api_state.rs
Philip (a-0) 76f6a6b67b rustfmt
2024-01-21 12:25:11 +01:00

208 lines
6 KiB
Rust

use std::sync::Arc;
use anyhow::Error;
use cozo::DbInstance;
use jsonwebtoken::{DecodingKey, EncodingKey, Validation};
use serde_with::chrono::Utc;
use tracing::debug;
use ubisync_lib::{
messages::MessageContent,
types::{Element, ElementContent, ElementId, Pot, PotId},
};
use crate::{
api::v0::app::{App, AppId},
state::queries,
};
use super::State;
pub struct ApiState {
state: Arc<State>,
jwt_encoding_key: EncodingKey,
jwt_decoding_key: DecodingKey,
jwt_validation: Validation,
}
impl ApiState {
pub fn new(state: Arc<State>, jwt_secret: &str) -> Self {
let mut validation = Validation::default();
validation.set_required_spec_claims(&vec!["sub"]);
ApiState {
state: state,
jwt_encoding_key: EncodingKey::from_secret(jwt_secret.as_bytes()),
jwt_decoding_key: DecodingKey::from_secret(jwt_secret.as_bytes()),
jwt_validation: validation,
}
}
pub fn add_app(&self, name: &str, description: &str, app_type: &str) -> anyhow::Result<AppId> {
let id = AppId::new();
let last_access = Utc::now();
queries::apps::add(self.db(), &id, &last_access, name, description, app_type)?;
debug!("Successfully added app");
Ok(id)
}
pub fn app_exists(&self, id: &AppId) -> anyhow::Result<bool> {
queries::apps::exists(self.db(), id)
}
pub fn get_app(&self, id: &AppId) -> anyhow::Result<App> {
queries::apps::get(self.db(), id)
}
pub fn create_element(
&self,
content: &ElementContent,
pot: &PotId,
) -> anyhow::Result<ElementId> {
let id = ElementId::new();
queries::elements::add(self.db(), &id, &content, None, true, pot)?;
debug!("Added element {{{}}}", &id.to_string());
self.state.send_to_peers(MessageContent::CreateElement {
id: id.clone(),
content: content.clone(),
pot: pot.clone(),
});
Ok(id)
}
pub fn write_element_content(
&self,
id: &ElementId,
app: &AppId,
content: &ElementContent,
) -> anyhow::Result<()> {
if queries::elements::get_app_access(self.db(), id, app)? {
queries::elements::set_content(self.db(), id, content)?;
queries::elements::set_local_changes(self.db(), id, true)?;
debug!("Wrote element content {{{}}}", &id.to_string());
Ok(())
} else {
Err(Error::msg(
"Element does not exist or app does not have access to it",
))
}
}
pub fn remove_element(&self, id: &ElementId) -> anyhow::Result<()> {
let res = self.state.remove_element(id);
debug!("Removed element {{{}}}", &id.to_string());
res
}
pub fn set_app_default_pot(&self, app_id: &AppId, pot_id: &PotId) -> anyhow::Result<()> {
queries::apps::set_default_pot(self.db(), app_id, pot_id)
}
pub fn get_default_pot(&self, app_id: &AppId) -> anyhow::Result<Pot> {
queries::apps::get_default_pot(self.db(), app_id)
}
pub fn create_pot(&self, app_id: &AppId, app_type: &str) -> anyhow::Result<PotId> {
let pot_id = PotId::new();
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(),
});
Ok(pot_id)
}
pub fn get_element(&self, id: &ElementId, app: &AppId) -> anyhow::Result<Element> {
if queries::elements::get_app_access(self.db(), id, app)? {
self.state.get_element(id)
} else {
Err(Error::msg(
"Element does not exist or app does not have access to it",
))
}
}
fn db(&self) -> &DbInstance {
&self.state.db
}
pub fn jwt_encoding_key(&self) -> &EncodingKey {
&self.jwt_encoding_key
}
pub fn jwt_decoding_key(&self) -> &DecodingKey {
&self.jwt_decoding_key
}
pub fn jwt_validation(&self) -> &Validation {
&self.jwt_validation
}
}
#[cfg(test)]
mod tests {
use tracing::Level;
use ubisync_lib::types::ElementContent;
use crate::state::State;
use super::ApiState;
#[tokio::test]
#[serial_test::serial]
async fn test_element_create() {
tracing_subscriber::fmt()
.pretty()
.with_max_level(Level::DEBUG)
.init();
let state = ApiState::new(
State::new("mem").await.unwrap(),
"abcdabcdabcdabcdabcdabcdabcdabcd",
);
let app_id = state.add_app("appname", "appdesc", "apptype").unwrap();
let pot_id = state.create_pot(&app_id, "apptype").unwrap();
let id = state
.create_element(&ElementContent::Text("Test-text".to_string()), &pot_id)
.unwrap();
let el = state.get_element(&id, &app_id).unwrap();
assert_eq!(
ElementContent::Text("Test-text".to_string()),
el.content().to_owned()
)
}
#[tokio::test]
#[serial_test::serial]
async fn test_element_write() {
tracing_subscriber::fmt()
.pretty()
.with_max_level(Level::DEBUG)
.init();
let state = ApiState::new(
State::new("mem").await.unwrap(),
"abcdabcdabcdabcdabcdabcdabcdabcd",
);
let app_id = state.add_app("appname", "appdesc", "apptype").unwrap();
let pot_id = state.create_pot(&app_id, "apptype").unwrap();
let id = state
.create_element(&ElementContent::Text("Test-text".to_string()), &pot_id)
.unwrap();
state
.write_element_content(
&id,
&app_id,
&ElementContent::Text("Test-text 2".to_string()),
)
.unwrap();
let el = state.get_element(&id, &app_id).unwrap();
assert_eq!(
ElementContent::Text("Test-text 2".to_string()),
el.content().to_owned()
)
}
}