Introduced "pot" concept for isolating different apps' elements from each other. Removed tracing_setup
macro since it disabled most logging output
This commit is contained in:
parent
4f8d6ec3d0
commit
a768ce0f4e
17 changed files with 424 additions and 41 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -3047,7 +3047,6 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"axum",
|
"axum",
|
||||||
"chrono",
|
|
||||||
"cozo",
|
"cozo",
|
||||||
"i2p",
|
"i2p",
|
||||||
"itertools",
|
"itertools",
|
||||||
|
@ -3055,6 +3054,7 @@ dependencies = [
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"serde_with",
|
||||||
"serial_test",
|
"serial_test",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
|
|
|
@ -1,12 +1,15 @@
|
||||||
use reqwest::Method;
|
use reqwest::Method;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::types::PotId;
|
||||||
|
|
||||||
use super::UbisyncRequest;
|
use super::UbisyncRequest;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct AppRegisterRequest {
|
pub struct AppRegisterRequest {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub description: String,
|
pub description: String,
|
||||||
|
pub app_type: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
|
@ -25,3 +28,47 @@ impl UbisyncRequest for AppRegisterRequest {
|
||||||
"/app/register".to_string()
|
"/app/register".to_string()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct AppCreatePotRequest {
|
||||||
|
pub app_type: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct AppCreatePotResponse {
|
||||||
|
pub pot_id: PotId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UbisyncRequest for AppCreatePotRequest {
|
||||||
|
type PathParameters = ();
|
||||||
|
type Response = AppCreatePotResponse;
|
||||||
|
|
||||||
|
fn method(&self) -> Method {
|
||||||
|
Method::PUT
|
||||||
|
}
|
||||||
|
|
||||||
|
fn path(&self, _: Self::PathParameters) -> String {
|
||||||
|
"/app/pot".to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct AppSetDefaultPotRequest {
|
||||||
|
pub pot_id: PotId,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct AppSetDefaultPotResponse;
|
||||||
|
|
||||||
|
impl UbisyncRequest for AppSetDefaultPotRequest {
|
||||||
|
type PathParameters = ();
|
||||||
|
type Response = AppSetDefaultPotResponse;
|
||||||
|
|
||||||
|
fn method(&self) -> Method {
|
||||||
|
Method::POST
|
||||||
|
}
|
||||||
|
|
||||||
|
fn path(&self, _: Self::PathParameters) -> String {
|
||||||
|
"/app/pot/default".to_string()
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,15 +2,3 @@ pub mod api;
|
||||||
pub mod messages;
|
pub mod messages;
|
||||||
pub mod peer;
|
pub mod peer;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! tracing_setup {
|
|
||||||
($level:expr) => {
|
|
||||||
let _tracing_default_guard = tracing::subscriber::set_default(
|
|
||||||
tracing_subscriber::fmt()
|
|
||||||
.pretty()
|
|
||||||
.with_max_level($level)
|
|
||||||
.finish(),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
|
@ -13,5 +13,11 @@ pub use message_id::MessageId;
|
||||||
mod peer_id;
|
mod peer_id;
|
||||||
pub use peer_id::PeerId;
|
pub use peer_id::PeerId;
|
||||||
|
|
||||||
|
mod pot_id;
|
||||||
|
pub use pot_id::PotId;
|
||||||
|
|
||||||
|
mod pot;
|
||||||
|
pub use pot::Pot;
|
||||||
|
|
||||||
mod tag;
|
mod tag;
|
||||||
pub use tag::Tag;
|
pub use tag::Tag;
|
||||||
|
|
22
ubisync-lib/src/types/pot.rs
Normal file
22
ubisync-lib/src/types/pot.rs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use super::PotId;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
pub struct Pot {
|
||||||
|
id: PotId,
|
||||||
|
app_type: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pot {
|
||||||
|
pub fn new(id: PotId, app_type: String) -> Self {
|
||||||
|
Pot { id, app_type }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn id(&self) -> &PotId {
|
||||||
|
&self.id
|
||||||
|
}
|
||||||
|
pub fn app_type(&self) -> &str {
|
||||||
|
&self.app_type
|
||||||
|
}
|
||||||
|
}
|
29
ubisync-lib/src/types/pot_id.rs
Normal file
29
ubisync-lib/src/types/pot_id.rs
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
pub struct PotId(Uuid);
|
||||||
|
impl PotId {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
PotId(Uuid::new_v4())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToString for PotId {
|
||||||
|
fn to_string(&self) -> String {
|
||||||
|
self.0.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&PotId> for String {
|
||||||
|
fn from(value: &PotId) -> Self {
|
||||||
|
value.0.to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<&str> for PotId {
|
||||||
|
type Error = serde_json::Error;
|
||||||
|
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||||
|
serde_json::from_str(value)
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,9 @@
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use error::UbisyncError;
|
use error::UbisyncError;
|
||||||
use reqwest::{Client, StatusCode};
|
use reqwest::{Client, StatusCode};
|
||||||
|
use tracing::debug;
|
||||||
use ubisync_lib::api::{
|
use ubisync_lib::api::{
|
||||||
app::{AppRegisterRequest, AppRegisterResponse},
|
app::{AppRegisterRequest, AppRegisterResponse, AppCreatePotRequest},
|
||||||
UbisyncRequest,
|
UbisyncRequest,
|
||||||
};
|
};
|
||||||
pub use ubisync_lib::*;
|
pub use ubisync_lib::*;
|
||||||
|
@ -25,6 +26,7 @@ impl UbisyncClient {
|
||||||
jwt_token: Option<&str>,
|
jwt_token: Option<&str>,
|
||||||
application_name: &str,
|
application_name: &str,
|
||||||
application_description: &str,
|
application_description: &str,
|
||||||
|
application_type: &str,
|
||||||
) -> Result<Self, UbisyncError> {
|
) -> Result<Self, UbisyncError> {
|
||||||
let http_client = Client::new();
|
let http_client = Client::new();
|
||||||
let mut node_api_versions = http_client
|
let mut node_api_versions = http_client
|
||||||
|
@ -49,6 +51,7 @@ impl UbisyncClient {
|
||||||
.json(&AppRegisterRequest {
|
.json(&AppRegisterRequest {
|
||||||
name: application_name.to_string(),
|
name: application_name.to_string(),
|
||||||
description: application_description.to_string(),
|
description: application_description.to_string(),
|
||||||
|
app_type: application_type.to_string(),
|
||||||
})
|
})
|
||||||
.send()
|
.send()
|
||||||
.await
|
.await
|
||||||
|
@ -74,6 +77,14 @@ impl UbisyncClient {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn create_default_pot(self) -> anyhow::Result<UbisyncClient> {
|
||||||
|
let response = self.send(AppCreatePotRequest {
|
||||||
|
app_type: None,
|
||||||
|
}, ()).await?;
|
||||||
|
debug!("Created new pot with ID {:?}", response.pot_id);
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn send<R>(
|
pub async fn send<R>(
|
||||||
&self,
|
&self,
|
||||||
request: R,
|
request: R,
|
||||||
|
|
|
@ -8,12 +8,12 @@ edition = "2021"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
anyhow = "1.0.71"
|
anyhow = "1.0.71"
|
||||||
axum = { version = "0.7.2", features = [ "macros" ] }
|
axum = { version = "0.7.2", features = [ "macros" ] }
|
||||||
chrono = "0.4.31"
|
|
||||||
itertools = "0.12.0"
|
itertools = "0.12.0"
|
||||||
cozo = { version = "0.7.5", features = [ "storage-rocksdb", "requests", "graph-algo" ] }
|
cozo = { version = "0.7.5", features = [ "storage-rocksdb", "requests", "graph-algo" ] }
|
||||||
jsonwebtoken = "9.2.0"
|
jsonwebtoken = "9.2.0"
|
||||||
serde = { version = "1.0.166", features = [ "derive" ] }
|
serde = { version = "1.0.166", features = [ "derive" ] }
|
||||||
serde_json = "1.0.99"
|
serde_json = "1.0.99"
|
||||||
|
serde_with = { version = "3.4.0", features = [ "chrono" ] }
|
||||||
serial_test = "2.0.0"
|
serial_test = "2.0.0"
|
||||||
tokio = { version = "1.32.0", features = ["full"] }
|
tokio = { version = "1.32.0", features = ["full"] }
|
||||||
tracing = "0.1.37"
|
tracing = "0.1.37"
|
||||||
|
|
|
@ -8,10 +8,12 @@ use axum::{
|
||||||
response::{IntoResponse, Response},
|
response::{IntoResponse, Response},
|
||||||
Extension, Json,
|
Extension, Json,
|
||||||
};
|
};
|
||||||
|
|
||||||
use jsonwebtoken::{decode, Header};
|
use jsonwebtoken::{decode, Header};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_with::chrono::{DateTime, Utc};
|
||||||
use tracing::{debug, error, warn};
|
use tracing::{debug, error, warn};
|
||||||
use ubisync_lib::api::app::{AppRegisterRequest, AppRegisterResponse};
|
use ubisync_lib::{api::app::{AppRegisterRequest, AppRegisterResponse, AppSetDefaultPotRequest, AppSetDefaultPotResponse, AppCreatePotRequest, AppCreatePotResponse}, types::PotId};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::state::ApiState;
|
use crate::state::ApiState;
|
||||||
|
@ -25,6 +27,16 @@ impl AppId {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
pub struct App {
|
||||||
|
pub id: AppId,
|
||||||
|
pub app_type: String,
|
||||||
|
pub name: String,
|
||||||
|
pub description: String,
|
||||||
|
pub default_pot: Option<PotId>,
|
||||||
|
pub last_access: DateTime<Utc>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
struct JWTClaims {
|
struct JWTClaims {
|
||||||
sub: AppId,
|
sub: AppId,
|
||||||
|
@ -63,7 +75,7 @@ pub(super) async fn register(
|
||||||
// Maybe ask for consent by user
|
// Maybe ask for consent by user
|
||||||
|
|
||||||
// If user wants registration, proceed
|
// If user wants registration, proceed
|
||||||
let result = s.add_app(&body.name, &body.description);
|
let result = s.add_app(&body.name, &body.description, &body.app_type);
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(id) => {
|
Ok(id) => {
|
||||||
|
@ -93,3 +105,57 @@ pub(super) async fn register(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) async fn set_default_pot(
|
||||||
|
s: Extension<Arc<ApiState>>,
|
||||||
|
app_id: Extension<AppId>,
|
||||||
|
Json(body): Json<AppSetDefaultPotRequest>,
|
||||||
|
) -> Response {
|
||||||
|
match s.set_app_default_pot(&app_id.0, &body.pot_id) {
|
||||||
|
Ok(_) => (StatusCode::OK, Json { 0: AppSetDefaultPotResponse }).into_response(),
|
||||||
|
Err(e) => {
|
||||||
|
debug!("Could not set default pot: {}", e);
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR.into_response()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) async fn create_pot(
|
||||||
|
s: Extension<Arc<ApiState>>,
|
||||||
|
app_id: Extension<AppId>,
|
||||||
|
Json(body): Json<AppCreatePotRequest>,
|
||||||
|
) -> Response {
|
||||||
|
let app = match s.get_app(&app_id.0) {
|
||||||
|
Ok(a) => a,
|
||||||
|
Err(e) => {
|
||||||
|
debug!("Failed to fetch app: {}", e);
|
||||||
|
return StatusCode::INTERNAL_SERVER_ERROR.into_response()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let inferred_app_type = match body.app_type {
|
||||||
|
Some(t) => t,
|
||||||
|
None => app.app_type
|
||||||
|
};
|
||||||
|
match s.create_pot(&app_id.0, &inferred_app_type) {
|
||||||
|
Ok(id) => {
|
||||||
|
// If this is the first pot for this app, set it as default
|
||||||
|
if app.default_pot.is_none() {
|
||||||
|
match s.set_app_default_pot(&app_id.0, &id) {
|
||||||
|
Ok(_) => (StatusCode::OK, Json { 0: AppCreatePotResponse {pot_id: id} }).into_response(),
|
||||||
|
Err(e) => {
|
||||||
|
debug!("Failed to set default pot: {}", e);
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR.into_response()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Otherwise, just return
|
||||||
|
else {
|
||||||
|
(StatusCode::OK, Json { 0: AppCreatePotResponse {pot_id: id} }).into_response()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
debug!("Could not create pot: {}", e);
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR.into_response()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
use axum::{
|
use axum::{
|
||||||
middleware::from_fn,
|
middleware::from_fn,
|
||||||
routing::{get, put},
|
routing::{get, put, post},
|
||||||
Extension, Router,
|
Extension, Router,
|
||||||
};
|
};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -13,6 +13,8 @@ pub mod element;
|
||||||
pub fn get_router(state: ApiState) -> Router {
|
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/default", post(app::set_default_pot))
|
||||||
.route("/element", put(element::create))
|
.route("/element", put(element::create))
|
||||||
.route(
|
.route(
|
||||||
"/element/:id",
|
"/element/:id",
|
||||||
|
|
|
@ -1,15 +1,18 @@
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use chrono::Utc;
|
|
||||||
use cozo::DbInstance;
|
use cozo::DbInstance;
|
||||||
use jsonwebtoken::{DecodingKey, EncodingKey, Validation};
|
use jsonwebtoken::{DecodingKey, EncodingKey, Validation};
|
||||||
|
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},
|
types::{Element, ElementContent, ElementId, PotId},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{api::v0::app::AppId, state::queries};
|
use crate::{
|
||||||
|
api::v0::app::{App, AppId},
|
||||||
|
state::queries,
|
||||||
|
};
|
||||||
|
|
||||||
use super::State;
|
use super::State;
|
||||||
|
|
||||||
|
@ -32,10 +35,10 @@ impl ApiState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_app(&self, name: &str, description: &str) -> anyhow::Result<AppId> {
|
pub fn add_app(&self, name: &str, description: &str, app_type: &str) -> anyhow::Result<AppId> {
|
||||||
let id = AppId::new();
|
let id = AppId::new();
|
||||||
let last_access = Utc::now();
|
let last_access = Utc::now();
|
||||||
queries::apps::add(self.db(), &id, &last_access, name, description)?;
|
queries::apps::add(self.db(), &id, &last_access, name, description, app_type)?;
|
||||||
debug!("Successfully added app");
|
debug!("Successfully added app");
|
||||||
|
|
||||||
Ok(id)
|
Ok(id)
|
||||||
|
@ -45,6 +48,10 @@ impl ApiState {
|
||||||
queries::apps::exists(self.db(), id)
|
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) -> anyhow::Result<ElementId> {
|
pub fn create_element(&self, content: &ElementContent) -> anyhow::Result<ElementId> {
|
||||||
let id = ElementId::new();
|
let id = ElementId::new();
|
||||||
queries::elements::add(self.db(), &id, &content, None, true)?;
|
queries::elements::add(self.db(), &id, &content, None, true)?;
|
||||||
|
@ -76,6 +83,16 @@ impl ApiState {
|
||||||
res
|
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 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)?;
|
||||||
|
Ok(pot_id)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_element(&self, id: &ElementId) -> anyhow::Result<Element> {
|
pub fn get_element(&self, id: &ElementId) -> anyhow::Result<Element> {
|
||||||
self.state.get_element(id)
|
self.state.get_element(id)
|
||||||
}
|
}
|
||||||
|
@ -100,7 +117,7 @@ impl ApiState {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use tracing::Level;
|
use tracing::Level;
|
||||||
use ubisync_lib::{tracing_setup, types::ElementContent};
|
use ubisync_lib::types::ElementContent;
|
||||||
|
|
||||||
use crate::state::State;
|
use crate::state::State;
|
||||||
|
|
||||||
|
@ -109,7 +126,10 @@ mod tests {
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial_test::serial]
|
#[serial_test::serial]
|
||||||
async fn test_element_create() {
|
async fn test_element_create() {
|
||||||
tracing_setup!(Level::DEBUG);
|
tracing_subscriber::fmt()
|
||||||
|
.pretty()
|
||||||
|
.with_max_level(Level::DEBUG)
|
||||||
|
.init();
|
||||||
|
|
||||||
let state = ApiState::new(
|
let state = ApiState::new(
|
||||||
State::new("mem").await.unwrap(),
|
State::new("mem").await.unwrap(),
|
||||||
|
@ -128,7 +148,10 @@ mod tests {
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial_test::serial]
|
#[serial_test::serial]
|
||||||
async fn test_element_write() {
|
async fn test_element_write() {
|
||||||
tracing_setup!(Level::DEBUG);
|
tracing_subscriber::fmt()
|
||||||
|
.pretty()
|
||||||
|
.with_max_level(Level::DEBUG)
|
||||||
|
.init();
|
||||||
|
|
||||||
let state = ApiState::new(
|
let state = ApiState::new(
|
||||||
State::new("mem").await.unwrap(),
|
State::new("mem").await.unwrap(),
|
||||||
|
|
|
@ -88,15 +88,15 @@ mod tests {
|
||||||
use super::CommState;
|
use super::CommState;
|
||||||
|
|
||||||
use tracing::Level;
|
use tracing::Level;
|
||||||
use ubisync_lib::{
|
use ubisync_lib::types::{ElementContent, ElementId, MessageId};
|
||||||
tracing_setup,
|
|
||||||
types::{ElementContent, ElementId, MessageId},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial_test::serial]
|
#[serial_test::serial]
|
||||||
async fn test_element_add() {
|
async fn test_element_add() {
|
||||||
tracing_setup!(Level::DEBUG);
|
tracing_subscriber::fmt()
|
||||||
|
.pretty()
|
||||||
|
.with_max_level(Level::DEBUG)
|
||||||
|
.init();
|
||||||
|
|
||||||
let state = CommState::new(State::new("mem").await.unwrap());
|
let state = CommState::new(State::new("mem").await.unwrap());
|
||||||
let id = ElementId::new();
|
let id = ElementId::new();
|
||||||
|
@ -117,7 +117,10 @@ mod tests {
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
#[serial_test::serial]
|
#[serial_test::serial]
|
||||||
async fn test_element_update() {
|
async fn test_element_update() {
|
||||||
tracing_setup!(Level::DEBUG);
|
tracing_subscriber::fmt()
|
||||||
|
.pretty()
|
||||||
|
.with_max_level(Level::DEBUG)
|
||||||
|
.init();
|
||||||
|
|
||||||
let state = CommState::new(State::new("mem").await.unwrap());
|
let state = CommState::new(State::new("mem").await.unwrap());
|
||||||
let id = ElementId::new();
|
let id = ElementId::new();
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use anyhow::{bail, Error};
|
use anyhow::{bail, Error};
|
||||||
use chrono::{DateTime, Utc};
|
use cozo::{DataValue, DbInstance, Num, ScriptMutability};
|
||||||
use cozo::{DataValue, DbInstance, Num};
|
use serde_with::chrono::{DateTime, Utc};
|
||||||
|
use ubisync_lib::types::PotId;
|
||||||
|
|
||||||
use crate::{api::v0::app::AppId, run_query};
|
use crate::{api::v0::app::{AppId, App}, run_query};
|
||||||
|
|
||||||
pub fn add(
|
pub fn add(
|
||||||
db: &DbInstance,
|
db: &DbInstance,
|
||||||
|
@ -12,6 +13,7 @@ pub fn add(
|
||||||
last_access: &DateTime<Utc>,
|
last_access: &DateTime<Utc>,
|
||||||
name: &str,
|
name: &str,
|
||||||
description: &str,
|
description: &str,
|
||||||
|
app_type: &str,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
let params = vec![
|
let params = vec![
|
||||||
(
|
(
|
||||||
|
@ -24,11 +26,13 @@ pub fn add(
|
||||||
),
|
),
|
||||||
("name", DataValue::Str(name.into())),
|
("name", DataValue::Str(name.into())),
|
||||||
("description", DataValue::Str(description.into())),
|
("description", DataValue::Str(description.into())),
|
||||||
|
("app_type", DataValue::Str(app_type.into())),
|
||||||
|
("default_pot", DataValue::Null),
|
||||||
];
|
];
|
||||||
|
|
||||||
match run_query!(
|
match run_query!(
|
||||||
&db,
|
&db,
|
||||||
":insert apps {id => last_access, name, description}",
|
":insert apps {id => last_access, app_type, name, description, default_pot}",
|
||||||
params,
|
params,
|
||||||
cozo::ScriptMutability::Mutable
|
cozo::ScriptMutability::Mutable
|
||||||
) {
|
) {
|
||||||
|
@ -45,7 +49,7 @@ pub fn exists(db: &DbInstance, id: &AppId) -> anyhow::Result<bool> {
|
||||||
);
|
);
|
||||||
|
|
||||||
let result = db.run_script(
|
let result = db.run_script(
|
||||||
"?[name] := *apps[$id, last_access, name, description]",
|
"?[name] := *apps[$id, last_access, app_type, name, description, default_pot]",
|
||||||
params,
|
params,
|
||||||
cozo::ScriptMutability::Immutable,
|
cozo::ScriptMutability::Immutable,
|
||||||
);
|
);
|
||||||
|
@ -56,3 +60,68 @@ pub fn exists(db: &DbInstance, id: &AppId) -> anyhow::Result<bool> {
|
||||||
|
|
||||||
Err(Error::msg("Could not check whether app is registered"))
|
Err(Error::msg("Could not check whether app is registered"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get(db: &DbInstance, id: &AppId) -> anyhow::Result<App> {
|
||||||
|
let mut params = BTreeMap::new();
|
||||||
|
params.insert(
|
||||||
|
"id".to_string(),
|
||||||
|
DataValue::Str(serde_json::to_string(&id)?.into()),
|
||||||
|
);
|
||||||
|
|
||||||
|
let result = db.run_script(
|
||||||
|
"?[last_access, app_type, name, description, default_pot] := *apps[$id, last_access, app_type, name, description, default_pot]",
|
||||||
|
params,
|
||||||
|
cozo::ScriptMutability::Immutable,
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Ok(rows) = result {
|
||||||
|
if let Some(firstrow) = rows.rows.first() {
|
||||||
|
if let [DataValue::Num(Num::Int(ts)), DataValue::Str(app_type), DataValue::Str(name), DataValue::Str(desc), default_pot] = firstrow.as_slice() {
|
||||||
|
let last_access = DateTime::from_timestamp(ts.to_owned(), 0).ok_or(Error::msg("Failed to deserialize timestamp"))?;
|
||||||
|
let pot_id = match default_pot {
|
||||||
|
DataValue::Str(dpid) => Some(serde_json::from_str(dpid)?),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
return Ok(App {id: id.clone(), app_type: app_type.to_string(), last_access, name: name.to_string(), description: desc.to_string(), default_pot: pot_id});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(Error::msg("Could not find app"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_default_pot(db: &DbInstance, id: &AppId, pot: &PotId) -> anyhow::Result<()> {
|
||||||
|
let params = vec![
|
||||||
|
("id", DataValue::Str(serde_json::to_string(id)?.into())),
|
||||||
|
("default_pot", DataValue::Str(serde_json::to_string(pot)?.into()))
|
||||||
|
];
|
||||||
|
|
||||||
|
match run_query!(
|
||||||
|
&db,
|
||||||
|
":update apps {id => default_pot}",
|
||||||
|
params,
|
||||||
|
ScriptMutability::Mutable
|
||||||
|
) {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(report) => bail!(report),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_pot(db: &DbInstance, pot_id: &PotId, app_id: &AppId, app_type: &str) -> anyhow::Result<()> {
|
||||||
|
let params = vec![
|
||||||
|
("pot_id", DataValue::Str(serde_json::to_string(pot_id)?.into())),
|
||||||
|
("app_id", DataValue::Str(serde_json::to_string(app_id)?.into())),
|
||||||
|
("app_type", DataValue::Str(app_type.into())),
|
||||||
|
];
|
||||||
|
|
||||||
|
match run_query!(
|
||||||
|
&db,
|
||||||
|
":insert pots {pot_id => app_type}
|
||||||
|
:insert pot_memberships {pot_id => app_id}",
|
||||||
|
params,
|
||||||
|
ScriptMutability::Mutable
|
||||||
|
) {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(e) => bail!(e),
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
pub mod apps;
|
pub mod apps;
|
||||||
pub mod elements;
|
pub mod elements;
|
||||||
pub mod peers;
|
pub mod peers;
|
||||||
|
pub mod pots;
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! build_query {
|
macro_rules! build_query {
|
||||||
|
|
96
ubisync/src/state/queries/pots.rs
Normal file
96
ubisync/src/state/queries/pots.rs
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
|
use anyhow::Error;
|
||||||
|
use cozo::{DataValue, DbInstance, ScriptMutability};
|
||||||
|
use ubisync_lib::types::{Pot, PotId};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
api::v0::app::AppId,
|
||||||
|
run_query,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn add(db: &DbInstance, id: &PotId, app_type: &str) -> anyhow::Result<()> {
|
||||||
|
let params = vec![
|
||||||
|
("id", DataValue::Str(serde_json::to_string(id)?.into())),
|
||||||
|
(
|
||||||
|
"app_type",
|
||||||
|
DataValue::Str(serde_json::to_string(app_type)?.into()),
|
||||||
|
),
|
||||||
|
];
|
||||||
|
|
||||||
|
match run_query!(
|
||||||
|
&db,
|
||||||
|
":put pots {id => app_type}",
|
||||||
|
params,
|
||||||
|
ScriptMutability::Mutable
|
||||||
|
) {
|
||||||
|
Ok(_) => Ok(()),
|
||||||
|
Err(report) => Err(Error::msg(format!("Query failed: {}", report))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(db: &DbInstance, id: &PotId) -> anyhow::Result<Pot> {
|
||||||
|
let mut params = BTreeMap::new();
|
||||||
|
params.insert(
|
||||||
|
"id".to_string(),
|
||||||
|
DataValue::Str(serde_json::to_string(&id)?.into()),
|
||||||
|
);
|
||||||
|
let result = db.run_script(
|
||||||
|
"
|
||||||
|
?[id, app_type] := *pots{id, app_type}
|
||||||
|
",
|
||||||
|
params,
|
||||||
|
cozo::ScriptMutability::Immutable,
|
||||||
|
);
|
||||||
|
match result {
|
||||||
|
Ok(rows) => {
|
||||||
|
if let Some(firstrow) = rows.rows.first() {
|
||||||
|
if let [DataValue::Str(pot_id), DataValue::Str(app_type)] = firstrow.as_slice() {
|
||||||
|
Ok(Pot::new(
|
||||||
|
serde_json::from_str(pot_id)?,
|
||||||
|
app_type.to_string(),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
Err(Error::msg("Could not parse result from query"))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(Error::msg("Pot not found"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(report) => Err(Error::msg(format!("Query failed: {}", report))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_pot_members(db: &DbInstance, id: &PotId) -> anyhow::Result<Vec<AppId>> {
|
||||||
|
let mut params = BTreeMap::new();
|
||||||
|
params.insert(
|
||||||
|
"id".to_string(),
|
||||||
|
DataValue::Str(serde_json::to_string(&id)?.into()),
|
||||||
|
);
|
||||||
|
let result = db.run_script(
|
||||||
|
"
|
||||||
|
?[app_id] := *pot_memberships{id, app_id}
|
||||||
|
",
|
||||||
|
params,
|
||||||
|
cozo::ScriptMutability::Immutable,
|
||||||
|
);
|
||||||
|
match result {
|
||||||
|
Ok(rows) => Ok(rows
|
||||||
|
.rows
|
||||||
|
.iter()
|
||||||
|
.map(|row| {
|
||||||
|
if let [DataValue::Str(app_id_string)] = row.as_slice() {
|
||||||
|
if let Ok(app_id) = serde_json::from_str::<AppId>(&app_id_string) {
|
||||||
|
Some(app_id)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.flatten()
|
||||||
|
.collect()),
|
||||||
|
Err(report) => Err(Error::msg(format!("Query failed: {}", report))),
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,14 +11,26 @@ pub fn add_schema(db: &DbInstance) -> anyhow::Result<()> {
|
||||||
id: String,
|
id: String,
|
||||||
=>
|
=>
|
||||||
last_access: Int,
|
last_access: Int,
|
||||||
|
app_type: String,
|
||||||
name: String,
|
name: String,
|
||||||
description: String,
|
description: String,
|
||||||
|
default_pot: String?,
|
||||||
}}
|
}}
|
||||||
{:create peers {
|
{:create peers {
|
||||||
id: String,
|
id: String,
|
||||||
=>
|
=>
|
||||||
name: String,
|
name: String,
|
||||||
}}
|
}}
|
||||||
|
{:create pots {
|
||||||
|
id: String,
|
||||||
|
=>
|
||||||
|
app_type: String
|
||||||
|
}}
|
||||||
|
{:create pot_memberships {
|
||||||
|
pot_id: String,
|
||||||
|
=>
|
||||||
|
app_id: String,
|
||||||
|
}}
|
||||||
{:create elements {
|
{:create elements {
|
||||||
id: String,
|
id: String,
|
||||||
=>
|
=>
|
||||||
|
|
|
@ -4,14 +4,16 @@ use tracing::{debug, warn, Level};
|
||||||
use ubisync::{config::Config, Ubisync};
|
use ubisync::{config::Config, Ubisync};
|
||||||
use ubisync_lib::{
|
use ubisync_lib::{
|
||||||
api::element::{ElementCreateRequest, ElementGetRequest},
|
api::element::{ElementCreateRequest, ElementGetRequest},
|
||||||
tracing_setup,
|
|
||||||
types::{Element, ElementContent},
|
types::{Element, ElementContent},
|
||||||
};
|
};
|
||||||
use ubisync_sdk::UbisyncClient;
|
use ubisync_sdk::UbisyncClient;
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn two_nodes_element_creation() {
|
async fn two_nodes_element_creation() {
|
||||||
tracing_setup!(Level::DEBUG);
|
tracing_subscriber::fmt()
|
||||||
|
.pretty()
|
||||||
|
.with_max_level(Level::DEBUG)
|
||||||
|
.init();
|
||||||
|
|
||||||
// Two nodes need to bind to different ports
|
// Two nodes need to bind to different ports
|
||||||
let mut c2 = Config::default();
|
let mut c2 = Config::default();
|
||||||
|
@ -21,10 +23,16 @@ 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")
|
let api_client1 = UbisyncClient::init("localhost", 9981, None, "App", "Long desc", "test-app-type")
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.create_default_pot()
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let api_client2 = UbisyncClient::init("localhost", 9982, None, "App", "Long desc")
|
let api_client2 = UbisyncClient::init("localhost", 9982, None, "App", "Long desc", "test-app-type")
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.create_default_pot()
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue