Signed-off-by: kjuulh <contact@kjuulh.io>
This commit is contained in:
parent
710fb431f7
commit
9587c60e72
@ -37,6 +37,7 @@ service Graph {
|
|||||||
rpc CreateItem(CreateItemRequest) returns (CreateItemResponse);
|
rpc CreateItem(CreateItemRequest) returns (CreateItemResponse);
|
||||||
rpc UpdateItem(UpdateItemRequest) returns (UpdateItemResponse);
|
rpc UpdateItem(UpdateItemRequest) returns (UpdateItemResponse);
|
||||||
rpc ToggleItem(ToggleItemRequest) returns (ToggleItemResponse);
|
rpc ToggleItem(ToggleItemRequest) returns (ToggleItemResponse);
|
||||||
|
rpc Archive(ArchiveRequest) returns (ArchiveResponse);
|
||||||
|
|
||||||
// Queriers
|
// Queriers
|
||||||
rpc GetAvailableRoots(GetAvailableRootsRequest) returns (GetAvailableRootsResponse);
|
rpc GetAvailableRoots(GetAvailableRootsRequest) returns (GetAvailableRootsResponse);
|
||||||
@ -76,6 +77,12 @@ message ToggleItemRequest {
|
|||||||
}
|
}
|
||||||
message ToggleItemResponse {}
|
message ToggleItemResponse {}
|
||||||
|
|
||||||
|
message ArchiveRequest {
|
||||||
|
string root = 1;
|
||||||
|
repeated string path = 2;
|
||||||
|
}
|
||||||
|
message ArchiveResponse {}
|
||||||
|
|
||||||
// Queries
|
// Queries
|
||||||
message GetAvailableRootsRequest {}
|
message GetAvailableRootsRequest {}
|
||||||
message GetAvailableRootsResponse {
|
message GetAvailableRootsResponse {
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
-- Add migration script here
|
||||||
|
|
||||||
|
ALTER TABLE nodes ADD COLUMN status VARCHAR(20) DEFAULT 'active' NOT NULL;
|
@ -2,6 +2,7 @@ use hyperlog_core::log::ItemState;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
services::{
|
services::{
|
||||||
|
archive::{self, Archive, ArchiveExt},
|
||||||
create_item::{self, CreateItem, CreateItemExt},
|
create_item::{self, CreateItem, CreateItemExt},
|
||||||
create_root::{self, CreateRoot, CreateRootExt},
|
create_root::{self, CreateRoot, CreateRootExt},
|
||||||
create_section::{self, CreateSection, CreateSectionExt},
|
create_section::{self, CreateSection, CreateSectionExt},
|
||||||
@ -43,6 +44,10 @@ pub enum Command {
|
|||||||
src: Vec<String>,
|
src: Vec<String>,
|
||||||
dest: Vec<String>,
|
dest: Vec<String>,
|
||||||
},
|
},
|
||||||
|
Archive {
|
||||||
|
root: String,
|
||||||
|
path: Vec<String>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@ -52,6 +57,7 @@ pub struct Commander {
|
|||||||
create_item: CreateItem,
|
create_item: CreateItem,
|
||||||
update_item: UpdateItem,
|
update_item: UpdateItem,
|
||||||
toggle_item: ToggleItem,
|
toggle_item: ToggleItem,
|
||||||
|
archive: Archive,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Commander {
|
impl Commander {
|
||||||
@ -61,6 +67,7 @@ impl Commander {
|
|||||||
create_item: CreateItem,
|
create_item: CreateItem,
|
||||||
update_item: UpdateItem,
|
update_item: UpdateItem,
|
||||||
toggle_item: ToggleItem,
|
toggle_item: ToggleItem,
|
||||||
|
archive: Archive,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
create_root,
|
create_root,
|
||||||
@ -68,6 +75,7 @@ impl Commander {
|
|||||||
create_item,
|
create_item,
|
||||||
update_item,
|
update_item,
|
||||||
toggle_item,
|
toggle_item,
|
||||||
|
archive,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,6 +141,13 @@ impl Commander {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Command::Move { .. } => todo!(),
|
Command::Move { .. } => todo!(),
|
||||||
|
Command::Archive { root, path } => {
|
||||||
|
self.archive
|
||||||
|
.execute(archive::Request { root, path })
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -149,6 +164,7 @@ impl CommanderExt for SharedState {
|
|||||||
self.create_item_service(),
|
self.create_item_service(),
|
||||||
self.update_item_service(),
|
self.update_item_service(),
|
||||||
self.toggle_item_service(),
|
self.toggle_item_service(),
|
||||||
|
self.archive_service(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -380,6 +380,38 @@ impl Graph for Server {
|
|||||||
|
|
||||||
Ok(Response::new(ToggleItemResponse {}))
|
Ok(Response::new(ToggleItemResponse {}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn archive(
|
||||||
|
&self,
|
||||||
|
request: tonic::Request<ArchiveRequest>,
|
||||||
|
) -> std::result::Result<tonic::Response<ArchiveResponse>, tonic::Status> {
|
||||||
|
let req = request.into_inner();
|
||||||
|
tracing::trace!("update item: req({:?})", req);
|
||||||
|
|
||||||
|
if req.root.is_empty() {
|
||||||
|
return Err(tonic::Status::new(
|
||||||
|
tonic::Code::InvalidArgument,
|
||||||
|
"root cannot be empty".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.path.is_empty() {
|
||||||
|
return Err(tonic::Status::new(
|
||||||
|
tonic::Code::InvalidArgument,
|
||||||
|
"path cannot be empty".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.commander
|
||||||
|
.execute(Command::Archive {
|
||||||
|
root: req.root,
|
||||||
|
path: req.path,
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.map_err(to_tonic_err)?;
|
||||||
|
|
||||||
|
Ok(Response::new(ArchiveResponse {}))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_native(from: &hyperlog_core::log::GraphItem) -> anyhow::Result<GraphItem> {
|
fn to_native(from: &hyperlog_core::log::GraphItem) -> anyhow::Result<GraphItem> {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
pub mod archive;
|
||||||
pub mod create_item;
|
pub mod create_item;
|
||||||
pub mod create_root;
|
pub mod create_root;
|
||||||
pub mod create_section;
|
pub mod create_section;
|
||||||
|
70
crates/hyperlog-server/src/services/archive.rs
Normal file
70
crates/hyperlog-server/src/services/archive.rs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
use crate::state::SharedState;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Archive {
|
||||||
|
db: sqlx::PgPool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Request {
|
||||||
|
pub root: String,
|
||||||
|
pub path: Vec<String>,
|
||||||
|
}
|
||||||
|
pub struct Response {}
|
||||||
|
|
||||||
|
#[derive(sqlx::FromRow)]
|
||||||
|
struct Root {
|
||||||
|
id: uuid::Uuid,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Archive {
|
||||||
|
pub fn new(db: sqlx::PgPool) -> Self {
|
||||||
|
Self { db }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn execute(&self, req: Request) -> anyhow::Result<Response> {
|
||||||
|
let Root { id: root_id, .. } =
|
||||||
|
sqlx::query_as(r#"SELECT * FROM roots WHERE root_name = $1"#)
|
||||||
|
.bind(req.root)
|
||||||
|
.fetch_one(&self.db)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
sqlx::query(
|
||||||
|
r#"
|
||||||
|
UPDATE nodes
|
||||||
|
SET status = 'archive'
|
||||||
|
WHERE
|
||||||
|
root_id = $1
|
||||||
|
AND path = $2;
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.bind(root_id)
|
||||||
|
.bind(req.path.join("."))
|
||||||
|
.execute(&self.db)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
sqlx::query(
|
||||||
|
r#"
|
||||||
|
UPDATE nodes
|
||||||
|
SET status = 'archive'
|
||||||
|
WHERE root_id = $1
|
||||||
|
AND path LIKE $2;
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.bind(root_id)
|
||||||
|
.bind(format!("{}.%", req.path.join(".")))
|
||||||
|
.execute(&self.db)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(Response {})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ArchiveExt {
|
||||||
|
fn archive_service(&self) -> Archive;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ArchiveExt for SharedState {
|
||||||
|
fn archive_service(&self) -> Archive {
|
||||||
|
Archive::new(self.db.clone())
|
||||||
|
}
|
||||||
|
}
|
@ -60,6 +60,7 @@ impl GetGraph {
|
|||||||
nodes
|
nodes
|
||||||
WHERE
|
WHERE
|
||||||
root_id = $1
|
root_id = $1
|
||||||
|
AND status = 'active'
|
||||||
LIMIT
|
LIMIT
|
||||||
1000
|
1000
|
||||||
"#,
|
"#,
|
||||||
|
@ -92,7 +92,8 @@ impl<'a> App<'a> {
|
|||||||
Msg::ItemCreated(IOEvent::Success(()))
|
Msg::ItemCreated(IOEvent::Success(()))
|
||||||
| Msg::ItemUpdated(IOEvent::Success(()))
|
| Msg::ItemUpdated(IOEvent::Success(()))
|
||||||
| Msg::SectionCreated(IOEvent::Success(()))
|
| Msg::SectionCreated(IOEvent::Success(()))
|
||||||
| Msg::ItemToggled(IOEvent::Success(())) => {
|
| Msg::ItemToggled(IOEvent::Success(()))
|
||||||
|
| Msg::Archive(IOEvent::Success(())) => {
|
||||||
batch.with(self.graph_explorer.new_update_graph());
|
batch.with(self.graph_explorer.new_update_graph());
|
||||||
}
|
}
|
||||||
Msg::MoveRight => self.graph_explorer.move_right()?,
|
Msg::MoveRight => self.graph_explorer.move_right()?,
|
||||||
|
@ -39,6 +39,10 @@ pub enum Command {
|
|||||||
src: Vec<String>,
|
src: Vec<String>,
|
||||||
dest: Vec<String>,
|
dest: Vec<String>,
|
||||||
},
|
},
|
||||||
|
Archive {
|
||||||
|
root: String,
|
||||||
|
path: Vec<String>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
@ -74,6 +74,9 @@ impl Commander {
|
|||||||
state,
|
state,
|
||||||
},
|
},
|
||||||
)?,
|
)?,
|
||||||
|
Command::Archive { root, path } => self
|
||||||
|
.engine
|
||||||
|
.archive(&root, &path.iter().map(|p| p.as_str()).collect::<Vec<_>>())?,
|
||||||
}
|
}
|
||||||
|
|
||||||
self.storage.store(&self.engine)?;
|
self.storage.store(&self.engine)?;
|
||||||
|
@ -27,7 +27,6 @@ impl Commander {
|
|||||||
let request = tonic::Request::new(CreateRootRequest { root });
|
let request = tonic::Request::new(CreateRootRequest { root });
|
||||||
let response = client.create_root(request).await?;
|
let response = client.create_root(request).await?;
|
||||||
let res = response.into_inner();
|
let res = response.into_inner();
|
||||||
//self.engine.create_root(&root)?;
|
|
||||||
}
|
}
|
||||||
Command::CreateSection { root, path } => {
|
Command::CreateSection { root, path } => {
|
||||||
let channel = self.channel.clone();
|
let channel = self.channel.clone();
|
||||||
@ -37,12 +36,6 @@ impl Commander {
|
|||||||
let request = tonic::Request::new(CreateSectionRequest { root, path });
|
let request = tonic::Request::new(CreateSectionRequest { root, path });
|
||||||
let response = client.create_section(request).await?;
|
let response = client.create_section(request).await?;
|
||||||
let res = response.into_inner();
|
let res = response.into_inner();
|
||||||
|
|
||||||
// self.engine.create(
|
|
||||||
// &root,
|
|
||||||
// &path.iter().map(|p| p.as_str()).collect::<Vec<_>>(),
|
|
||||||
// GraphItem::Section(BTreeMap::default()),
|
|
||||||
// )?;
|
|
||||||
}
|
}
|
||||||
Command::CreateItem {
|
Command::CreateItem {
|
||||||
root,
|
root,
|
||||||
@ -73,23 +66,9 @@ impl Commander {
|
|||||||
});
|
});
|
||||||
let response = client.create_item(request).await?;
|
let response = client.create_item(request).await?;
|
||||||
let res = response.into_inner();
|
let res = response.into_inner();
|
||||||
// self.engine.create(
|
|
||||||
// &root,
|
|
||||||
// &path.iter().map(|p| p.as_str()).collect::<Vec<_>>(),
|
|
||||||
// GraphItem::Item {
|
|
||||||
// title,
|
|
||||||
// description,
|
|
||||||
// state,
|
|
||||||
// },
|
|
||||||
// )?
|
|
||||||
}
|
}
|
||||||
Command::Move { root, src, dest } => {
|
Command::Move { root, src, dest } => {
|
||||||
todo!()
|
todo!()
|
||||||
// self.engine.section_move(
|
|
||||||
// &root,
|
|
||||||
// &src.iter().map(|p| p.as_str()).collect::<Vec<_>>(),
|
|
||||||
// &dest.iter().map(|p| p.as_str()).collect::<Vec<_>>(),
|
|
||||||
// )?
|
|
||||||
}
|
}
|
||||||
Command::ToggleItem { root, path } => {
|
Command::ToggleItem { root, path } => {
|
||||||
let channel = self.channel.clone();
|
let channel = self.channel.clone();
|
||||||
@ -129,21 +108,18 @@ impl Commander {
|
|||||||
});
|
});
|
||||||
let response = client.update_item(request).await?;
|
let response = client.update_item(request).await?;
|
||||||
let res = response.into_inner();
|
let res = response.into_inner();
|
||||||
// self.engine.update_item(
|
}
|
||||||
// &root,
|
Command::Archive { root, path } => {
|
||||||
// &path.iter().map(|p| p.as_str()).collect::<Vec<_>>(),
|
let channel = self.channel.clone();
|
||||||
// GraphItem::Item {
|
|
||||||
// title,
|
let mut client = GraphClient::new(channel);
|
||||||
// description,
|
|
||||||
// state,
|
let request = tonic::Request::new(ArchiveRequest { root, path });
|
||||||
// },
|
let response = client.archive(request).await?;
|
||||||
// )?
|
let res = response.into_inner();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// self.storage.store(&self.engine)?;
|
|
||||||
// self.events.enque_command(cmd)?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
|
|||||||
|
|
||||||
pub mod batch;
|
pub mod batch;
|
||||||
|
|
||||||
|
pub mod archive;
|
||||||
pub mod create_item;
|
pub mod create_item;
|
||||||
pub mod create_section;
|
pub mod create_section;
|
||||||
pub mod open_update_item_dialog;
|
pub mod open_update_item_dialog;
|
||||||
|
58
crates/hyperlog-tui/src/commands/archive.rs
Normal file
58
crates/hyperlog-tui/src/commands/archive.rs
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
use hyperlog_core::log::ItemState;
|
||||||
|
use itertools::Itertools;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
commander::{self, Commander},
|
||||||
|
models::{IOEvent, Msg},
|
||||||
|
state::SharedState,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct ArchiveCommand {
|
||||||
|
commander: Commander,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ArchiveCommand {
|
||||||
|
pub fn new(commander: Commander) -> Self {
|
||||||
|
Self { commander }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn command(self, root: &str, path: &[&str]) -> super::Command {
|
||||||
|
let root = root.to_owned();
|
||||||
|
let path = path.iter().map(|s| s.to_string()).collect_vec();
|
||||||
|
|
||||||
|
super::Command::new(|dispatch| {
|
||||||
|
tokio::spawn(async move {
|
||||||
|
dispatch.send(Msg::Archive(IOEvent::Initialized));
|
||||||
|
|
||||||
|
match self
|
||||||
|
.commander
|
||||||
|
.execute(commander::Command::Archive { root, path })
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(()) => {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
{
|
||||||
|
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch.send(Msg::Archive(IOEvent::Success(())));
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
dispatch.send(Msg::Archive(IOEvent::Failure(e.to_string())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
None
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ArchiveCommandExt {
|
||||||
|
fn archive_command(&self) -> ArchiveCommand;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ArchiveCommandExt for SharedState {
|
||||||
|
fn archive_command(&self) -> ArchiveCommand {
|
||||||
|
ArchiveCommand::new(self.commander.clone())
|
||||||
|
}
|
||||||
|
}
|
@ -6,7 +6,7 @@ use ratatui::{prelude::*, widgets::*};
|
|||||||
use crate::{
|
use crate::{
|
||||||
command_parser::Commands,
|
command_parser::Commands,
|
||||||
commands::{
|
commands::{
|
||||||
batch::BatchCommand, create_item::CreateItemCommandExt,
|
archive::ArchiveCommandExt, batch::BatchCommand, create_item::CreateItemCommandExt,
|
||||||
create_section::CreateSectionCommandExt,
|
create_section::CreateSectionCommandExt,
|
||||||
open_update_item_dialog::OpenUpdateItemDialogCommandExt, toggle_item::ToggleItemCommandExt,
|
open_update_item_dialog::OpenUpdateItemDialogCommandExt, toggle_item::ToggleItemCommandExt,
|
||||||
update_graph::UpdateGraphCommandExt, Command, IntoCommand,
|
update_graph::UpdateGraphCommandExt, Command, IntoCommand,
|
||||||
@ -236,7 +236,19 @@ impl<'a> GraphExplorer<'a> {
|
|||||||
match command {
|
match command {
|
||||||
Commands::Archive => {
|
Commands::Archive => {
|
||||||
if !self.get_current_path().is_empty() {
|
if !self.get_current_path().is_empty() {
|
||||||
tracing::debug!("archiving path: {:?}", self.get_current_path())
|
batch.with(
|
||||||
|
self.state
|
||||||
|
.archive_command()
|
||||||
|
.command(
|
||||||
|
&self.inner.root,
|
||||||
|
&self
|
||||||
|
.get_current_path()
|
||||||
|
.iter()
|
||||||
|
.map(|i| i.as_str())
|
||||||
|
.collect_vec(),
|
||||||
|
)
|
||||||
|
.into_command(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Commands::CreateSection { name } => {
|
Commands::CreateSection { name } => {
|
||||||
@ -244,13 +256,6 @@ impl<'a> GraphExplorer<'a> {
|
|||||||
let mut path = self.get_current_path();
|
let mut path = self.get_current_path();
|
||||||
path.push(name.replace(".", "-"));
|
path.push(name.replace(".", "-"));
|
||||||
|
|
||||||
// self.state
|
|
||||||
// .commander
|
|
||||||
// .execute(commander::Command::CreateSection {
|
|
||||||
// root: self.inner.root.clone(),
|
|
||||||
// path,
|
|
||||||
// })?;
|
|
||||||
|
|
||||||
let cmd = self.state.create_section_command().command(
|
let cmd = self.state.create_section_command().command(
|
||||||
&self.inner.root,
|
&self.inner.root,
|
||||||
&path.iter().map(|i| i.as_str()).collect_vec(),
|
&path.iter().map(|i| i.as_str()).collect_vec(),
|
||||||
|
@ -204,6 +204,12 @@ impl Engine {
|
|||||||
Some(items)
|
Some(items)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn archive(&mut self, root: &str, path: &[&str]) -> anyhow::Result<()> {
|
||||||
|
self.delete(root, path)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for Engine {
|
impl Display for Engine {
|
||||||
|
@ -27,6 +27,7 @@ pub enum Msg {
|
|||||||
ItemUpdated(IOEvent<()>),
|
ItemUpdated(IOEvent<()>),
|
||||||
SectionCreated(IOEvent<()>),
|
SectionCreated(IOEvent<()>),
|
||||||
ItemToggled(IOEvent<()>),
|
ItemToggled(IOEvent<()>),
|
||||||
|
Archive(IOEvent<()>),
|
||||||
|
|
||||||
OpenUpdateItemDialog(IOEvent<()>),
|
OpenUpdateItemDialog(IOEvent<()>),
|
||||||
}
|
}
|
||||||
|
@ -69,4 +69,8 @@ impl SharedEngine {
|
|||||||
pub(crate) fn get_roots(&self) -> Option<Vec<String>> {
|
pub(crate) fn get_roots(&self) -> Option<Vec<String>> {
|
||||||
self.inner.read().unwrap().get_roots()
|
self.inner.read().unwrap().get_roots()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn archive(&self, root: &str, path: &[&str]) -> anyhow::Result<()> {
|
||||||
|
self.inner.write().unwrap().archive(root, path)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user