Web API, Web UI #86

Merged
projectmoon merged 37 commits from web-api into master 2021-07-15 15:04:54 +00:00
9 changed files with 994 additions and 226 deletions
Showing only changes of commit 4776b6f739 - Show all commits

921
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -4,15 +4,19 @@ version = "0.1.0"
authors = ["projectmoon <projectmoon@agnos.is>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
tonic = { git = "https://github.com/hyperium/tonic", branch = "master" }
tonic-web = { git = "https://github.com/hyperium/tonic", branch = "master" }
log = "0.4"
tracing-subscriber = "0.2"
tonic = { version = "0.4" }
prost = "0.7"
once_cell = "1.7"
tenebrous-rpc = { path = "../rpc" }
tokio-stream = { version = "0.1", features = ["net"] }
juniper = { git = "https://github.com/graphql-rust/juniper", branch = "master" }
juniper_rocket_async = { git = "https://github.com/graphql-rust/juniper", branch = "master" }
rocket = { git = "https://github.com/SergioBenitez/Rocket", branch = "master" }
warp = "0.2"
#tokio-stream = { version = "0.1", features = ["net"] } # needed for grpc-web?
[dependencies.tokio]
version = "1"
features = [ "full" ]
# [dependencies.tokio]
# version = "1"
# features = [ "full" ]

42
api/src/grpc_web.rs Normal file
View File

@ -0,0 +1,42 @@
// use std::net::SocketAddr;
// use tenebrous_rpc::protos::web_api::{
// web_api_server::{WebApi, WebApiServer},
// RoomsListReply, UserIdRequest,
// };
// use tokio::net::TcpListener;
// use tokio_stream::wrappers::TcpListenerStream;
// use tonic::{transport::Server, Request, Response, Status};
//grpc-web stuff
// struct WebApiService;
// #[tonic::async_trait]
// impl WebApi for WebApiService {
// async fn list_room(
// &self,
// request: Request<UserIdRequest>,
// ) -> Result<Response<RoomsListReply>, Status> {
// println!("Hello hopefully from a web browser");
// Ok(Response::new(RoomsListReply { rooms: vec![] }))
// }
// }
// #[tokio::main]
// pub async fn grpc_web() -> Result<(), Box<dyn std::error::Error>> {
// let addr = SocketAddr::from(([127, 0, 0, 1], 10000));
// let listener = TcpListener::bind(addr).await.expect("listener");
// let url = format!("http://{}", listener.local_addr().unwrap());
// println!("Listening at {}", url);
// let svc = tonic_web::config()
// .allow_origins(vec!["http://localhost:8000"])
// .enable(WebApiServer::new(WebApiService));
// let fut = Server::builder()
// .accept_http1(true)
// .add_service(svc)
// .serve_with_incoming(TcpListenerStream::new(listener));
// fut.await?;
// Ok(())
// }

View File

@ -1,41 +1,155 @@
use std::net::SocketAddr;
use tenebrous_rpc::protos::web_api::{
web_api_server::{WebApi, WebApiServer},
RoomsListReply, UserIdRequest,
use juniper::{
graphql_object, EmptyMutation, EmptySubscription, FieldError, FieldResult, GraphQLObject,
RootNode,
};
use tokio::net::TcpListener;
use tokio_stream::wrappers::TcpListenerStream;
use tonic::{transport::Server, Request, Response, Status};
use once_cell::sync::OnceCell;
use rocket::{response::content, Rocket, State};
use std::cell::RefCell;
use std::env;
use std::sync::{Arc, RwLock};
use tenebrous_rpc::protos::dicebot::dicebot_client::DicebotClient;
use tenebrous_rpc::protos::dicebot::GetVariableRequest;
use tonic::{metadata::MetadataValue, transport::Channel as TonicChannel, Request as TonicRequest};
use tracing_subscriber::filter::EnvFilter;
struct WebApiService;
//grpc stuff
async fn create_client(
shared_secret: &str,
) -> Result<DicebotClient<TonicChannel>, Box<dyn std::error::Error>> {
let channel = TonicChannel::from_static("http://0.0.0.0:9090")
.connect()
.await?;
#[tonic::async_trait]
impl WebApi for WebApiService {
async fn list_room(
&self,
request: Request<UserIdRequest>,
) -> Result<Response<RoomsListReply>, Status> {
println!("Hello hopefully from a web browser");
Ok(Response::new(RoomsListReply { rooms: vec![] }))
let bearer = MetadataValue::from_str(&format!("Bearer {}", shared_secret))?;
let client = DicebotClient::with_interceptor(channel, move |mut req: TonicRequest<()>| {
req.metadata_mut().insert("authorization", bearer.clone());
Ok(req)
});
Ok(client)
}
//api stuff
#[derive(GraphQLObject)]
#[graphql(description = "User variable in a room.")]
struct UserVariable {
room_id: String,
variable_name: String,
value: i32,
}
//graphql shit
#[derive(Clone)]
struct Context {
dicebot_client: DicebotClient<TonicChannel>,
}
// To make our context usable by Juniper, we have to implement a marker trait.
impl juniper::Context for Context {}
#[derive(Clone, Copy, Debug)]
struct Query;
#[graphql_object(
// Here we specify the context type for the object.
// We need to do this in every type that
// needs access to the context.
context = Context,
)]
impl Query {
fn apiVersion() -> &str {
"1.0"
}
async fn variable(
context: &mut Context,
room_id: String,
user_id: String,
variable: String,
) -> FieldResult<UserVariable> {
let request = TonicRequest::new(GetVariableRequest {
room_id: room_id.clone(),
user_id: user_id.clone(),
variable_name: variable.clone(),
});
let response = context
.dicebot_client
.clone()
.get_variable(request)
.await?
.into_inner();
Ok(UserVariable {
room_id: room_id.clone(),
variable_name: variable.clone(),
value: response.value,
})
}
}
#[tokio::main]
type Schema = RootNode<'static, Query, EmptyMutation<Context>, EmptySubscription<Context>>;
fn schema() -> Schema {
Schema::new(
Query,
EmptyMutation::<Context>::new(),
EmptySubscription::<Context>::new(),
)
}
//rocket stuff
#[rocket::get("/")]
fn graphiql() -> content::Html<String> {
juniper_rocket_async::graphiql_source("/graphql", None)
}
#[rocket::get("/graphql?<request>")]
fn get_graphql_handler(
context: &State<Context>,
request: juniper_rocket_async::GraphQLRequest,
schema: &State<Schema>,
) -> juniper_rocket_async::GraphQLResponse {
request.execute_sync(&*schema, &*context)
}
#[rocket::post("/graphql", data = "<request>")]
fn post_graphql_handler(
context: &State<Context>,
request: juniper_rocket_async::GraphQLRequest,
schema: &State<Schema>,
) -> juniper_rocket_async::GraphQLResponse {
request.execute_sync(&*schema, &*context)
}
#[rocket::main]
pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
let addr = SocketAddr::from(([127, 0, 0, 1], 10000));
let listener = TcpListener::bind(addr).await.expect("listener");
let url = format!("http://{}", listener.local_addr().unwrap());
println!("Listening at {}", url);
let filter = if env::var("RUST_LOG").is_ok() {
EnvFilter::from_default_env()
} else {
EnvFilter::new("warp_async")
};
let svc = tonic_web::config()
.allow_origins(vec!["http://localhost:8000"])
.enable(WebApiServer::new(WebApiService));
tracing_subscriber::fmt().with_env_filter(filter).init();
let fut = Server::builder()
.accept_http1(true)
.add_service(svc)
.serve_with_incoming(TcpListenerStream::new(listener));
let log = warp::log("warp_server");
let client = create_client("abc123").await?;
fut.await?;
log::info!("Listening on 127.0.0.1:8080");
let context = Context {
dicebot_client: client,
};
Rocket::build()
.manage(client)
.manage(schema())
.mount(
"/",
rocket::routes![graphiql, get_graphql_handler, post_graphql_handler],
)
.launch()
.await
.expect("server to launch");
Ok(())
}

View File

@ -36,7 +36,7 @@ barrel = { version = "0.6", features = ["sqlite3"] }
tempfile = "3"
substring = "1.4"
fuse-rust = "0.2"
tonic = { git = "https://github.com/hyperium/tonic", branch = "master" }
tonic = { version = "0.4" }
prost = "0.7"
tenebrous-rpc = { path = "../rpc" }

View File

@ -10,12 +10,10 @@ async fn create_client(
.await?;
let bearer = MetadataValue::from_str(&format!("Bearer {}", shared_secret))?;
let client = DicebotClient::new(channel);
// let client = DicebotClient::with_interceptor(channel, move |mut req: Request<()>| {
// req.metadata_mut().insert("authorization", bearer.clone());
// Ok(req)
// });
let client = DicebotClient::with_interceptor(channel, move |mut req: Request<()>| {
req.metadata_mut().insert("authorization", bearer.clone());
Ok(req)
});
Ok(client)
}

View File

@ -13,8 +13,8 @@ default = ["tonic/default", "tonic-build/default"]
wasm = [ "tonic/codegen", "tonic/prost", "tonic-build/prost"]
[build-dependencies]
tonic-build = { git = "https://github.com/hyperium/tonic", branch = "master", default_features = false }
tonic-build = { version = "0.4", default_features = false }
[dependencies]
tonic = { git = "https://github.com/hyperium/tonic", branch = "master", default_features = false }
tonic = { version = "0.4", default_features = false }
prost = "0.7"

View File

@ -9,21 +9,18 @@ edition = "2018"
[lib]
crate-type = ["cdylib", "rlib"]
[build-dependencies]
tonic-build = { version = "0.4", default-features = false, features = ["prost"] }
[dependencies]
yew = { version = "0.17", default_features = false }#features = [ "agent", "web-sys" ] }
yew = { version = "0.17" }
yewtil = {version = "0.3" }
yew-router = {version = "0.14", default_features = false }
yewdux = {version = "^0.6", default_features = false }
wasm-bindgen = { version = "0.2", default-features = false, features = ["serde-serialize"] }
yew-router = {version = "0.14" }
yewdux = {version = "^0.6" }
wasm-bindgen = { version = "0.2" }
grpc-web-client = "0.1"
prost = { version = "0.7.0", default-features = false }
tonic = { git = "https://github.com/hyperium/tonic", branch = "master", default-features = false, features = ["codegen", "prost"] }
tenebrous-rpc = { path = "../../rpc", default_features = false, features = ["wasm"] }
# [dependencies.tokio]
# version = "1"
# features = [ "rt", "sync" ]
# hopefully we can add grpc-web later instead of graphql.
# prost = { version = "0.7.0", default-features = false }
# tonic = { git = "https://github.com/hyperium/tonic", branch = "master", default-features = false, features = ["codegen", "prost"] }
# tenebrous-rpc = { path = "../../rpc", default_features = false, features = ["wasm"] }
# [build-dependencies]
# tonic-build = { version = "0.4", default-features = false, features = ["prost"] }

View File

@ -1,15 +1,15 @@
async fn test_grpc_web() {
use grpc_web_client::Client as GrpcWebClient;
use tenebrous_rpc::protos::web_api::web_api_client::WebApiClient as TheCloud;
use tenebrous_rpc::protos::web_api::{RoomsListReply, UserIdRequest};
// async fn test_grpc_web() {
// use grpc_web_client::Client as GrpcWebClient;
// use tenebrous_rpc::protos::web_api::web_api_client::WebApiClient as TheCloud;
// use tenebrous_rpc::protos::web_api::{RoomsListReply, UserIdRequest};
let client = GrpcWebClient::new("http://localhost:10000".to_string());
let mut client = TheCloud::new(client);
// let client = GrpcWebClient::new("http://localhost:10000".to_string());
// let mut client = TheCloud::new(client);
let request = tonic::Request::new(UserIdRequest {
user_id: "WebTonic".into(),
});
// let request = tonic::Request::new(UserIdRequest {
// user_id: "WebTonic".into(),
// });
let response = client.list_room(request).await.unwrap().into_inner();
println!("Room reply: {:?}", response);
}
// let response = client.list_room(request).await.unwrap().into_inner();
// println!("Room reply: {:?}", response);
// }