Database handle remodeling
This commit is contained in:
parent
e0b83d30b6
commit
f2c9fe48a1
11 changed files with 489 additions and 361 deletions
|
@ -41,19 +41,22 @@ impl ApiState {
|
||||||
app_type: String,
|
app_type: String,
|
||||||
) -> anyhow::Result<AppId> {
|
) -> anyhow::Result<AppId> {
|
||||||
let id = AppId::new();
|
let id = AppId::new();
|
||||||
self.db().add_app(id.clone(), name, description, app_type)?;
|
self.db()
|
||||||
|
.apps()
|
||||||
|
.add(id.clone(), name, description, app_type)?;
|
||||||
debug!("Successfully added app");
|
debug!("Successfully added app");
|
||||||
|
|
||||||
Ok(id)
|
Ok(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn app_exists(&self, id: AppId) -> anyhow::Result<bool> {
|
pub fn app_exists(&self, id: AppId) -> anyhow::Result<bool> {
|
||||||
Ok(self.db().get_app(id)?.is_some())
|
Ok(self.db().apps().get(id)?.is_some())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_app(&self, id: AppId) -> anyhow::Result<App> {
|
pub fn get_app(&self, id: AppId) -> anyhow::Result<App> {
|
||||||
self.db()
|
self.db()
|
||||||
.get_app(id)
|
.apps()
|
||||||
|
.get(id)
|
||||||
.map(|app_opt| app_opt.ok_or(Error::msg("Failed to find app")))?
|
.map(|app_opt| app_opt.ok_or(Error::msg("Failed to find app")))?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +67,7 @@ impl ApiState {
|
||||||
update_strategy: ContentUpdateStrategy,
|
update_strategy: ContentUpdateStrategy,
|
||||||
) -> anyhow::Result<ElementId> {
|
) -> anyhow::Result<ElementId> {
|
||||||
let id = ElementId::new();
|
let id = ElementId::new();
|
||||||
self.db().add_element(
|
self.db().elements().add(
|
||||||
id.clone(),
|
id.clone(),
|
||||||
content.clone(),
|
content.clone(),
|
||||||
update_strategy,
|
update_strategy,
|
||||||
|
@ -82,7 +85,7 @@ impl ApiState {
|
||||||
},
|
},
|
||||||
self.state
|
self.state
|
||||||
.own_peer_id()
|
.own_peer_id()
|
||||||
.map(|id| self.db().get_peer_family_members(id).unwrap_or_default())
|
.map(|id| self.db().families().get_members(id).unwrap_or_default())
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -95,9 +98,9 @@ impl ApiState {
|
||||||
app: AppId,
|
app: AppId,
|
||||||
content: ElementContent,
|
content: ElementContent,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
if self.db().app_has_access(app, id.clone())? {
|
if self.db().apps().has_access(app, id.clone())? {
|
||||||
self.db().set_element_content(id.clone(), content)?;
|
self.db().elements().set_content(id.clone(), content)?;
|
||||||
self.db().set_element_local_changes(id.clone(), true)?;
|
self.db().elements().set_local_changes(id.clone(), true)?;
|
||||||
debug!("Wrote element content {{{}}}", id.to_string());
|
debug!("Wrote element content {{{}}}", id.to_string());
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -110,22 +113,23 @@ impl ApiState {
|
||||||
|
|
||||||
pub fn remove_element(&self, id: ElementId) -> anyhow::Result<()> {
|
pub fn remove_element(&self, id: ElementId) -> anyhow::Result<()> {
|
||||||
self.db()
|
self.db()
|
||||||
.remove_element(id.clone())
|
.elements()
|
||||||
|
.remove(id.clone())
|
||||||
.inspect(|_| debug!("Removed element {{{}}}", id.to_string()))
|
.inspect(|_| debug!("Removed element {{{}}}", id.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_app_default_pot(&self, app_id: AppId, pot_id: PotId) -> anyhow::Result<()> {
|
pub fn set_app_default_pot(&self, app_id: AppId, pot_id: PotId) -> anyhow::Result<()> {
|
||||||
self.db().set_default_pot(app_id, pot_id)
|
self.db().apps().set_default_pot(app_id, pot_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_default_pot(&self, app_id: AppId) -> anyhow::Result<Option<Pot>> {
|
pub fn get_default_pot(&self, app_id: AppId) -> anyhow::Result<Option<Pot>> {
|
||||||
self.db().get_default_pot(app_id)
|
self.db().apps().get_default_pot(app_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_pot(&self, app_id: AppId, app_type: String) -> anyhow::Result<PotId> {
|
pub fn create_pot(&self, app_id: AppId, app_type: String) -> anyhow::Result<PotId> {
|
||||||
let pot_id = PotId::new();
|
let pot_id = PotId::new();
|
||||||
self.db().add_pot(pot_id.clone(), app_type.clone())?;
|
self.db().pots().add(pot_id.clone(), app_type.clone())?;
|
||||||
self.db().add_pot_membership(pot_id.clone(), app_id)?;
|
self.db().pot_memberships().add(pot_id.clone(), app_id)?;
|
||||||
|
|
||||||
self.state.send_to_peers(
|
self.state.send_to_peers(
|
||||||
MessageContent::AddPot {
|
MessageContent::AddPot {
|
||||||
|
@ -134,16 +138,17 @@ impl ApiState {
|
||||||
},
|
},
|
||||||
self.state
|
self.state
|
||||||
.own_peer_id()
|
.own_peer_id()
|
||||||
.map(|id| self.db().get_peer_family_members(id).unwrap_or_default())
|
.map(|id| self.db().families().get_members(id).unwrap_or_default())
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
);
|
);
|
||||||
Ok(pot_id)
|
Ok(pot_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_element(&self, id: ElementId, app: AppId) -> anyhow::Result<Element> {
|
pub fn get_element(&self, id: ElementId, app: AppId) -> anyhow::Result<Element> {
|
||||||
if self.db().app_has_access(app, id.clone())? {
|
if self.db().apps().has_access(app, id.clone())? {
|
||||||
self.db()
|
self.db()
|
||||||
.get_element(id)
|
.elements()
|
||||||
|
.get(id)
|
||||||
.ok_or(Error::msg("Could not get element"))
|
.ok_or(Error::msg("Could not get element"))
|
||||||
} else {
|
} else {
|
||||||
Err(Error::msg(
|
Err(Error::msg(
|
||||||
|
|
|
@ -33,7 +33,8 @@ impl CommState {
|
||||||
pot_id: PotId,
|
pot_id: PotId,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
self.db()
|
self.db()
|
||||||
.add_element(
|
.elements()
|
||||||
|
.add(
|
||||||
id.clone(),
|
id.clone(),
|
||||||
content,
|
content,
|
||||||
update_strategy,
|
update_strategy,
|
||||||
|
@ -51,14 +52,15 @@ impl CommState {
|
||||||
latest_message: MessageId,
|
latest_message: MessageId,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
//TODO: resolve potential conflicts with local changes
|
//TODO: resolve potential conflicts with local changes
|
||||||
self.db().set_element_content(id.clone(), content)?;
|
self.db().elements().set_content(id.clone(), content)?;
|
||||||
self.db()
|
self.db()
|
||||||
.set_element_latest_message(id.clone(), Some(latest_message))?;
|
.elements()
|
||||||
|
.set_latest_message(id.clone(), Some(latest_message))?;
|
||||||
debug!("Updated element {{{}}}", id.to_string());
|
debug!("Updated element {{{}}}", id.to_string());
|
||||||
|
|
||||||
if let Some(el) = self.db().get_element(id.clone()) {
|
if let Some(el) = self.db().elements().get(id.clone()) {
|
||||||
if let Some(pot) = el.pot() {
|
if let Some(pot) = el.pot() {
|
||||||
if let Ok(apps) = self.db().get_pot_members(pot.to_owned()) {
|
if let Ok(apps) = self.db().pot_memberships().get_members(pot.to_owned()) {
|
||||||
for app in apps {
|
for app in apps {
|
||||||
self.state
|
self.state
|
||||||
.emit_app_event(&app, AppEvent::ElementUpdate { id: id.clone() })
|
.emit_app_event(&app, AppEvent::ElementUpdate { id: id.clone() })
|
||||||
|
@ -72,17 +74,19 @@ impl CommState {
|
||||||
|
|
||||||
pub fn remove_element(&self, id: ElementId) -> anyhow::Result<()> {
|
pub fn remove_element(&self, id: ElementId) -> anyhow::Result<()> {
|
||||||
self.db()
|
self.db()
|
||||||
.remove_element(id.clone())
|
.elements()
|
||||||
|
.remove(id.clone())
|
||||||
.inspect(|_| debug!("Removed element {{{}}}", &id.to_string()))
|
.inspect(|_| debug!("Removed element {{{}}}", &id.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_element(&self, id: ElementId) -> Option<Element> {
|
pub fn get_element(&self, id: ElementId) -> Option<Element> {
|
||||||
self.db().get_element(id)
|
self.db().elements().get(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_peer(&self, peer: Peer) -> anyhow::Result<()> {
|
pub fn set_peer(&self, peer: Peer) -> anyhow::Result<()> {
|
||||||
self.db()
|
self.db()
|
||||||
.add_peer(peer.clone())
|
.peers()
|
||||||
|
.add(peer.clone())
|
||||||
.inspect(|_| debug!("Added peer {:?}.", peer))
|
.inspect(|_| debug!("Added peer {:?}.", peer))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,7 +95,7 @@ impl CommState {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_pot(&self, id: PotId, app_type: String) -> anyhow::Result<()> {
|
pub fn add_pot(&self, id: PotId, app_type: String) -> anyhow::Result<()> {
|
||||||
self.db().add_pot(id.clone(), app_type.clone())?;
|
self.db().pots().add(id.clone(), app_type.clone())?;
|
||||||
|
|
||||||
let _ = self.state.emit_node_event(UbisyncNodeEvent::NewPot {
|
let _ = self.state.emit_node_event(UbisyncNodeEvent::NewPot {
|
||||||
id: id,
|
id: id,
|
||||||
|
@ -108,7 +112,7 @@ impl CommState {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_peer_from_family(&self, peer: PeerId) {
|
pub fn remove_peer_from_family(&self, peer: PeerId) {
|
||||||
let _ = self.db().remove_peer_from_family(peer);
|
let _ = self.db().families().remove_peer(peer);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_family_join_request(&self, peer: PeerId) -> bool {
|
pub fn has_family_join_request(&self, peer: PeerId) -> bool {
|
||||||
|
@ -116,11 +120,11 @@ impl CommState {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_own_family(&self, family: Family) -> anyhow::Result<()> {
|
pub fn set_own_family(&self, family: Family) -> anyhow::Result<()> {
|
||||||
self.db().add_peer_family(family)
|
self.db().families().add(family)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_family_of_peer(&self, peer: PeerId) -> anyhow::Result<Option<FamilyId>> {
|
pub fn get_family_of_peer(&self, peer: PeerId) -> anyhow::Result<Option<FamilyId>> {
|
||||||
self.db().get_family_of_peer(peer)
|
self.db().families().get_by_peer(peer)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn own_peer_id(&self) -> Option<PeerId> {
|
pub fn own_peer_id(&self) -> Option<PeerId> {
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
use anyhow::{anyhow, Error};
|
use anyhow::{anyhow, Error};
|
||||||
use bonsaidb::core::schema::{Collection, SerializedCollection};
|
use bonsaidb::{
|
||||||
|
core::schema::{Collection, SerializedCollection},
|
||||||
|
local::Database,
|
||||||
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_with::chrono::{DateTime, Utc};
|
use serde_with::chrono::{DateTime, Utc};
|
||||||
|
@ -35,8 +38,17 @@ impl From<DbApp> for App {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StateDB {
|
pub(crate) struct Apps<'a> {
|
||||||
pub fn add_app(
|
parent: &'a StateDB,
|
||||||
|
db: &'a Database,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Apps<'a> {
|
||||||
|
pub const fn new(parent: &'a StateDB, bonsai: &'a Database) -> Self {
|
||||||
|
Self { parent, db: bonsai }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(
|
||||||
&self,
|
&self,
|
||||||
id: AppId,
|
id: AppId,
|
||||||
name: String,
|
name: String,
|
||||||
|
@ -52,48 +64,50 @@ impl StateDB {
|
||||||
description,
|
description,
|
||||||
default_pot: None,
|
default_pot: None,
|
||||||
},
|
},
|
||||||
&self.db,
|
self.db,
|
||||||
)
|
)
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
.map_err(|e| anyhow!(e))
|
.map_err(|e| anyhow!(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_default_pot(&self, id: AppId, pot: PotId) -> anyhow::Result<()> {
|
pub fn set_default_pot(&self, id: AppId, pot: PotId) -> anyhow::Result<()> {
|
||||||
DbApp::get(&AsKey::new(id), &self.db)
|
DbApp::get(&AsKey::new(id), self.db)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.modify(&self.db, |app| app.contents.default_pot = Some(pot.clone()))
|
.modify(self.db, |app| app.contents.default_pot = Some(pot.clone()))
|
||||||
.map_err(|e| anyhow!(e))
|
.map_err(|e| anyhow!(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_default_pot(&self, id: AppId) -> anyhow::Result<Option<Pot>> {
|
pub fn get_default_pot(&self, id: AppId) -> anyhow::Result<Option<Pot>> {
|
||||||
let pot_id = DbApp::get(&AsKey::new(id), &self.db)?
|
let pot_id = DbApp::get(&AsKey::new(id), self.db)?
|
||||||
.map(|app| app.contents.default_pot)
|
.map(|app| app.contents.default_pot)
|
||||||
.ok_or(Error::msg("App not found"))?
|
.ok_or(Error::msg("App not found"))?
|
||||||
.ok_or(Error::msg("Could not get default pot"))?;
|
.ok_or(Error::msg("Could not get default pot"))?;
|
||||||
self.get_pot(pot_id)
|
self.parent.pots().get(pot_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_all_apps(&self) -> anyhow::Result<Vec<App>> {
|
pub fn get_all(&self) -> anyhow::Result<Vec<App>> {
|
||||||
Ok(DbApp::all(&self.db)
|
Ok(DbApp::all(self.db)
|
||||||
.query()?
|
.query()?
|
||||||
.iter()
|
.iter()
|
||||||
.map(|app| app.contents.clone().into())
|
.map(|app| app.contents.clone().into())
|
||||||
.collect_vec())
|
.collect_vec())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_all_app_ids(&self) -> anyhow::Result<Vec<AppId>> {
|
pub fn get_all_ids(&self) -> anyhow::Result<Vec<AppId>> {
|
||||||
Ok(DbApp::all(&self.db)
|
Ok(DbApp::all(self.db)
|
||||||
.query()?
|
.query()?
|
||||||
.iter()
|
.iter()
|
||||||
.map(|app| (*app.contents.id).clone())
|
.map(|app| (*app.contents.id).clone())
|
||||||
.collect_vec())
|
.collect_vec())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn app_has_access(&self, app: AppId, element: ElementId) -> anyhow::Result<bool> {
|
pub fn has_access(&self, app: AppId, element: ElementId) -> anyhow::Result<bool> {
|
||||||
if let Some(el) = self.get_element(element) {
|
if let Some(el) = self.parent.elements().get(element) {
|
||||||
Ok(self
|
Ok(self
|
||||||
.get_pot_members(
|
.parent
|
||||||
|
.pot_memberships()
|
||||||
|
.get_members(
|
||||||
el.pot()
|
el.pot()
|
||||||
.clone()
|
.clone()
|
||||||
.ok_or(Error::msg("Could not fetch pot members"))?,
|
.ok_or(Error::msg("Could not fetch pot members"))?,
|
||||||
|
@ -104,8 +118,8 @@ impl StateDB {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_app(&self, id: AppId) -> anyhow::Result<Option<App>> {
|
pub fn get(&self, id: AppId) -> anyhow::Result<Option<App>> {
|
||||||
DbApp::get(&AsKey::new(id), &self.db)
|
DbApp::get(&AsKey::new(id), self.db)
|
||||||
.map(|app_option| app_option.map(|app| app.contents.into()))
|
.map(|app_option| app_option.map(|app| app.contents.into()))
|
||||||
.map_err(|e| anyhow!(e))
|
.map_err(|e| anyhow!(e))
|
||||||
}
|
}
|
||||||
|
@ -113,7 +127,7 @@ impl StateDB {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use ubisync_lib::types::{AppId, ElementContent, ElementId, ContentUpdateStrategy, Pot, PotId};
|
use ubisync_lib::types::{AppId, ContentUpdateStrategy, ElementContent, ElementId, Pot, PotId};
|
||||||
|
|
||||||
use crate::{api::v0::app::App, state::database::StateDB};
|
use crate::{api::v0::app::App, state::database::StateDB};
|
||||||
|
|
||||||
|
@ -121,15 +135,16 @@ mod tests {
|
||||||
fn add_get() {
|
fn add_get() {
|
||||||
let db = StateDB::init(None);
|
let db = StateDB::init(None);
|
||||||
let app_id = AppId::new();
|
let app_id = AppId::new();
|
||||||
db.add_app(
|
db.apps()
|
||||||
app_id.clone(),
|
.add(
|
||||||
"app name".to_string(),
|
app_id.clone(),
|
||||||
"description".to_string(),
|
"app name".to_string(),
|
||||||
"app_type".to_string(),
|
"description".to_string(),
|
||||||
)
|
"app_type".to_string(),
|
||||||
.unwrap();
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let retrieved_app = db.get_app(app_id.clone()).unwrap();
|
let retrieved_app = db.apps().get(app_id.clone()).unwrap();
|
||||||
|
|
||||||
match retrieved_app {
|
match retrieved_app {
|
||||||
Some(App {
|
Some(App {
|
||||||
|
@ -159,85 +174,93 @@ mod tests {
|
||||||
fn get_default_pot() {
|
fn get_default_pot() {
|
||||||
let db = StateDB::init(None);
|
let db = StateDB::init(None);
|
||||||
let app_id = AppId::new();
|
let app_id = AppId::new();
|
||||||
db.add_app(
|
db.apps()
|
||||||
app_id.clone(),
|
.add(
|
||||||
"app name".to_string(),
|
app_id.clone(),
|
||||||
"description".to_string(),
|
"app name".to_string(),
|
||||||
"app_type".to_string(),
|
"description".to_string(),
|
||||||
)
|
"app_type".to_string(),
|
||||||
.unwrap();
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(db.get_default_pot(app_id).unwrap(), None)
|
assert_eq!(db.apps().get_default_pot(app_id).unwrap(), None)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn set_default_pot() {
|
fn set_default_pot() {
|
||||||
let db = StateDB::init(None);
|
let db = StateDB::init(None);
|
||||||
let app_id = AppId::new();
|
let app_id = AppId::new();
|
||||||
db.add_app(
|
db.apps()
|
||||||
app_id.clone(),
|
.add(
|
||||||
"app name".to_string(),
|
app_id.clone(),
|
||||||
"description".to_string(),
|
"app name".to_string(),
|
||||||
"app_type".to_string(),
|
"description".to_string(),
|
||||||
)
|
"app_type".to_string(),
|
||||||
.unwrap();
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let pot = Pot::new(PotId::new(), "app_type".to_string());
|
let pot = Pot::new(PotId::new(), "app_type".to_string());
|
||||||
db.add_pot(pot.id.clone(), pot.app_type.clone()).unwrap();
|
db.pots().add(pot.id.clone(), pot.app_type.clone()).unwrap();
|
||||||
db.set_default_pot(app_id.clone(), pot.id.clone()).unwrap();
|
db.apps()
|
||||||
|
.set_default_pot(app_id.clone(), pot.id.clone())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(db.get_default_pot(app_id).unwrap(), Some(pot))
|
assert_eq!(db.apps().get_default_pot(app_id).unwrap(), Some(pot))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn get_apps() {
|
fn get_apps() {
|
||||||
let db = StateDB::init(None);
|
let db = StateDB::init(None);
|
||||||
|
|
||||||
assert_eq!(db.get_all_apps().unwrap(), vec![]);
|
assert_eq!(db.apps().get_all().unwrap(), vec![]);
|
||||||
|
|
||||||
let (app1, app2) = (AppId::new(), AppId::new());
|
let (app1, app2) = (AppId::new(), AppId::new());
|
||||||
|
|
||||||
db.add_app(
|
db.apps()
|
||||||
app1,
|
.add(
|
||||||
"name1".to_string(),
|
app1,
|
||||||
"desc1".to_string(),
|
"name1".to_string(),
|
||||||
"type1".to_string(),
|
"desc1".to_string(),
|
||||||
)
|
"type1".to_string(),
|
||||||
.unwrap();
|
)
|
||||||
db.add_app(
|
.unwrap();
|
||||||
app2,
|
db.apps()
|
||||||
"name2".to_string(),
|
.add(
|
||||||
"desc2".to_string(),
|
app2,
|
||||||
"type2".to_string(),
|
"name2".to_string(),
|
||||||
)
|
"desc2".to_string(),
|
||||||
.unwrap();
|
"type2".to_string(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(db.get_all_apps().unwrap().len(), 2);
|
assert_eq!(db.apps().get_all().unwrap().len(), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn get_app_ids() {
|
fn get_app_ids() {
|
||||||
let db = StateDB::init(None);
|
let db = StateDB::init(None);
|
||||||
assert_eq!(db.get_all_app_ids().unwrap(), vec![]);
|
assert_eq!(db.apps().get_all_ids().unwrap(), vec![]);
|
||||||
|
|
||||||
let (app1, app2) = (AppId::new(), AppId::new());
|
let (app1, app2) = (AppId::new(), AppId::new());
|
||||||
|
|
||||||
db.add_app(
|
db.apps()
|
||||||
app1.clone(),
|
.add(
|
||||||
"name1".to_string(),
|
app1.clone(),
|
||||||
"desc1".to_string(),
|
"name1".to_string(),
|
||||||
"type1".to_string(),
|
"desc1".to_string(),
|
||||||
)
|
"type1".to_string(),
|
||||||
.unwrap();
|
)
|
||||||
db.add_app(
|
.unwrap();
|
||||||
app2.clone(),
|
db.apps()
|
||||||
"name2".to_string(),
|
.add(
|
||||||
"desc2".to_string(),
|
app2.clone(),
|
||||||
"type2".to_string(),
|
"name2".to_string(),
|
||||||
)
|
"desc2".to_string(),
|
||||||
.unwrap();
|
"type2".to_string(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(db.get_all_app_ids().unwrap(), vec![app1, app2])
|
assert_eq!(db.apps().get_all_ids().unwrap(), vec![app1, app2])
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -247,32 +270,37 @@ mod tests {
|
||||||
let pot_id = PotId::new();
|
let pot_id = PotId::new();
|
||||||
let element_id = ElementId::new();
|
let element_id = ElementId::new();
|
||||||
|
|
||||||
db.add_app(
|
db.apps()
|
||||||
app_id.clone(),
|
.add(
|
||||||
"name".to_string(),
|
app_id.clone(),
|
||||||
"description".to_string(),
|
"name".to_string(),
|
||||||
"app_type".to_string(),
|
"description".to_string(),
|
||||||
)
|
"app_type".to_string(),
|
||||||
.unwrap();
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
db.add_pot(pot_id.clone(), "app_type".to_string()).unwrap();
|
db.pots()
|
||||||
db.add_element(
|
.add(pot_id.clone(), "app_type".to_string())
|
||||||
element_id.clone(),
|
.unwrap();
|
||||||
ElementContent::Text("Text".to_string()),
|
db.elements()
|
||||||
ContentUpdateStrategy::Overwrite,
|
.add(
|
||||||
None,
|
element_id.clone(),
|
||||||
false,
|
ElementContent::Text("Text".to_string()),
|
||||||
pot_id.clone(),
|
ContentUpdateStrategy::Overwrite,
|
||||||
)
|
None,
|
||||||
.unwrap();
|
false,
|
||||||
|
pot_id.clone(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
db.app_has_access(app_id.clone(), element_id.clone())
|
db.apps()
|
||||||
|
.has_access(app_id.clone(), element_id.clone())
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
db.add_pot_membership(pot_id, app_id.clone()).unwrap();
|
db.pot_memberships().add(pot_id, app_id.clone()).unwrap();
|
||||||
assert_eq!(db.app_has_access(app_id, element_id).unwrap(), true);
|
assert_eq!(db.apps().has_access(app_id, element_id).unwrap(), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
use anyhow::{anyhow, Error};
|
use anyhow::{anyhow, Error};
|
||||||
use bonsaidb::core::schema::{Collection, SerializedCollection};
|
use bonsaidb::{
|
||||||
|
core::schema::{Collection, SerializedCollection},
|
||||||
|
local::Database,
|
||||||
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use ubisync_lib::types::{Element, ElementContent, ElementId, ContentUpdateStrategy, MessageId, PotId};
|
use ubisync_lib::types::{
|
||||||
|
ContentUpdateStrategy, Element, ElementContent, ElementId, MessageId, PotId,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::state::database::{as_key::AsKey, StateDB};
|
use crate::state::database::{as_key::AsKey, StateDB};
|
||||||
|
|
||||||
|
@ -30,8 +35,16 @@ impl From<DbElement> for Element {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StateDB {
|
pub(crate) struct Elements<'a> {
|
||||||
pub fn add_element(
|
parent: &'a StateDB,
|
||||||
|
db: &'a Database,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Elements<'a> {
|
||||||
|
pub const fn new(parent: &'a StateDB, bonsai: &'a Database) -> Self {
|
||||||
|
Self { parent, db: bonsai }
|
||||||
|
}
|
||||||
|
pub fn add(
|
||||||
&self,
|
&self,
|
||||||
id: ElementId,
|
id: ElementId,
|
||||||
content: ElementContent,
|
content: ElementContent,
|
||||||
|
@ -49,66 +62,60 @@ impl StateDB {
|
||||||
local_changes,
|
local_changes,
|
||||||
pot,
|
pot,
|
||||||
},
|
},
|
||||||
&self.db,
|
self.db,
|
||||||
)
|
)
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
.map_err(|e| anyhow!(e))
|
.map_err(|e| anyhow!(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_element(&self, id: ElementId) -> Option<Element> {
|
pub fn get(&self, id: ElementId) -> Option<Element> {
|
||||||
DbElement::get(&AsKey::new(id), &self.db)
|
DbElement::get(&AsKey::new(id), self.db)
|
||||||
.ok()?
|
.ok()?
|
||||||
.map(|el| el.contents.into())
|
.map(|el| el.contents.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_element_content(
|
pub fn set_content(&self, id: ElementId, content: ElementContent) -> anyhow::Result<()> {
|
||||||
&self,
|
DbElement::get(&AsKey::new(id), self.db)
|
||||||
id: ElementId,
|
|
||||||
content: ElementContent,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
DbElement::get(&AsKey::new(id), &self.db)
|
|
||||||
.map_err(|e| anyhow!(e))?
|
.map_err(|e| anyhow!(e))?
|
||||||
.ok_or(Error::msg("Could not find element by id"))?
|
.ok_or(Error::msg("Could not find element by id"))?
|
||||||
.modify(&self.db, |t| t.contents.content = content.clone())
|
.modify(self.db, |t| t.contents.content = content.clone())
|
||||||
.map_err(|e| anyhow!(e))
|
.map_err(|e| anyhow!(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_element_latest_message(
|
pub fn set_latest_message(
|
||||||
&self,
|
&self,
|
||||||
id: ElementId,
|
id: ElementId,
|
||||||
message: Option<MessageId>,
|
message: Option<MessageId>,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
DbElement::get(&AsKey::new(id), &self.db)
|
DbElement::get(&AsKey::new(id), self.db)
|
||||||
.map_err(|e| anyhow!(e))?
|
.map_err(|e| anyhow!(e))?
|
||||||
.ok_or(Error::msg("Could not find element by id"))?
|
.ok_or(Error::msg("Could not find element by id"))?
|
||||||
.modify(&self.db, |t| t.contents.latest_message = message.clone())
|
.modify(self.db, |t| t.contents.latest_message = message.clone())
|
||||||
.map_err(|e| anyhow!(e))
|
.map_err(|e| anyhow!(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_element_local_changes(
|
pub fn set_local_changes(&self, id: ElementId, local_changes: bool) -> anyhow::Result<()> {
|
||||||
&self,
|
DbElement::get(&AsKey::new(id), self.db)
|
||||||
id: ElementId,
|
|
||||||
local_changes: bool,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
DbElement::get(&AsKey::new(id), &self.db)
|
|
||||||
.map_err(|e| anyhow!(e))?
|
.map_err(|e| anyhow!(e))?
|
||||||
.ok_or(Error::msg("Could not find element by id"))?
|
.ok_or(Error::msg("Could not find element by id"))?
|
||||||
.modify(&self.db, |t| t.contents.local_changes = local_changes)
|
.modify(self.db, |t| t.contents.local_changes = local_changes)
|
||||||
.map_err(|e| anyhow!(e))
|
.map_err(|e| anyhow!(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_element(&self, id: ElementId) -> anyhow::Result<()> {
|
pub fn remove(&self, id: ElementId) -> anyhow::Result<()> {
|
||||||
DbElement::get(&AsKey::new(id), &self.db)
|
DbElement::get(&AsKey::new(id), self.db)
|
||||||
.map_err(|e| anyhow!(e))?
|
.map_err(|e| anyhow!(e))?
|
||||||
.ok_or(Error::msg("Could not find element by id"))?
|
.ok_or(Error::msg("Could not find element by id"))?
|
||||||
.delete(&self.db)
|
.delete(self.db)
|
||||||
.map_err(|e| anyhow!(e))
|
.map_err(|e| anyhow!(e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use ubisync_lib::types::{Element, ElementContent, ElementId, ContentUpdateStrategy, MessageId, PotId};
|
use ubisync_lib::types::{
|
||||||
|
ContentUpdateStrategy, Element, ElementContent, ElementId, MessageId, PotId,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::state::database::StateDB;
|
use crate::state::database::StateDB;
|
||||||
|
|
||||||
|
@ -117,29 +124,28 @@ mod tests {
|
||||||
let db = StateDB::init(None);
|
let db = StateDB::init(None);
|
||||||
let pot_id = PotId::new();
|
let pot_id = PotId::new();
|
||||||
let element_id = ElementId::new();
|
let element_id = ElementId::new();
|
||||||
db.add_element(
|
db.elements()
|
||||||
element_id.clone(),
|
.add(
|
||||||
ElementContent::Text("Content!!!".to_string()),
|
element_id.clone(),
|
||||||
ContentUpdateStrategy::default(),
|
ElementContent::Text("Content!!!".to_string()),
|
||||||
None,
|
ContentUpdateStrategy::default(),
|
||||||
false,
|
None,
|
||||||
pot_id.clone(),
|
false,
|
||||||
)
|
pot_id.clone(),
|
||||||
.unwrap();
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let retrieved_element = db.get_element(element_id.clone());
|
let retrieved_element = db.elements().get(element_id.clone());
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(
|
Some(Element {
|
||||||
Element {
|
id: element_id,
|
||||||
id: element_id,
|
content: ElementContent::Text("Content!!!".to_string()),
|
||||||
content: ElementContent::Text("Content!!!".to_string()),
|
update_strategy: ContentUpdateStrategy::default(),
|
||||||
update_strategy: ContentUpdateStrategy::default(),
|
latest_message: None,
|
||||||
latest_message: None,
|
local_changes: false,
|
||||||
local_changes: false,
|
pot: Some(pot_id),
|
||||||
pot: Some(pot_id),
|
}),
|
||||||
}
|
|
||||||
),
|
|
||||||
retrieved_element
|
retrieved_element
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -148,23 +154,25 @@ mod tests {
|
||||||
fn set_content() {
|
fn set_content() {
|
||||||
let db = StateDB::init(None);
|
let db = StateDB::init(None);
|
||||||
let element_id = ElementId::new();
|
let element_id = ElementId::new();
|
||||||
db.add_element(
|
db.elements()
|
||||||
element_id.clone(),
|
.add(
|
||||||
ElementContent::Text("Content!!!".to_string()),
|
element_id.clone(),
|
||||||
ContentUpdateStrategy::default(),
|
ElementContent::Text("Content!!!".to_string()),
|
||||||
None,
|
ContentUpdateStrategy::default(),
|
||||||
false,
|
None,
|
||||||
PotId::new(),
|
false,
|
||||||
)
|
PotId::new(),
|
||||||
.unwrap();
|
)
|
||||||
db.set_element_content(
|
.unwrap();
|
||||||
element_id.clone(),
|
db.elements()
|
||||||
ElementContent::Text("New Content!!!".to_string()),
|
.set_content(
|
||||||
)
|
element_id.clone(),
|
||||||
.unwrap();
|
ElementContent::Text("New Content!!!".to_string()),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
db.get_element(element_id).unwrap().content().to_owned(),
|
db.elements().get(element_id).unwrap().content().to_owned(),
|
||||||
ElementContent::Text("New Content!!!".to_string())
|
ElementContent::Text("New Content!!!".to_string())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -174,18 +182,20 @@ mod tests {
|
||||||
let db = StateDB::init(None);
|
let db = StateDB::init(None);
|
||||||
|
|
||||||
let element_id = ElementId::new();
|
let element_id = ElementId::new();
|
||||||
db.add_element(
|
db.elements()
|
||||||
element_id.clone(),
|
.add(
|
||||||
ElementContent::Text("Content!!!".to_string()),
|
element_id.clone(),
|
||||||
ContentUpdateStrategy::default(),
|
ElementContent::Text("Content!!!".to_string()),
|
||||||
None,
|
ContentUpdateStrategy::default(),
|
||||||
false,
|
None,
|
||||||
PotId::new(),
|
false,
|
||||||
)
|
PotId::new(),
|
||||||
.unwrap();
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
db.get_element(element_id.clone())
|
db.elements()
|
||||||
|
.get(element_id.clone())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.latest_message()
|
.latest_message()
|
||||||
.to_owned(),
|
.to_owned(),
|
||||||
|
@ -193,11 +203,13 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
let msg_id = MessageId::new();
|
let msg_id = MessageId::new();
|
||||||
db.set_element_latest_message(element_id.clone(), Some(msg_id.clone()))
|
db.elements()
|
||||||
|
.set_latest_message(element_id.clone(), Some(msg_id.clone()))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
db.get_element(element_id)
|
db.elements()
|
||||||
|
.get(element_id)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.latest_message()
|
.latest_message()
|
||||||
.to_owned(),
|
.to_owned(),
|
||||||
|
@ -210,31 +222,29 @@ mod tests {
|
||||||
let db = StateDB::init(None);
|
let db = StateDB::init(None);
|
||||||
|
|
||||||
let element_id = ElementId::new();
|
let element_id = ElementId::new();
|
||||||
db.add_element(
|
db.elements()
|
||||||
element_id.clone(),
|
.add(
|
||||||
ElementContent::Text("Content!!!".to_string()),
|
element_id.clone(),
|
||||||
ContentUpdateStrategy::default(),
|
ElementContent::Text("Content!!!".to_string()),
|
||||||
None,
|
ContentUpdateStrategy::default(),
|
||||||
false,
|
None,
|
||||||
PotId::new(),
|
false,
|
||||||
)
|
PotId::new(),
|
||||||
.unwrap();
|
)
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
db.get_element(element_id.clone())
|
|
||||||
.unwrap().local_changes(),
|
|
||||||
false
|
|
||||||
);
|
|
||||||
|
|
||||||
db.set_element_local_changes(element_id.clone(), true)
|
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
db.get_element(element_id)
|
db.elements()
|
||||||
|
.get(element_id.clone())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.local_changes(),
|
.local_changes(),
|
||||||
true
|
false
|
||||||
)
|
);
|
||||||
|
|
||||||
|
db.elements()
|
||||||
|
.set_local_changes(element_id.clone(), true)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(db.elements().get(element_id).unwrap().local_changes(), true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,13 @@ use peers::DbPeer;
|
||||||
use pot_memberships::DbPotMembership;
|
use pot_memberships::DbPotMembership;
|
||||||
use pots::DbPot;
|
use pots::DbPot;
|
||||||
|
|
||||||
|
pub(crate) use apps::Apps;
|
||||||
|
pub(crate) use elements::Elements;
|
||||||
|
pub(crate) use peer_families::Families;
|
||||||
|
pub(crate) use peers::Peers;
|
||||||
|
pub(crate) use pot_memberships::PotMemberships;
|
||||||
|
pub(crate) use pots::Pots;
|
||||||
|
|
||||||
mod apps;
|
mod apps;
|
||||||
mod elements;
|
mod elements;
|
||||||
mod peer_families;
|
mod peer_families;
|
||||||
|
@ -20,4 +27,3 @@ pub struct UbisyncSchema;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {}
|
mod tests {}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,15 @@
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use anyhow::{anyhow, Error};
|
use anyhow::{anyhow, Error};
|
||||||
use bonsaidb::core::{
|
use bonsaidb::{
|
||||||
connection::Connection,
|
core::{
|
||||||
document::Emit,
|
connection::Connection,
|
||||||
schema::{view::map::Mappings, Collection, MapReduce, SerializedCollection, View, ViewSchema},
|
document::Emit,
|
||||||
|
schema::{
|
||||||
|
view::map::Mappings, Collection, MapReduce, SerializedCollection, View, ViewSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
local::Database,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -85,9 +90,17 @@ impl From<DbPeerFamily> for Family {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StateDB {
|
pub(crate) struct Families<'a> {
|
||||||
pub fn add_peer_family(&self, family: Family) -> anyhow::Result<()> {
|
parent: &'a StateDB,
|
||||||
if self.get_peer_family(family.id.clone())?.is_some() {
|
db: &'a Database,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Families<'a> {
|
||||||
|
pub const fn new(parent: &'a StateDB, bonsai: &'a Database) -> Self {
|
||||||
|
Self { parent, db: bonsai }
|
||||||
|
}
|
||||||
|
pub fn add(&self, family: Family) -> anyhow::Result<()> {
|
||||||
|
if self.get(family.id.clone())?.is_some() {
|
||||||
Err(Error::msg("Peer family already exists"))
|
Err(Error::msg("Peer family already exists"))
|
||||||
} else {
|
} else {
|
||||||
DbPeerFamily::push(
|
DbPeerFamily::push(
|
||||||
|
@ -96,44 +109,40 @@ impl StateDB {
|
||||||
name: family.name,
|
name: family.name,
|
||||||
members: HashSet::from_iter(family.members),
|
members: HashSet::from_iter(family.members),
|
||||||
},
|
},
|
||||||
&self.db,
|
self.db,
|
||||||
)
|
)
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
.map_err(|e| anyhow!(e))
|
.map_err(|e| anyhow!(e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_peer_to_family(&self, peer: PeerId, family: FamilyId) -> anyhow::Result<()> {
|
pub fn add_peer(&self, peer: PeerId, family: FamilyId) -> anyhow::Result<()> {
|
||||||
DbPeerFamily::get(&AsKey::new(family), &self.db)
|
DbPeerFamily::get(&AsKey::new(family), self.db)
|
||||||
.map_err(|e| anyhow!(e))?
|
.map_err(|e| anyhow!(e))?
|
||||||
.ok_or(Error::msg("Could not find peer family"))?
|
.ok_or(Error::msg("Could not find peer family"))?
|
||||||
.modify(&self.db, |doc| {
|
.modify(self.db, |doc| {
|
||||||
doc.contents.members.insert(peer.clone());
|
doc.contents.members.insert(peer.clone());
|
||||||
})
|
})
|
||||||
.map_err(|e| anyhow!(e))
|
.map_err(|e| anyhow!(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_peer_family_name(
|
pub fn set_name(&self, family: FamilyId, name: Option<String>) -> anyhow::Result<()> {
|
||||||
&self,
|
DbPeerFamily::get(&AsKey::new(family), self.db)
|
||||||
family: FamilyId,
|
|
||||||
name: Option<String>,
|
|
||||||
) -> anyhow::Result<()> {
|
|
||||||
DbPeerFamily::get(&AsKey::new(family), &self.db)
|
|
||||||
.map_err(|e| anyhow!(e))?
|
.map_err(|e| anyhow!(e))?
|
||||||
.ok_or(Error::msg("Could not find peer family"))?
|
.ok_or(Error::msg("Could not find peer family"))?
|
||||||
.modify(&self.db, |doc| {
|
.modify(self.db, |doc| {
|
||||||
doc.contents.name = name.clone();
|
doc.contents.name = name.clone();
|
||||||
})
|
})
|
||||||
.map_err(|e| anyhow!(e))
|
.map_err(|e| anyhow!(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_peer_family(&self, id: FamilyId) -> anyhow::Result<Option<Family>> {
|
pub fn get(&self, id: FamilyId) -> anyhow::Result<Option<Family>> {
|
||||||
DbPeerFamily::get(&AsKey::new(id), &self.db)
|
DbPeerFamily::get(&AsKey::new(id), self.db)
|
||||||
.map(|doc_opt| doc_opt.map(|doc| doc.contents.into()))
|
.map(|doc_opt| doc_opt.map(|doc| doc.contents.into()))
|
||||||
.map_err(|e| anyhow!(e))
|
.map_err(|e| anyhow!(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_family_of_peer(&self, peer: PeerId) -> anyhow::Result<Option<FamilyId>> {
|
pub fn get_by_peer(&self, peer: PeerId) -> anyhow::Result<Option<FamilyId>> {
|
||||||
self.db
|
self.db
|
||||||
.view::<DbPeerFamilyIdByMemberId>()
|
.view::<DbPeerFamilyIdByMemberId>()
|
||||||
.with_key(&AsKey::new(peer))
|
.with_key(&AsKey::new(peer))
|
||||||
|
@ -146,7 +155,7 @@ impl StateDB {
|
||||||
.map_err(|e| anyhow!(e))
|
.map_err(|e| anyhow!(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_peer_family_members(&self, peer: PeerId) -> anyhow::Result<Vec<PeerId>> {
|
pub fn get_members(&self, peer: PeerId) -> anyhow::Result<Vec<PeerId>> {
|
||||||
self.db
|
self.db
|
||||||
.view::<DbPeerFamilyByMemberId>()
|
.view::<DbPeerFamilyByMemberId>()
|
||||||
.with_key(&AsKey::new(peer))
|
.with_key(&AsKey::new(peer))
|
||||||
|
@ -160,20 +169,21 @@ impl StateDB {
|
||||||
.value
|
.value
|
||||||
.clone()
|
.clone()
|
||||||
.map(|family| family.members)
|
.map(|family| family.members)
|
||||||
.map(|map| map.into_iter().collect_vec()).unwrap_or(vec![])),
|
.map(|map| map.into_iter().collect_vec())
|
||||||
|
.unwrap_or(vec![])),
|
||||||
_ => Err(Error::msg("Peer appears to be member of multiple families")),
|
_ => Err(Error::msg("Peer appears to be member of multiple families")),
|
||||||
})?
|
})?
|
||||||
.map_err(|e| anyhow!(e))
|
.map_err(|e| anyhow!(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_peer_from_family(&self, peer: PeerId) -> anyhow::Result<()> {
|
pub fn remove_peer(&self, peer: PeerId) -> anyhow::Result<()> {
|
||||||
self.db
|
self.db
|
||||||
.view::<DbPeerFamilyIdByMemberId>()
|
.view::<DbPeerFamilyIdByMemberId>()
|
||||||
.with_key(&AsKey::new(peer.clone()))
|
.with_key(&AsKey::new(peer.clone()))
|
||||||
.query_with_collection_docs()
|
.query_with_collection_docs()
|
||||||
.map(|results| {
|
.map(|results| {
|
||||||
if let Some(family) = results.into_iter().next() {
|
if let Some(family) = results.into_iter().next() {
|
||||||
family.document.to_owned().modify(&self.db, |doc| {
|
family.document.to_owned().modify(self.db, |doc| {
|
||||||
doc.contents.members.remove(&peer.clone());
|
doc.contents.members.remove(&peer.clone());
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
@ -199,13 +209,14 @@ mod tests {
|
||||||
let family_id = FamilyId::new();
|
let family_id = FamilyId::new();
|
||||||
let peer_id = PeerId::default();
|
let peer_id = PeerId::default();
|
||||||
|
|
||||||
db.add_peer_family(Family {
|
db.families()
|
||||||
id: family_id.clone(),
|
.add(Family {
|
||||||
name: Some("My family name".to_string()),
|
id: family_id.clone(),
|
||||||
members: HashSet::from([peer_id.clone()]),
|
name: Some("My family name".to_string()),
|
||||||
})
|
members: HashSet::from([peer_id.clone()]),
|
||||||
.unwrap();
|
})
|
||||||
let retrieved_family = db.get_peer_family(family_id.clone()).unwrap();
|
.unwrap();
|
||||||
|
let retrieved_family = db.families().get(family_id.clone()).unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
retrieved_family,
|
retrieved_family,
|
||||||
|
@ -223,14 +234,15 @@ mod tests {
|
||||||
let family_id = FamilyId::new();
|
let family_id = FamilyId::new();
|
||||||
let peer_id = PeerId::default();
|
let peer_id = PeerId::default();
|
||||||
|
|
||||||
db.add_peer_family(Family {
|
db.families()
|
||||||
id: family_id.clone(),
|
.add(Family {
|
||||||
name: Some("My family name".to_string()),
|
id: family_id.clone(),
|
||||||
members: HashSet::from([peer_id.clone()]),
|
name: Some("My family name".to_string()),
|
||||||
})
|
members: HashSet::from([peer_id.clone()]),
|
||||||
.unwrap();
|
})
|
||||||
db.remove_peer_from_family(peer_id.clone()).unwrap();
|
.unwrap();
|
||||||
let retrieved_family = db.get_peer_family(family_id.clone()).unwrap();
|
db.families().remove_peer(peer_id.clone()).unwrap();
|
||||||
|
let retrieved_family = db.families().get(family_id.clone()).unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
retrieved_family,
|
retrieved_family,
|
||||||
|
@ -248,15 +260,16 @@ mod tests {
|
||||||
let family_id = FamilyId::new();
|
let family_id = FamilyId::new();
|
||||||
let peer_id = PeerId::default();
|
let peer_id = PeerId::default();
|
||||||
|
|
||||||
db.add_peer_family(Family {
|
db.families()
|
||||||
id: family_id.clone(),
|
.add(Family {
|
||||||
name: Some("My family name".to_string()),
|
id: family_id.clone(),
|
||||||
members: HashSet::from([peer_id.clone()]),
|
name: Some("My family name".to_string()),
|
||||||
})
|
members: HashSet::from([peer_id.clone()]),
|
||||||
.unwrap();
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
db.get_peer_family(family_id.clone()).unwrap(),
|
db.families().get(family_id.clone()).unwrap(),
|
||||||
Some(Family::new(
|
Some(Family::new(
|
||||||
family_id.clone(),
|
family_id.clone(),
|
||||||
Some("My family name".to_string()),
|
Some("My family name".to_string()),
|
||||||
|
@ -264,11 +277,12 @@ mod tests {
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
db.set_peer_family_name(family_id.clone(), Some("New family name".to_string()))
|
db.families()
|
||||||
|
.set_name(family_id.clone(), Some("New family name".to_string()))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
db.get_peer_family(family_id.clone()).unwrap(),
|
db.families().get(family_id.clone()).unwrap(),
|
||||||
Some(Family::new(
|
Some(Family::new(
|
||||||
family_id,
|
family_id,
|
||||||
Some("New family name".to_string()),
|
Some("New family name".to_string()),
|
||||||
|
@ -278,18 +292,19 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn get_family_of_peer() {
|
fn get_by_peer() {
|
||||||
let db = StateDB::init(None);
|
let db = StateDB::init(None);
|
||||||
let family_id = FamilyId::new();
|
let family_id = FamilyId::new();
|
||||||
let peer_id = PeerId::default();
|
let peer_id = PeerId::default();
|
||||||
|
|
||||||
db.add_peer_family(Family {
|
db.families()
|
||||||
id: family_id.clone(),
|
.add(Family {
|
||||||
name: Some("My family name".to_string()),
|
id: family_id.clone(),
|
||||||
members: HashSet::from([peer_id.clone()]),
|
name: Some("My family name".to_string()),
|
||||||
})
|
members: HashSet::from([peer_id.clone()]),
|
||||||
.unwrap();
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
assert_eq!(db.get_family_of_peer(peer_id).unwrap(), Some(family_id))
|
assert_eq!(db.families().get_by_peer(peer_id).unwrap(), Some(family_id))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,14 @@
|
||||||
use anyhow::{anyhow, Error};
|
use anyhow::{anyhow, Error};
|
||||||
use bonsaidb::core::schema::{Collection, SerializedCollection};
|
use bonsaidb::{
|
||||||
|
core::schema::{Collection, SerializedCollection},
|
||||||
|
local::Database,
|
||||||
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use ubisync_lib::{
|
use ubisync_lib::{peer::Peer, types::PeerId};
|
||||||
peer::Peer,
|
|
||||||
types::{ElementId, PeerId},
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::state::database::{as_key::AsKey, StateDB};
|
use crate::state::database::{as_key::AsKey, StateDB};
|
||||||
|
|
||||||
use super::peer_families::DbPeerFamily;
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Collection, PartialEq, Clone)]
|
#[derive(Debug, Serialize, Deserialize, Collection, PartialEq, Clone)]
|
||||||
#[collection(name = "peers", views = [])]
|
#[collection(name = "peers", views = [])]
|
||||||
pub(super) struct DbPeer {
|
pub(super) struct DbPeer {
|
||||||
|
@ -25,9 +23,18 @@ impl From<DbPeer> for Peer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StateDB {
|
pub(crate) struct Peers<'a> {
|
||||||
pub fn add_peer(&self, peer: Peer) -> anyhow::Result<()> {
|
parent: &'a StateDB,
|
||||||
if self.get_peer(peer.id())?.is_some() {
|
db: &'a Database,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Peers<'a> {
|
||||||
|
pub const fn new(parent: &'a StateDB, bonsai: &'a Database) -> Self {
|
||||||
|
Self { parent, db: bonsai }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add(&self, peer: Peer) -> anyhow::Result<()> {
|
||||||
|
if self.get(peer.id())?.is_some() {
|
||||||
Err(Error::msg("Peer already exists"))
|
Err(Error::msg("Peer already exists"))
|
||||||
} else {
|
} else {
|
||||||
DbPeer::push(
|
DbPeer::push(
|
||||||
|
@ -35,21 +42,21 @@ impl StateDB {
|
||||||
id: AsKey::new(peer.id()),
|
id: AsKey::new(peer.id()),
|
||||||
name: peer.name(),
|
name: peer.name(),
|
||||||
},
|
},
|
||||||
&self.db,
|
self.db,
|
||||||
)
|
)
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
.map_err(|e| anyhow!(e))
|
.map_err(|e| anyhow!(e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_peer(&self, id: PeerId) -> anyhow::Result<Option<Peer>> {
|
pub fn get(&self, id: PeerId) -> anyhow::Result<Option<Peer>> {
|
||||||
DbPeer::get(&AsKey::new(id), &self.db)
|
DbPeer::get(&AsKey::new(id), self.db)
|
||||||
.map(|doc| doc.map(|peer| Peer::new((*peer.contents.id).clone(), peer.contents.name)))
|
.map(|doc| doc.map(|peer| Peer::new((*peer.contents.id).clone(), peer.contents.name)))
|
||||||
.map_err(|e| anyhow!(e))
|
.map_err(|e| anyhow!(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_all_peers(&self) -> anyhow::Result<Vec<Peer>> {
|
pub fn get_all(&self) -> anyhow::Result<Vec<Peer>> {
|
||||||
DbPeer::all(&self.db)
|
DbPeer::all(self.db)
|
||||||
.query()
|
.query()
|
||||||
.map(|peers| {
|
.map(|peers| {
|
||||||
peers
|
peers
|
||||||
|
@ -60,11 +67,11 @@ impl StateDB {
|
||||||
.map_err(|e| anyhow!(e))
|
.map_err(|e| anyhow!(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_peer_name(&self, id: PeerId, name: &Option<String>) -> anyhow::Result<()> {
|
pub fn set_name(&self, id: PeerId, name: &Option<String>) -> anyhow::Result<()> {
|
||||||
DbPeer::get(&AsKey::new(id), &self.db)
|
DbPeer::get(&AsKey::new(id), self.db)
|
||||||
.map_err(|e| anyhow!(e))?
|
.map_err(|e| anyhow!(e))?
|
||||||
.ok_or(Error::msg("Peer not found"))?
|
.ok_or(Error::msg("Peer not found"))?
|
||||||
.modify(&self.db, |doc| doc.contents.name = name.clone())
|
.modify(self.db, |doc| doc.contents.name = name.clone())
|
||||||
.map_err(|e| anyhow!(e))
|
.map_err(|e| anyhow!(e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -80,9 +87,9 @@ mod tests {
|
||||||
let db = StateDB::init(None);
|
let db = StateDB::init(None);
|
||||||
|
|
||||||
let peer = Peer::new(PeerId::default(), Some("Peer name".to_string()));
|
let peer = Peer::new(PeerId::default(), Some("Peer name".to_string()));
|
||||||
db.add_peer(peer.clone()).unwrap();
|
db.peers().add(peer.clone()).unwrap();
|
||||||
|
|
||||||
let retrieved_peer = db.get_peer(peer.id()).unwrap();
|
let retrieved_peer = db.peers().get(peer.id()).unwrap();
|
||||||
|
|
||||||
assert_eq!(Some(peer), retrieved_peer)
|
assert_eq!(Some(peer), retrieved_peer)
|
||||||
}
|
}
|
||||||
|
@ -92,9 +99,9 @@ mod tests {
|
||||||
let db = StateDB::init(None);
|
let db = StateDB::init(None);
|
||||||
|
|
||||||
let peer = Peer::new(PeerId::default(), Some("Peer name".to_string()));
|
let peer = Peer::new(PeerId::default(), Some("Peer name".to_string()));
|
||||||
db.add_peer(peer.clone()).unwrap();
|
db.peers().add(peer.clone()).unwrap();
|
||||||
|
|
||||||
let all_peers = db.get_all_peers().unwrap();
|
let all_peers = db.peers().get_all().unwrap();
|
||||||
assert_eq!(all_peers, vec![peer]);
|
assert_eq!(all_peers, vec![peer]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -103,11 +110,12 @@ mod tests {
|
||||||
let db = StateDB::init(None);
|
let db = StateDB::init(None);
|
||||||
|
|
||||||
let peer = Peer::new(PeerId::default(), Some("Peer name".to_string()));
|
let peer = Peer::new(PeerId::default(), Some("Peer name".to_string()));
|
||||||
db.add_peer(peer.clone()).unwrap();
|
db.peers().add(peer.clone()).unwrap();
|
||||||
db.set_peer_name(peer.id().clone(), &Some("New peer name".to_string()))
|
db.peers()
|
||||||
|
.set_name(peer.id().clone(), &Some("New peer name".to_string()))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let retrieved_peer = db.get_peer(peer.id()).unwrap();
|
let retrieved_peer = db.peers().get(peer.id()).unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(Peer::new(peer.id(), Some("New peer name".to_string()))),
|
Some(Peer::new(peer.id(), Some("New peer name".to_string()))),
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
use anyhow::{anyhow, Error};
|
use anyhow::{anyhow, Error};
|
||||||
use bonsaidb::core::{
|
use bonsaidb::{
|
||||||
connection::Connection,
|
core::{
|
||||||
document::Emit,
|
connection::Connection,
|
||||||
schema::{Collection, MapReduce, SerializedCollection, View, ViewSchema},
|
document::Emit,
|
||||||
|
schema::{Collection, MapReduce, SerializedCollection, View, ViewSchema},
|
||||||
|
},
|
||||||
|
local::Database,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use tracing::debug;
|
||||||
use ubisync_lib::types::{AppId, PotId};
|
use ubisync_lib::types::{AppId, PotId};
|
||||||
|
|
||||||
use crate::state::database::{as_key::AsKey, StateDB};
|
use crate::state::database::{as_key::AsKey, StateDB};
|
||||||
|
@ -93,18 +97,26 @@ impl MapReduce for DbPotMembershipsByBothIds {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StateDB {
|
pub(crate) struct PotMemberships<'a> {
|
||||||
pub fn add_pot_membership(&self, pot: PotId, app: AppId) -> anyhow::Result<()> {
|
parent: &'a StateDB,
|
||||||
if let Err(_) = self.get_pot(pot.clone()) {
|
db: &'a Database,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> PotMemberships<'a> {
|
||||||
|
pub const fn new(parent: &'a StateDB, bonsai: &'a Database) -> Self {
|
||||||
|
Self { parent, db: bonsai }
|
||||||
|
}
|
||||||
|
pub fn add(&self, pot: PotId, app: AppId) -> anyhow::Result<()> {
|
||||||
|
if let Err(_) = self.parent.pots().get(pot.clone()) {
|
||||||
Err(Error::msg(
|
Err(Error::msg(
|
||||||
"A member was meant to be added to a pot which does not exist.",
|
"A member was meant to be added to a pot which does not exist.",
|
||||||
))
|
))
|
||||||
} else if let Err(_) = self.get_app(app.clone()) {
|
} else if let Err(_) = self.parent.apps().get(app.clone()) {
|
||||||
Err(Error::msg(
|
Err(Error::msg(
|
||||||
"A member app which does not exist was meant to be added to a pot",
|
"A member app which does not exist was meant to be added to a pot",
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
if self.get_pot_membership(pot.clone(), app.clone())?.is_some() {
|
if self.get(pot.clone(), app.clone())?.is_some() {
|
||||||
Err(Error::msg("Pot membership already exists"))
|
Err(Error::msg("Pot membership already exists"))
|
||||||
} else {
|
} else {
|
||||||
DbPotMembership::push(
|
DbPotMembership::push(
|
||||||
|
@ -112,7 +124,7 @@ impl StateDB {
|
||||||
pot_id: AsKey::new(pot),
|
pot_id: AsKey::new(pot),
|
||||||
app_id: AsKey::new(app),
|
app_id: AsKey::new(app),
|
||||||
},
|
},
|
||||||
&self.db,
|
self.db,
|
||||||
)
|
)
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
.map_err(|e| anyhow!(e))
|
.map_err(|e| anyhow!(e))
|
||||||
|
@ -120,7 +132,7 @@ impl StateDB {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_pot_membership(&self, pot: PotId, app: AppId) -> anyhow::Result<Option<()>> {
|
pub fn get(&self, pot: PotId, app: AppId) -> anyhow::Result<Option<()>> {
|
||||||
self.db
|
self.db
|
||||||
.view::<DbPotMembershipsByBothIds>()
|
.view::<DbPotMembershipsByBothIds>()
|
||||||
.with_key(&(AsKey::new(pot), AsKey::new(app)))
|
.with_key(&(AsKey::new(pot), AsKey::new(app)))
|
||||||
|
@ -129,7 +141,7 @@ impl StateDB {
|
||||||
.map_err(|e| anyhow!(e))
|
.map_err(|e| anyhow!(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_pot_members(&self, pot: PotId) -> anyhow::Result<Vec<AppId>> {
|
pub fn get_members(&self, pot: PotId) -> anyhow::Result<Vec<AppId>> {
|
||||||
self.db
|
self.db
|
||||||
.view::<DbPotMembershipsByPotId>()
|
.view::<DbPotMembershipsByPotId>()
|
||||||
.with_key(&AsKey::new(pot))
|
.with_key(&AsKey::new(pot))
|
||||||
|
@ -137,7 +149,7 @@ impl StateDB {
|
||||||
.map_err(|e| anyhow!(e))
|
.map_err(|e| anyhow!(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_app_pot_memberships(&self, app: AppId) -> anyhow::Result<Vec<PotId>> {
|
pub fn get_memberships(&self, app: AppId) -> anyhow::Result<Vec<PotId>> {
|
||||||
self.db
|
self.db
|
||||||
.view::<DbPotMembershipsByAppId>()
|
.view::<DbPotMembershipsByAppId>()
|
||||||
.with_key(&AsKey::new(app))
|
.with_key(&AsKey::new(app))
|
||||||
|
@ -157,21 +169,25 @@ mod tests {
|
||||||
let db = StateDB::init(None);
|
let db = StateDB::init(None);
|
||||||
let pot_id = PotId::new();
|
let pot_id = PotId::new();
|
||||||
let app_id = AppId::new();
|
let app_id = AppId::new();
|
||||||
db.add_pot(pot_id.clone(), "app_type".to_string()).unwrap();
|
db.pots()
|
||||||
db.add_app(
|
.add(pot_id.clone(), "app_type".to_string())
|
||||||
app_id.clone(),
|
.unwrap();
|
||||||
"name".to_string(),
|
db.apps()
|
||||||
"description".to_string(),
|
.add(
|
||||||
"app_type".to_string(),
|
app_id.clone(),
|
||||||
)
|
"name".to_string(),
|
||||||
.unwrap();
|
"description".to_string(),
|
||||||
db.add_pot_membership(pot_id.clone(), app_id.clone())
|
"app_type".to_string(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
db.pot_memberships()
|
||||||
|
.add(pot_id.clone(), app_id.clone())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let retrieved_members = db.get_pot_members(pot_id.clone()).unwrap();
|
let retrieved_members = db.pot_memberships().get_members(pot_id.clone()).unwrap();
|
||||||
assert_eq!(vec![app_id.clone()], retrieved_members);
|
assert_eq!(vec![app_id.clone()], retrieved_members);
|
||||||
|
|
||||||
let retrieved_memberships = db.get_app_pot_memberships(app_id).unwrap();
|
let retrieved_memberships = db.pot_memberships().get_memberships(app_id).unwrap();
|
||||||
assert_eq!(vec![pot_id], retrieved_memberships)
|
assert_eq!(vec![pot_id], retrieved_memberships)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
use anyhow::{Error, anyhow};
|
use anyhow::{anyhow, Error};
|
||||||
use bonsaidb::core::schema::{Collection, SerializedCollection};
|
use bonsaidb::{
|
||||||
|
core::schema::{Collection, SerializedCollection},
|
||||||
|
local::Database,
|
||||||
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use ubisync_lib::types::{Pot, PotId};
|
use ubisync_lib::types::{Pot, PotId};
|
||||||
|
|
||||||
|
@ -22,9 +25,17 @@ impl From<DbPot> for Pot {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StateDB {
|
pub(crate) struct Pots<'a> {
|
||||||
pub fn add_pot(&self, id: PotId, app_type: String) -> anyhow::Result<()> {
|
parent: &'a StateDB,
|
||||||
if self.get_pot(id.clone())?.is_some() {
|
db: &'a Database,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Pots<'a> {
|
||||||
|
pub const fn new(parent: &'a StateDB, bonsai: &'a Database) -> Self {
|
||||||
|
Self { parent, db: bonsai }
|
||||||
|
}
|
||||||
|
pub fn add(&self, id: PotId, app_type: String) -> anyhow::Result<()> {
|
||||||
|
if self.get(id.clone())?.is_some() {
|
||||||
Err(Error::msg("Pot already exists"))
|
Err(Error::msg("Pot already exists"))
|
||||||
} else {
|
} else {
|
||||||
DbPot::push(
|
DbPot::push(
|
||||||
|
@ -32,15 +43,15 @@ impl StateDB {
|
||||||
id: AsKey::new(id),
|
id: AsKey::new(id),
|
||||||
app_type,
|
app_type,
|
||||||
},
|
},
|
||||||
&self.db,
|
self.db,
|
||||||
)
|
)
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
.map_err(|e| anyhow!(e))
|
.map_err(|e| anyhow!(e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_pot(&self, id: PotId) -> anyhow::Result<Option<Pot>> {
|
pub fn get(&self, id: PotId) -> anyhow::Result<Option<Pot>> {
|
||||||
DbPot::get(&AsKey::new(id), &self.db)
|
DbPot::get(&AsKey::new(id), self.db)
|
||||||
.map(|pot_opt| pot_opt.map(|pot| pot.contents.into()))
|
.map(|pot_opt| pot_opt.map(|pot| pot.contents.into()))
|
||||||
.map_err(|e| anyhow!(e))
|
.map_err(|e| anyhow!(e))
|
||||||
}
|
}
|
||||||
|
@ -56,9 +67,11 @@ mod tests {
|
||||||
fn add_get() {
|
fn add_get() {
|
||||||
let db = StateDB::init(None);
|
let db = StateDB::init(None);
|
||||||
let pot_id = PotId::new();
|
let pot_id = PotId::new();
|
||||||
db.add_pot(pot_id.clone(), "app_type".to_string()).unwrap();
|
db.pots()
|
||||||
|
.add(pot_id.clone(), "app_type".to_string())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
let retrieved_pot = db.get_pot(pot_id.clone()).unwrap();
|
let retrieved_pot = db.pots().get(pot_id.clone()).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
retrieved_pot,
|
retrieved_pot,
|
||||||
Some(Pot {
|
Some(Pot {
|
||||||
|
|
|
@ -2,21 +2,21 @@ use anyhow::anyhow;
|
||||||
use bonsaidb::{
|
use bonsaidb::{
|
||||||
core::keyvalue::KeyValue,
|
core::keyvalue::KeyValue,
|
||||||
local::{
|
local::{
|
||||||
config::{Builder, StorageConfiguration},
|
config::{Builder, StorageConfiguration},Database
|
||||||
Database as BonsaiDb,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
use ubisync_lib::types::PeerId;
|
use ubisync_lib::types::PeerId;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use self::collections::UbisyncSchema;
|
use self::collections::{Apps, Elements, Families, Peers, PotMemberships, Pots, UbisyncSchema};
|
||||||
|
|
||||||
mod as_key;
|
mod as_key;
|
||||||
mod collections;
|
mod collections;
|
||||||
|
|
||||||
|
|
||||||
pub struct StateDB {
|
pub struct StateDB {
|
||||||
db: BonsaiDb,
|
db: Database,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StateDB {
|
impl StateDB {
|
||||||
|
@ -26,9 +26,8 @@ impl StateDB {
|
||||||
None => StorageConfiguration::new(format!("/tmp/{}", Uuid::new_v4())),
|
None => StorageConfiguration::new(format!("/tmp/{}", Uuid::new_v4())),
|
||||||
// None => StorageConfiguration::default().memory_only()
|
// None => StorageConfiguration::default().memory_only()
|
||||||
};
|
};
|
||||||
StateDB {
|
let db = Database::open::<UbisyncSchema>(storage_conf).unwrap();
|
||||||
db: BonsaiDb::open::<UbisyncSchema>(storage_conf).unwrap(),
|
StateDB { db }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_family_join_request(&self, peer: PeerId) {
|
pub fn add_family_join_request(&self, peer: PeerId) {
|
||||||
|
@ -54,6 +53,25 @@ impl StateDB {
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
.map_err(|e| anyhow!(e))
|
.map_err(|e| anyhow!(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const fn apps(&self) -> Apps {
|
||||||
|
Apps::new(&self, &self.db)
|
||||||
|
}
|
||||||
|
pub const fn elements(&self) -> Elements {
|
||||||
|
Elements::new(&self, &self.db)
|
||||||
|
}
|
||||||
|
pub const fn families(&self) -> Families {
|
||||||
|
Families::new(&self, &self.db)
|
||||||
|
}
|
||||||
|
pub const fn peers(&self) -> Peers {
|
||||||
|
Peers::new(&self, &self.db)
|
||||||
|
}
|
||||||
|
pub const fn pot_memberships(&self) -> PotMemberships {
|
||||||
|
PotMemberships::new(&self, &self.db)
|
||||||
|
}
|
||||||
|
pub const fn pots(&self) -> Pots {
|
||||||
|
Pots::new(&self, &self.db)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -76,7 +76,7 @@ impl State {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_apps(&self) -> anyhow::Result<Vec<App>> {
|
pub fn get_apps(&self) -> anyhow::Result<Vec<App>> {
|
||||||
self.db.get_all_apps()
|
self.db.apps().get_all()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_element_content(
|
pub fn set_element_content(
|
||||||
|
@ -85,7 +85,8 @@ impl State {
|
||||||
content: ElementContent,
|
content: ElementContent,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
self.db
|
self.db
|
||||||
.set_element_content(element_id.clone(), content.clone())
|
.elements()
|
||||||
|
.set_content(element_id.clone(), content.clone())
|
||||||
.inspect(|_| {
|
.inspect(|_| {
|
||||||
//TODO: Get all peers interested in the element, e.g. because they subscribe to the element's pot, a share, etc.
|
//TODO: Get all peers interested in the element, e.g. because they subscribe to the element's pot, a share, etc.
|
||||||
self.send_to_peers(
|
self.send_to_peers(
|
||||||
|
@ -94,28 +95,28 @@ impl State {
|
||||||
content: content.clone(),
|
content: content.clone(),
|
||||||
},
|
},
|
||||||
self.own_peer_id()
|
self.own_peer_id()
|
||||||
.map(|id| self.db.get_peer_family_members(id).unwrap_or_default())
|
.map(|id| self.db.families().get_members(id).unwrap_or_default())
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_element(&self, element_id: ElementId) -> anyhow::Result<()> {
|
pub fn remove_element(&self, element_id: ElementId) -> anyhow::Result<()> {
|
||||||
self.db.remove_element(element_id.clone()).inspect(|_| {
|
self.db.elements().remove(element_id.clone()).inspect(|_| {
|
||||||
//TODO: Get all peers interested in the element, e.g. because they subscribe to the element's pot, a share, etc.
|
//TODO: Get all peers interested in the element, e.g. because they subscribe to the element's pot, a share, etc.
|
||||||
self.send_to_peers(
|
self.send_to_peers(
|
||||||
MessageContent::RemoveElement {
|
MessageContent::RemoveElement {
|
||||||
id: element_id.clone(),
|
id: element_id.clone(),
|
||||||
},
|
},
|
||||||
self.own_peer_id()
|
self.own_peer_id()
|
||||||
.map(|id| self.db.get_peer_family_members(id).unwrap_or_default())
|
.map(|id| self.db.families().get_members(id).unwrap_or_default())
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_element(&self, id: ElementId) -> Option<Element> {
|
pub fn get_element(&self, id: ElementId) -> Option<Element> {
|
||||||
self.db.get_element(id)
|
self.db.elements().get(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_elements_by_tag(&self, _tag: &Tag) -> Vec<ElementId> {
|
pub fn get_elements_by_tag(&self, _tag: &Tag) -> Vec<ElementId> {
|
||||||
|
@ -123,20 +124,22 @@ impl State {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_peer(&self, peer: Peer) -> anyhow::Result<()> {
|
pub fn add_peer(&self, peer: Peer) -> anyhow::Result<()> {
|
||||||
self.db.add_peer(peer)
|
self.db.peers().add(peer)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_peers(&self) -> anyhow::Result<Vec<Peer>> {
|
pub fn get_peers(&self) -> anyhow::Result<Vec<Peer>> {
|
||||||
self.db.get_all_peers()
|
self.db.peers().get_all()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_pot_member(&self, pot: PotId, app: AppId) -> anyhow::Result<()> {
|
pub fn add_pot_member(&self, pot: PotId, app: AppId) -> anyhow::Result<()> {
|
||||||
let p = self
|
let p = self
|
||||||
.db
|
.db
|
||||||
.get_pot(pot.clone())?
|
.pots()
|
||||||
|
.get(pot.clone())?
|
||||||
.ok_or(Error::msg("Could not find pot"))?;
|
.ok_or(Error::msg("Could not find pot"))?;
|
||||||
self.db
|
self.db
|
||||||
.add_pot_membership(pot.clone(), app.clone())
|
.pot_memberships()
|
||||||
|
.add(pot.clone(), app.clone())
|
||||||
.inspect(|_| {
|
.inspect(|_| {
|
||||||
self.emit_app_event(
|
self.emit_app_event(
|
||||||
&app,
|
&app,
|
||||||
|
@ -153,11 +156,12 @@ impl State {
|
||||||
.own_peer_id()
|
.own_peer_id()
|
||||||
.ok_or(Error::msg("Could not get own PeerId"))?;
|
.ok_or(Error::msg("Could not get own PeerId"))?;
|
||||||
|
|
||||||
let my_family = match self.db.get_family_of_peer(my_id.clone())? {
|
let my_family = match self.db.families().get_by_peer(my_id.clone())? {
|
||||||
Some(id) => {
|
Some(id) => {
|
||||||
self.db.add_peer_to_family(peer.clone(), id.clone())?;
|
self.db.families().add_peer(peer.clone(), id.clone())?;
|
||||||
self.db
|
self.db
|
||||||
.get_peer_family(id)?
|
.families()
|
||||||
|
.get(id)?
|
||||||
.ok_or(Error::msg("Family not found"))?
|
.ok_or(Error::msg("Family not found"))?
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
|
@ -169,7 +173,7 @@ impl State {
|
||||||
name: None,
|
name: None,
|
||||||
members: HashSet::from([my_id.clone(), peer.clone()]),
|
members: HashSet::from([my_id.clone(), peer.clone()]),
|
||||||
};
|
};
|
||||||
self.db.add_peer_family(family.clone())?;
|
self.db.families().add(family.clone())?;
|
||||||
family
|
family
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -186,9 +190,10 @@ impl State {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_family_of(&self, peer: PeerId) -> anyhow::Result<Family> {
|
pub fn get_family_of(&self, peer: PeerId) -> anyhow::Result<Family> {
|
||||||
match self.db.get_peer_family(
|
match self.db.families().get(
|
||||||
self.db
|
self.db
|
||||||
.get_family_of_peer(peer)?
|
.families()
|
||||||
|
.get_by_peer(peer)?
|
||||||
.ok_or(Error::msg("Family of peer not found"))?,
|
.ok_or(Error::msg("Family of peer not found"))?,
|
||||||
) {
|
) {
|
||||||
Ok(Some(family)) => Ok(family),
|
Ok(Some(family)) => Ok(family),
|
||||||
|
@ -256,7 +261,7 @@ impl State {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_event_channels(&self) {
|
fn init_event_channels(&self) {
|
||||||
let app_ids = self.db.get_all_app_ids().unwrap();
|
let app_ids = self.db.apps().get_all_ids().unwrap();
|
||||||
for app in app_ids {
|
for app in app_ids {
|
||||||
self.create_event_channel_if_not_exists(app);
|
self.create_event_channel_if_not_exists(app);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue