feat: server can actually create root and sections
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
Signed-off-by: kjuulh <contact@kjuulh.io>
This commit is contained in:
parent
76f1c87663
commit
699bac7159
@ -32,11 +32,29 @@ message GraphItem {
|
||||
}
|
||||
|
||||
service Graph {
|
||||
// Commands
|
||||
rpc CreateSection(CreateSectionRequest) returns (CreateSectionResponse);
|
||||
rpc CreateRoot(CreateRootRequest) returns (CreateRootResponse);
|
||||
|
||||
// Queriers
|
||||
rpc GetAvailableRoots(GetAvailableRootsRequest) returns (GetAvailableRootsResponse);
|
||||
rpc Get(GetRequest) returns (GetReply);
|
||||
rpc CreateSection(CreateSectionRequest) returns (CreateSectionResponse);
|
||||
|
||||
}
|
||||
|
||||
// Commands
|
||||
message CreateSectionRequest {
|
||||
string root = 1;
|
||||
repeated string path = 2;
|
||||
}
|
||||
message CreateSectionResponse {}
|
||||
|
||||
message CreateRootRequest {
|
||||
string root = 1;
|
||||
}
|
||||
message CreateRootResponse {}
|
||||
|
||||
// Queries
|
||||
message GetAvailableRootsRequest {}
|
||||
message GetAvailableRootsResponse {
|
||||
repeated string roots = 1;
|
||||
@ -50,8 +68,3 @@ message GetRequest {
|
||||
message GetReply {
|
||||
GraphItem item = 1;
|
||||
}
|
||||
|
||||
|
||||
message CreateSectionRequest {}
|
||||
message CreateSectionResponse {}
|
||||
|
||||
|
@ -1 +1,14 @@
|
||||
-- Add migration script here
|
||||
|
||||
CREATE TABLE roots (
|
||||
id UUID NOT NULL PRIMARY KEY,
|
||||
root_name VARCHAR(255) UNIQUE NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE nodes (
|
||||
id UUID NOT NULL PRIMARY KEY,
|
||||
root_id UUID NOT NULL,
|
||||
path VARCHAR NOT NULL,
|
||||
item_type VARCHAR NOT NULL,
|
||||
item_content JSONB
|
||||
);
|
||||
|
@ -1,5 +1,13 @@
|
||||
use hyperlog_core::log::{GraphItem, ItemState};
|
||||
|
||||
use crate::{
|
||||
services::{
|
||||
create_root::{self, CreateRoot, CreateRootExt},
|
||||
create_section::{self, CreateSection, CreateSectionExt},
|
||||
},
|
||||
state::SharedState,
|
||||
};
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub enum Command {
|
||||
CreateRoot {
|
||||
@ -35,14 +43,35 @@ pub enum Command {
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub struct Commander {}
|
||||
pub struct Commander {
|
||||
create_root: CreateRoot,
|
||||
create_section: CreateSection,
|
||||
}
|
||||
|
||||
#[allow(dead_code, unused_variables)]
|
||||
impl Commander {
|
||||
pub fn execute(&self, cmd: Command) -> anyhow::Result<()> {
|
||||
pub fn new(create_root: CreateRoot, create_section: CreateSection) -> Self {
|
||||
Self {
|
||||
create_root,
|
||||
create_section,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn execute(&self, cmd: Command) -> anyhow::Result<()> {
|
||||
match cmd {
|
||||
Command::CreateRoot { root } => todo!(),
|
||||
Command::CreateSection { root, path } => todo!(),
|
||||
Command::CreateRoot { root } => {
|
||||
self.create_root
|
||||
.execute(create_root::Request { root })
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Command::CreateSection { root, path } => {
|
||||
self.create_section
|
||||
.execute(create_section::Request { root, path })
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Command::CreateItem {
|
||||
root,
|
||||
path,
|
||||
@ -61,38 +90,14 @@ impl Commander {
|
||||
Command::Move { root, src, dest } => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn create_root(&self, root: &str) -> anyhow::Result<()> {
|
||||
todo!()
|
||||
}
|
||||
pub trait CommanderExt {
|
||||
fn commander(&self) -> Commander;
|
||||
}
|
||||
|
||||
pub async fn create(&self, root: &str, path: &[&str], item: GraphItem) -> anyhow::Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn get(&self, root: &str, path: &[&str]) -> Option<GraphItem> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn section_move(
|
||||
&self,
|
||||
root: &str,
|
||||
src_path: &[&str],
|
||||
dest_path: &[&str],
|
||||
) -> anyhow::Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn delete(&self, root: &str, path: &[&str]) -> anyhow::Result<()> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub async fn update_item(
|
||||
&self,
|
||||
root: &str,
|
||||
path: &[&str],
|
||||
item: &GraphItem,
|
||||
) -> anyhow::Result<()> {
|
||||
todo!()
|
||||
impl CommanderExt for SharedState {
|
||||
fn commander(&self) -> Commander {
|
||||
Commander::new(self.create_root_service(), self.create_section_service())
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ use std::{collections::HashMap, net::SocketAddr};
|
||||
use tonic::{transport, Response};
|
||||
|
||||
use crate::{
|
||||
commands::{Command, Commander, CommanderExt},
|
||||
querier::{Querier, QuerierExt},
|
||||
state::SharedState,
|
||||
};
|
||||
@ -13,11 +14,12 @@ use crate::{
|
||||
#[allow(dead_code)]
|
||||
pub struct Server {
|
||||
querier: Querier,
|
||||
commander: Commander,
|
||||
}
|
||||
|
||||
impl Server {
|
||||
pub fn new(querier: Querier) -> Self {
|
||||
Self { querier }
|
||||
pub fn new(querier: Querier, commander: Commander) -> Self {
|
||||
Self { querier, commander }
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,8 +74,85 @@ impl Graph for Server {
|
||||
let req = request.into_inner();
|
||||
tracing::trace!("create section: 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(),
|
||||
));
|
||||
}
|
||||
|
||||
if req
|
||||
.path
|
||||
.iter()
|
||||
.filter(|item| item.is_empty())
|
||||
.collect::<Vec<_>>()
|
||||
.first()
|
||||
.is_some()
|
||||
{
|
||||
return Err(tonic::Status::new(
|
||||
tonic::Code::InvalidArgument,
|
||||
"path cannot contain empty paths".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
if req
|
||||
.path
|
||||
.iter()
|
||||
.filter(|item| item.contains("."))
|
||||
.collect::<Vec<_>>()
|
||||
.first()
|
||||
.is_some()
|
||||
{
|
||||
return Err(tonic::Status::new(
|
||||
tonic::Code::InvalidArgument,
|
||||
"path cannot contain `.`".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
self.commander
|
||||
.execute(Command::CreateSection {
|
||||
root: req.root,
|
||||
path: req.path,
|
||||
})
|
||||
.await
|
||||
.map_err(to_tonic_err)?;
|
||||
|
||||
Ok(Response::new(CreateSectionResponse {}))
|
||||
}
|
||||
|
||||
async fn create_root(
|
||||
&self,
|
||||
request: tonic::Request<CreateRootRequest>,
|
||||
) -> std::result::Result<tonic::Response<CreateRootResponse>, tonic::Status> {
|
||||
let req = request.into_inner();
|
||||
tracing::trace!("create root: req({:?})", req);
|
||||
|
||||
if req.root.is_empty() {
|
||||
return Err(tonic::Status::new(
|
||||
tonic::Code::InvalidArgument,
|
||||
"root cannot be empty".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
self.commander
|
||||
.execute(Command::CreateRoot { root: req.root })
|
||||
.await
|
||||
.map_err(to_tonic_err)?;
|
||||
|
||||
Ok(Response::new(CreateRootResponse {}))
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: create more defined protobuf categories for errors
|
||||
fn to_tonic_err(err: anyhow::Error) -> tonic::Status {
|
||||
tonic::Status::new(tonic::Code::Unknown, err.to_string())
|
||||
}
|
||||
|
||||
pub trait ServerExt {
|
||||
@ -82,7 +161,7 @@ pub trait ServerExt {
|
||||
|
||||
impl ServerExt for SharedState {
|
||||
fn grpc_server(&self) -> Server {
|
||||
Server::new(self.querier())
|
||||
Server::new(self.querier(), self.commander())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,8 @@ mod querier;
|
||||
|
||||
mod state;
|
||||
|
||||
mod services;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ServeOptions {
|
||||
pub external_http: SocketAddr,
|
||||
|
2
crates/hyperlog-server/src/services.rs
Normal file
2
crates/hyperlog-server/src/services.rs
Normal file
@ -0,0 +1,2 @@
|
||||
pub mod create_root;
|
||||
pub mod create_section;
|
38
crates/hyperlog-server/src/services/create_root.rs
Normal file
38
crates/hyperlog-server/src/services/create_root.rs
Normal file
@ -0,0 +1,38 @@
|
||||
use crate::state::SharedState;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CreateRoot {
|
||||
db: sqlx::PgPool,
|
||||
}
|
||||
|
||||
pub struct Request {
|
||||
pub root: String,
|
||||
}
|
||||
pub struct Response {}
|
||||
|
||||
impl CreateRoot {
|
||||
pub fn new(db: sqlx::PgPool) -> Self {
|
||||
Self { db }
|
||||
}
|
||||
|
||||
pub async fn execute(&self, req: Request) -> anyhow::Result<Response> {
|
||||
let root_id = uuid::Uuid::new_v4();
|
||||
sqlx::query(r#"INSERT INTO roots (id, root_name) VALUES ($1, $2)"#)
|
||||
.bind(root_id)
|
||||
.bind(req.root)
|
||||
.execute(&self.db)
|
||||
.await?;
|
||||
|
||||
Ok(Response {})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CreateRootExt {
|
||||
fn create_root_service(&self) -> CreateRoot;
|
||||
}
|
||||
|
||||
impl CreateRootExt for SharedState {
|
||||
fn create_root_service(&self) -> CreateRoot {
|
||||
CreateRoot::new(self.db.clone())
|
||||
}
|
||||
}
|
62
crates/hyperlog-server/src/services/create_section.rs
Normal file
62
crates/hyperlog-server/src/services/create_section.rs
Normal file
@ -0,0 +1,62 @@
|
||||
use hyperlog_core::log::GraphItem;
|
||||
|
||||
use crate::state::SharedState;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CreateSection {
|
||||
db: sqlx::PgPool,
|
||||
}
|
||||
|
||||
pub struct Request {
|
||||
pub root: String,
|
||||
pub path: Vec<String>,
|
||||
}
|
||||
pub struct Response {}
|
||||
|
||||
#[derive(sqlx::FromRow)]
|
||||
struct Root {
|
||||
id: uuid::Uuid,
|
||||
root_name: String,
|
||||
}
|
||||
|
||||
impl CreateSection {
|
||||
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?;
|
||||
|
||||
let node_id = uuid::Uuid::new_v4();
|
||||
sqlx::query(
|
||||
r#"
|
||||
INSERT INTO nodes
|
||||
(id, root_id, path, item_type, item_content)
|
||||
VALUES
|
||||
($1, $2, $3, $4, $5)"#,
|
||||
)
|
||||
.bind(node_id)
|
||||
.bind(root_id)
|
||||
.bind(req.path.join("."))
|
||||
.bind("SECTION".to_string())
|
||||
.bind(None::<serde_json::Value>)
|
||||
.execute(&self.db)
|
||||
.await?;
|
||||
|
||||
Ok(Response {})
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CreateSectionExt {
|
||||
fn create_section_service(&self) -> CreateSection;
|
||||
}
|
||||
|
||||
impl CreateSectionExt for SharedState {
|
||||
fn create_section_service(&self) -> CreateSection {
|
||||
CreateSection::new(self.db.clone())
|
||||
}
|
||||
}
|
@ -15,7 +15,7 @@ impl Deref for SharedState {
|
||||
}
|
||||
|
||||
pub struct State {
|
||||
pub _db: Pool<Postgres>,
|
||||
pub db: Pool<Postgres>,
|
||||
}
|
||||
|
||||
impl State {
|
||||
@ -32,6 +32,6 @@ impl State {
|
||||
|
||||
let _ = sqlx::query("SELECT 1;").fetch_one(&db).await?;
|
||||
|
||||
Ok(Self { _db: db })
|
||||
Ok(Self { db })
|
||||
}
|
||||
}
|
||||
|
@ -20,7 +20,13 @@ impl Commander {
|
||||
|
||||
match cmd.clone() {
|
||||
Command::CreateRoot { root } => {
|
||||
todo!()
|
||||
let channel = self.channel.clone();
|
||||
|
||||
let mut client = GraphClient::new(channel);
|
||||
|
||||
let request = tonic::Request::new(CreateRootRequest { root });
|
||||
let response = client.create_root(request).await?;
|
||||
let res = response.into_inner();
|
||||
//self.engine.create_root(&root)?;
|
||||
}
|
||||
Command::CreateSection { root, path } => {
|
||||
@ -28,7 +34,7 @@ impl Commander {
|
||||
|
||||
let mut client = GraphClient::new(channel);
|
||||
|
||||
let request = tonic::Request::new(CreateSectionRequest {});
|
||||
let request = tonic::Request::new(CreateSectionRequest { root, path });
|
||||
let response = client.create_section(request).await?;
|
||||
let res = response.into_inner();
|
||||
|
||||
|
@ -19,3 +19,5 @@ please:
|
||||
scripts:
|
||||
dev:
|
||||
type: shell
|
||||
install:
|
||||
type: shell
|
||||
|
5
scripts/install.sh
Executable file
5
scripts/install.sh
Executable file
@ -0,0 +1,5 @@
|
||||
#!/usr/bin/env zsh
|
||||
|
||||
set -eo pipefail
|
||||
|
||||
cargo install --path crates/hyperlog --force
|
Loading…
Reference in New Issue
Block a user