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>"] authors = ["projectmoon <projectmoon@agnos.is>"]
edition = "2018" edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
tonic = { git = "https://github.com/hyperium/tonic", branch = "master" } log = "0.4"
tonic-web = { git = "https://github.com/hyperium/tonic", branch = "master" } tracing-subscriber = "0.2"
tonic = { version = "0.4" }
prost = "0.7" prost = "0.7"
once_cell = "1.7"
tenebrous-rpc = { path = "../rpc" } 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] # [dependencies.tokio]
version = "1" # version = "1"
features = [ "full" ] # 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 juniper::{
use tenebrous_rpc::protos::web_api::{ graphql_object, EmptyMutation, EmptySubscription, FieldError, FieldResult, GraphQLObject,
web_api_server::{WebApi, WebApiServer}, RootNode,
RoomsListReply, UserIdRequest,
}; };
use tokio::net::TcpListener; use once_cell::sync::OnceCell;
use tokio_stream::wrappers::TcpListenerStream; use rocket::{response::content, Rocket, State};
use tonic::{transport::Server, Request, Response, Status}; 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] let bearer = MetadataValue::from_str(&format!("Bearer {}", shared_secret))?;
impl WebApi for WebApiService { let client = DicebotClient::with_interceptor(channel, move |mut req: TonicRequest<()>| {
async fn list_room( req.metadata_mut().insert("authorization", bearer.clone());
&self, Ok(req)
request: Request<UserIdRequest>, });
) -> Result<Response<RoomsListReply>, Status> {
println!("Hello hopefully from a web browser"); Ok(client)
Ok(Response::new(RoomsListReply { rooms: vec![] })) }
//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>> { pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
let addr = SocketAddr::from(([127, 0, 0, 1], 10000)); let filter = if env::var("RUST_LOG").is_ok() {
let listener = TcpListener::bind(addr).await.expect("listener"); EnvFilter::from_default_env()
let url = format!("http://{}", listener.local_addr().unwrap()); } else {
println!("Listening at {}", url); EnvFilter::new("warp_async")
};
let svc = tonic_web::config() tracing_subscriber::fmt().with_env_filter(filter).init();
.allow_origins(vec!["http://localhost:8000"])
.enable(WebApiServer::new(WebApiService));
let fut = Server::builder() let log = warp::log("warp_server");
.accept_http1(true) let client = create_client("abc123").await?;
.add_service(svc)
.serve_with_incoming(TcpListenerStream::new(listener));
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(()) Ok(())
} }

View File

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

View File

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

View File

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

View File

@ -9,21 +9,18 @@ edition = "2018"
[lib] [lib]
crate-type = ["cdylib", "rlib"] crate-type = ["cdylib", "rlib"]
[build-dependencies]
tonic-build = { version = "0.4", default-features = false, features = ["prost"] }
[dependencies] [dependencies]
yew = { version = "0.17", default_features = false }#features = [ "agent", "web-sys" ] } yew = { version = "0.17" }
yewtil = {version = "0.3" } yewtil = {version = "0.3" }
yew-router = {version = "0.14", default_features = false } yew-router = {version = "0.14" }
yewdux = {version = "^0.6", default_features = false } yewdux = {version = "^0.6" }
wasm-bindgen = { version = "0.2", default-features = false, features = ["serde-serialize"] } wasm-bindgen = { version = "0.2" }
grpc-web-client = "0.1" 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] # hopefully we can add grpc-web later instead of graphql.
# version = "1" # prost = { version = "0.7.0", default-features = false }
# features = [ "rt", "sync" ] # 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() { // async fn test_grpc_web() {
use grpc_web_client::Client as GrpcWebClient; // 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::web_api_client::WebApiClient as TheCloud;
use tenebrous_rpc::protos::web_api::{RoomsListReply, UserIdRequest}; // use tenebrous_rpc::protos::web_api::{RoomsListReply, UserIdRequest};
let client = GrpcWebClient::new("http://localhost:10000".to_string()); // let client = GrpcWebClient::new("http://localhost:10000".to_string());
let mut client = TheCloud::new(client); // let mut client = TheCloud::new(client);
let request = tonic::Request::new(UserIdRequest { // let request = tonic::Request::new(UserIdRequest {
user_id: "WebTonic".into(), // user_id: "WebTonic".into(),
}); // });
let response = client.list_room(request).await.unwrap().into_inner(); // let response = client.list_room(request).await.unwrap().into_inner();
println!("Room reply: {:?}", response); // println!("Room reply: {:?}", response);
} // }