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 {
|
service Graph {
|
||||||
|
// Commands
|
||||||
|
rpc CreateSection(CreateSectionRequest) returns (CreateSectionResponse);
|
||||||
|
rpc CreateRoot(CreateRootRequest) returns (CreateRootResponse);
|
||||||
|
|
||||||
|
// Queriers
|
||||||
rpc GetAvailableRoots(GetAvailableRootsRequest) returns (GetAvailableRootsResponse);
|
rpc GetAvailableRoots(GetAvailableRootsRequest) returns (GetAvailableRootsResponse);
|
||||||
rpc Get(GetRequest) returns (GetReply);
|
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 GetAvailableRootsRequest {}
|
||||||
message GetAvailableRootsResponse {
|
message GetAvailableRootsResponse {
|
||||||
repeated string roots = 1;
|
repeated string roots = 1;
|
||||||
@ -50,8 +68,3 @@ message GetRequest {
|
|||||||
message GetReply {
|
message GetReply {
|
||||||
GraphItem item = 1;
|
GraphItem item = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
message CreateSectionRequest {}
|
|
||||||
message CreateSectionResponse {}
|
|
||||||
|
|
||||||
|
@ -1 +1,14 @@
|
|||||||
-- Add migration script here
|
-- 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 hyperlog_core::log::{GraphItem, ItemState};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
services::{
|
||||||
|
create_root::{self, CreateRoot, CreateRootExt},
|
||||||
|
create_section::{self, CreateSection, CreateSectionExt},
|
||||||
|
},
|
||||||
|
state::SharedState,
|
||||||
|
};
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub enum Command {
|
pub enum Command {
|
||||||
CreateRoot {
|
CreateRoot {
|
||||||
@ -35,14 +43,35 @@ pub enum Command {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub struct Commander {}
|
pub struct Commander {
|
||||||
|
create_root: CreateRoot,
|
||||||
|
create_section: CreateSection,
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(dead_code, unused_variables)]
|
|
||||||
impl Commander {
|
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 {
|
match cmd {
|
||||||
Command::CreateRoot { root } => todo!(),
|
Command::CreateRoot { root } => {
|
||||||
Command::CreateSection { root, path } => todo!(),
|
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 {
|
Command::CreateItem {
|
||||||
root,
|
root,
|
||||||
path,
|
path,
|
||||||
@ -61,38 +90,14 @@ impl Commander {
|
|||||||
Command::Move { root, src, dest } => todo!(),
|
Command::Move { root, src, dest } => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn create_root(&self, root: &str) -> anyhow::Result<()> {
|
pub trait CommanderExt {
|
||||||
todo!()
|
fn commander(&self) -> Commander;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn create(&self, root: &str, path: &[&str], item: GraphItem) -> anyhow::Result<()> {
|
impl CommanderExt for SharedState {
|
||||||
todo!()
|
fn commander(&self) -> Commander {
|
||||||
}
|
Commander::new(self.create_root_service(), self.create_section_service())
|
||||||
|
|
||||||
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!()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ use std::{collections::HashMap, net::SocketAddr};
|
|||||||
use tonic::{transport, Response};
|
use tonic::{transport, Response};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
commands::{Command, Commander, CommanderExt},
|
||||||
querier::{Querier, QuerierExt},
|
querier::{Querier, QuerierExt},
|
||||||
state::SharedState,
|
state::SharedState,
|
||||||
};
|
};
|
||||||
@ -13,11 +14,12 @@ use crate::{
|
|||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub struct Server {
|
pub struct Server {
|
||||||
querier: Querier,
|
querier: Querier,
|
||||||
|
commander: Commander,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Server {
|
impl Server {
|
||||||
pub fn new(querier: Querier) -> Self {
|
pub fn new(querier: Querier, commander: Commander) -> Self {
|
||||||
Self { querier }
|
Self { querier, commander }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,8 +74,85 @@ impl Graph for Server {
|
|||||||
let req = request.into_inner();
|
let req = request.into_inner();
|
||||||
tracing::trace!("create section: req({:?})", req);
|
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 {}))
|
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 {
|
pub trait ServerExt {
|
||||||
@ -82,7 +161,7 @@ pub trait ServerExt {
|
|||||||
|
|
||||||
impl ServerExt for SharedState {
|
impl ServerExt for SharedState {
|
||||||
fn grpc_server(&self) -> Server {
|
fn grpc_server(&self) -> Server {
|
||||||
Server::new(self.querier())
|
Server::new(self.querier(), self.commander())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,8 @@ mod querier;
|
|||||||
|
|
||||||
mod state;
|
mod state;
|
||||||
|
|
||||||
|
mod services;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct ServeOptions {
|
pub struct ServeOptions {
|
||||||
pub external_http: SocketAddr,
|
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 struct State {
|
||||||
pub _db: Pool<Postgres>,
|
pub db: Pool<Postgres>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
@ -32,6 +32,6 @@ impl State {
|
|||||||
|
|
||||||
let _ = sqlx::query("SELECT 1;").fetch_one(&db).await?;
|
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() {
|
match cmd.clone() {
|
||||||
Command::CreateRoot { root } => {
|
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)?;
|
//self.engine.create_root(&root)?;
|
||||||
}
|
}
|
||||||
Command::CreateSection { root, path } => {
|
Command::CreateSection { root, path } => {
|
||||||
@ -28,7 +34,7 @@ impl Commander {
|
|||||||
|
|
||||||
let mut client = GraphClient::new(channel);
|
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 response = client.create_section(request).await?;
|
||||||
let res = response.into_inner();
|
let res = response.into_inner();
|
||||||
|
|
||||||
|
@ -19,3 +19,5 @@ please:
|
|||||||
scripts:
|
scripts:
|
||||||
dev:
|
dev:
|
||||||
type: shell
|
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