diff --git a/lib/src/sql/value/value.rs b/lib/src/sql/value/value.rs index f8c5b608..2899618b 100644 --- a/lib/src/sql/value/value.rs +++ b/lib/src/sql/value/value.rs @@ -571,6 +571,10 @@ impl Value { matches!(self, Value::Object(_)) } + pub fn is_number(&self) -> bool { + matches!(self, Value::Number(_)) + } + pub fn is_int(&self) -> bool { matches!(self, Value::Number(Number::Int(_))) } diff --git a/src/iam/signin.rs b/src/iam/signin.rs index 28da012c..2429010c 100644 --- a/src/iam/signin.rs +++ b/src/iam/signin.rs @@ -13,7 +13,7 @@ use surrealdb::sql::Value; use surrealdb::Auth; use surrealdb::Session; -pub async fn signin(session: &mut Session, vars: Object) -> Result { +pub async fn signin(session: &mut Session, vars: Object) -> Result { // Parse the specified variables let ns = vars.get("NS").or_else(|| vars.get("ns")); let db = vars.get("DB").or_else(|| vars.get("db")); @@ -26,9 +26,7 @@ pub async fn signin(session: &mut Session, vars: Object) -> Result { // Get the provided user and pass @@ -44,9 +42,7 @@ pub async fn signin(session: &mut Session, vars: Object) -> Result Err(Error::InvalidAuth), @@ -65,9 +61,7 @@ pub async fn signin(session: &mut Session, vars: Object) -> Result Err(Error::InvalidAuth), @@ -85,9 +79,7 @@ pub async fn signin(session: &mut Session, vars: Object) -> Result Err(Error::InvalidAuth), @@ -103,7 +95,7 @@ pub async fn sc( db: String, sc: String, vars: Object, -) -> Result { +) -> Result { // Get a database reference let kvs = DB.get().unwrap(); // Get local copy of options @@ -160,7 +152,7 @@ pub async fn sc( // Check the authentication token match enc { // The auth token was created successfully - Ok(tk) => Ok(tk), + Ok(tk) => Ok(tk.into()), // There was an error creating the token _ => Err(Error::InvalidAuth), } @@ -187,7 +179,7 @@ pub async fn db( db: String, user: String, pass: String, -) -> Result { +) -> Result { // Get a database reference let kvs = DB.get().unwrap(); // Create a new readonly transaction @@ -223,7 +215,7 @@ pub async fn db( // Check the authentication token match enc { // The auth token was created successfully - Ok(tk) => Ok(tk), + Ok(tk) => Ok(tk.into()), // There was an error creating the token _ => Err(Error::InvalidAuth), } @@ -242,7 +234,7 @@ pub async fn ns( ns: String, user: String, pass: String, -) -> Result { +) -> Result { // Get a database reference let kvs = DB.get().unwrap(); // Create a new readonly transaction @@ -276,7 +268,7 @@ pub async fn ns( // Check the authentication token match enc { // The auth token was created successfully - Ok(tk) => Ok(tk), + Ok(tk) => Ok(tk.into()), // There was an error creating the token _ => Err(Error::InvalidAuth), } @@ -290,14 +282,14 @@ pub async fn ns( } } -pub async fn su(session: &mut Session, user: String, pass: String) -> Result { +pub async fn su(session: &mut Session, user: String, pass: String) -> Result { // Get the config options let opts = CF.get().unwrap(); // Attempt to verify the root user if let Some(root) = &opts.pass { if user == opts.user && &pass == root { session.au = Arc::new(Auth::Kv); - return Ok(String::from("")); + return Ok(Value::None); } } // The specified user login does not exist diff --git a/src/iam/signup.rs b/src/iam/signup.rs index f4be77e9..a9999bfa 100644 --- a/src/iam/signup.rs +++ b/src/iam/signup.rs @@ -11,7 +11,7 @@ use surrealdb::sql::Value; use surrealdb::Auth; use surrealdb::Session; -pub async fn signup(session: &mut Session, vars: Object) -> Result { +pub async fn signup(session: &mut Session, vars: Object) -> Result { // Parse the specified variables let ns = vars.get("NS").or_else(|| vars.get("ns")); let db = vars.get("DB").or_else(|| vars.get("db")); @@ -24,9 +24,7 @@ pub async fn signup(session: &mut Session, vars: Object) -> Result Err(Error::InvalidAuth), } @@ -38,7 +36,7 @@ pub async fn sc( db: String, sc: String, vars: Object, -) -> Result { +) -> Result { // Get a database reference let kvs = DB.get().unwrap(); // Get local copy of options @@ -95,7 +93,7 @@ pub async fn sc( // Create the authentication token match enc { // The auth token was created successfully - Ok(tk) => Ok(tk), + Ok(tk) => Ok(tk.into()), // There was an error creating the token _ => Err(Error::InvalidAuth), } diff --git a/src/net/rpc.rs b/src/net/rpc.rs index ffec2fc9..27b2d277 100644 --- a/src/net/rpc.rs +++ b/src/net/rpc.rs @@ -9,9 +9,10 @@ use crate::net::session; use crate::net::LOG; use crate::rpc::args::Take; use crate::rpc::paths::{ID, METHOD, PARAMS}; +use crate::rpc::res; use crate::rpc::res::Failure; -use crate::rpc::res::Response; use futures::{SinkExt, StreamExt}; +use serde::Serialize; use std::collections::BTreeMap; use std::sync::Arc; use surrealdb::channel; @@ -127,24 +128,24 @@ impl Rpc { // Convert the message let str = match msg.to_str() { Ok(v) => v, - _ => return Response::failure(None, Failure::INTERNAL_ERROR).send(chn).await, + _ => return res::failure(None, Failure::INTERNAL_ERROR).send(chn).await, }; // Parse the request let req = match surrealdb::sql::json(str) { Ok(v) if v.is_some() => v, - _ => return Response::failure(None, Failure::PARSE_ERROR).send(chn).await, + _ => return res::failure(None, Failure::PARSE_ERROR).send(chn).await, }; // Fetch the 'id' argument let id = match req.pick(&*ID) { - Value::Uuid(v) => Some(v.to_raw()), - Value::Strand(v) => Some(v.to_raw()), - Value::Number(v) => Some(v.to_string()), - _ => return Response::failure(None, Failure::INVALID_REQUEST).send(chn).await, + v if v.is_uuid() || v.is_strand() || v.is_number() || v.is_none() || v.is_null() => { + Some(v) + } + _ => return res::failure(None, Failure::INVALID_REQUEST).send(chn).await, }; // Fetch the 'method' argument let method = match req.pick(&*METHOD) { Value::Strand(v) => v.to_raw(), - _ => return Response::failure(id, Failure::INVALID_REQUEST).send(chn).await, + _ => return res::failure(id, Failure::INVALID_REQUEST).send(chn).await, }; // Fetch the 'params' argument let params = match req.pick(&*PARAMS) { @@ -153,99 +154,111 @@ impl Rpc { }; // Match the method to a function let res = match &method[..] { - "ping" => Ok(Value::True), + "ping" => Ok(Value::None), "info" => match params.len() { 0 => rpc.read().await.info().await, - _ => return Response::failure(id, Failure::INVALID_PARAMS).send(chn).await, + _ => return res::failure(id, Failure::INVALID_PARAMS).send(chn).await, }, "use" => match params.take_two() { (Value::Strand(ns), Value::Strand(db)) => rpc.write().await.yuse(ns, db).await, - _ => return Response::failure(id, Failure::INVALID_PARAMS).send(chn).await, + _ => return res::failure(id, Failure::INVALID_PARAMS).send(chn).await, }, "signup" => match params.take_one() { Value::Object(v) => rpc.write().await.signup(v).await, - _ => return Response::failure(id, Failure::INVALID_PARAMS).send(chn).await, + _ => return res::failure(id, Failure::INVALID_PARAMS).send(chn).await, }, "signin" => match params.take_one() { Value::Object(v) => rpc.write().await.signin(v).await, - _ => return Response::failure(id, Failure::INVALID_PARAMS).send(chn).await, + _ => return res::failure(id, Failure::INVALID_PARAMS).send(chn).await, }, "invalidate" => match params.len() { 0 => rpc.write().await.invalidate().await, - _ => return Response::failure(id, Failure::INVALID_PARAMS).send(chn).await, + _ => return res::failure(id, Failure::INVALID_PARAMS).send(chn).await, }, "authenticate" => match params.take_one() { Value::None => rpc.write().await.invalidate().await, Value::Strand(v) => rpc.write().await.authenticate(v).await, - _ => return Response::failure(id, Failure::INVALID_PARAMS).send(chn).await, + _ => return res::failure(id, Failure::INVALID_PARAMS).send(chn).await, }, "kill" => match params.take_one() { v if v.is_uuid() => rpc.read().await.kill(v).await, - _ => return Response::failure(id, Failure::INVALID_PARAMS).send(chn).await, + _ => return res::failure(id, Failure::INVALID_PARAMS).send(chn).await, }, "live" => match params.take_one() { v if v.is_strand() => rpc.read().await.live(v).await, - _ => return Response::failure(id, Failure::INVALID_PARAMS).send(chn).await, + _ => return res::failure(id, Failure::INVALID_PARAMS).send(chn).await, }, "let" => match params.take_two() { (Value::Strand(s), v) => rpc.write().await.set(s, v).await, - _ => return Response::failure(id, Failure::INVALID_PARAMS).send(chn).await, + _ => return res::failure(id, Failure::INVALID_PARAMS).send(chn).await, }, "set" => match params.take_two() { (Value::Strand(s), v) => rpc.write().await.set(s, v).await, - _ => return Response::failure(id, Failure::INVALID_PARAMS).send(chn).await, + _ => return res::failure(id, Failure::INVALID_PARAMS).send(chn).await, }, "query" => match params.take_two() { - (Value::Strand(s), o) if o.is_none() => rpc.read().await.query(s).await, - (Value::Strand(s), Value::Object(o)) => rpc.read().await.query_with(s, o).await, - _ => return Response::failure(id, Failure::INVALID_PARAMS).send(chn).await, + (Value::Strand(s), o) if o.is_none() => { + let res = rpc.read().await.query(s).await; + return match res { + Ok(v) => res::success(id, v).send(chn).await, + Err(e) => res::failure(id, Failure::custom(e.to_string())).send(chn).await, + }; + } + (Value::Strand(s), Value::Object(o)) => { + let res = rpc.read().await.query_with(s, o).await; + return match res { + Ok(v) => res::success(id, v).send(chn).await, + Err(e) => res::failure(id, Failure::custom(e.to_string())).send(chn).await, + }; + } + _ => return res::failure(id, Failure::INVALID_PARAMS).send(chn).await, }, "select" => match params.take_one() { v if v.is_thing() => rpc.read().await.select(v).await, v if v.is_strand() => rpc.read().await.select(v).await, - _ => return Response::failure(id, Failure::INVALID_PARAMS).send(chn).await, + _ => return res::failure(id, Failure::INVALID_PARAMS).send(chn).await, }, "create" => match params.take_two() { (v, o) if v.is_thing() && o.is_none() => rpc.read().await.create(v, None).await, (v, o) if v.is_strand() && o.is_none() => rpc.read().await.create(v, None).await, (v, o) if v.is_thing() && o.is_object() => rpc.read().await.create(v, o).await, (v, o) if v.is_strand() && o.is_object() => rpc.read().await.create(v, o).await, - _ => return Response::failure(id, Failure::INVALID_PARAMS).send(chn).await, + _ => return res::failure(id, Failure::INVALID_PARAMS).send(chn).await, }, "update" => match params.take_two() { (v, o) if v.is_thing() && o.is_none() => rpc.read().await.update(v, None).await, (v, o) if v.is_strand() && o.is_none() => rpc.read().await.update(v, None).await, (v, o) if v.is_thing() && o.is_object() => rpc.read().await.update(v, o).await, (v, o) if v.is_strand() && o.is_object() => rpc.read().await.update(v, o).await, - _ => return Response::failure(id, Failure::INVALID_PARAMS).send(chn).await, + _ => return res::failure(id, Failure::INVALID_PARAMS).send(chn).await, }, "change" => match params.take_two() { (v, o) if v.is_thing() && o.is_none() => rpc.read().await.change(v, None).await, (v, o) if v.is_strand() && o.is_none() => rpc.read().await.change(v, None).await, (v, o) if v.is_thing() && o.is_object() => rpc.read().await.change(v, o).await, (v, o) if v.is_strand() && o.is_object() => rpc.read().await.change(v, o).await, - _ => return Response::failure(id, Failure::INVALID_PARAMS).send(chn).await, + _ => return res::failure(id, Failure::INVALID_PARAMS).send(chn).await, }, "modify" => match params.take_two() { (v, o) if v.is_thing() && o.is_array() => rpc.read().await.modify(v, o).await, (v, o) if v.is_strand() && o.is_array() => rpc.read().await.modify(v, o).await, - _ => return Response::failure(id, Failure::INVALID_PARAMS).send(chn).await, + _ => return res::failure(id, Failure::INVALID_PARAMS).send(chn).await, }, "delete" => match params.take_one() { v if v.is_thing() => rpc.read().await.delete(v).await, v if v.is_strand() => rpc.read().await.delete(v).await, - _ => return Response::failure(id, Failure::INVALID_PARAMS).send(chn).await, + _ => return res::failure(id, Failure::INVALID_PARAMS).send(chn).await, }, "version" => match params.len() { 0 => Ok(format!("{}-{}", PKG_NAME, *PKG_VERS).into()), - _ => return Response::failure(id, Failure::INVALID_PARAMS).send(chn).await, + _ => return res::failure(id, Failure::INVALID_PARAMS).send(chn).await, }, - _ => return Response::failure(id, Failure::METHOD_NOT_FOUND).send(chn).await, + _ => return res::failure(id, Failure::METHOD_NOT_FOUND).send(chn).await, }; // Return the final response match res { - Ok(v) => Response::success(id, v).send(chn).await, - Err(e) => Response::failure(id, Failure::custom(e.to_string())).send(chn).await, + Ok(v) => res::success(id, v).send(chn).await, + Err(e) => res::failure(id, Failure::custom(e.to_string())).send(chn).await, } } @@ -260,17 +273,11 @@ impl Rpc { } async fn signup(&mut self, vars: Object) -> Result { - crate::iam::signup::signup(&mut self.session, vars) - .await - .map(Into::into) - .map_err(Into::into) + crate::iam::signup::signup(&mut self.session, vars).await.map_err(Into::into) } async fn signin(&mut self, vars: Object) -> Result { - crate::iam::signin::signin(&mut self.session, vars) - .await - .map(Into::into) - .map_err(Into::into) + crate::iam::signin::signin(&mut self.session, vars).await.map_err(Into::into) } async fn invalidate(&mut self) -> Result { @@ -369,7 +376,7 @@ impl Rpc { // Methods for querying // ------------------------------ - async fn query(&self, sql: Strand) -> Result { + async fn query(&self, sql: Strand) -> Result { // Get a database reference let kvs = DB.get().unwrap(); // Get local copy of options @@ -378,13 +385,11 @@ impl Rpc { let var = Some(self.vars.clone()); // Execute the query on the database let res = kvs.execute(&sql, &self.session, var, opt.strict).await?; - // Extract the first query result - let res = res.into_iter().collect::>().into(); // Return the result to the client Ok(res) } - async fn query_with(&self, sql: Strand, mut vars: Object) -> Result { + async fn query_with(&self, sql: Strand, mut vars: Object) -> Result { // Get a database reference let kvs = DB.get().unwrap(); // Get local copy of options @@ -393,8 +398,6 @@ impl Rpc { let var = Some(mrg! { vars.0, &self.vars }); // Execute the query on the database let res = kvs.execute(&sql, &self.session, var, opt.strict).await?; - // Extract the first query result - let res = res.into_iter().collect::>().into(); // Return the result to the client Ok(res) } diff --git a/src/net/signin.rs b/src/net/signin.rs index 1dcfd3ba..7394facb 100644 --- a/src/net/signin.rs +++ b/src/net/signin.rs @@ -57,11 +57,11 @@ async fn handler( Ok(Value::Object(vars)) => match crate::iam::signin::signin(&mut session, vars).await { // Authentication was successful Ok(v) => match output.as_deref() { - Some("application/json") => Ok(output::json(&Success::new(v))), - Some("application/cbor") => Ok(output::cbor(&Success::new(v))), - Some("application/msgpack") => Ok(output::pack(&Success::new(v))), - Some("text/plain") => Ok(output::text(v)), - None => Ok(output::text(v)), + Some("application/json") => Ok(output::json(&Success::new(v.as_string()))), + Some("application/cbor") => Ok(output::cbor(&Success::new(v.as_string()))), + Some("application/msgpack") => Ok(output::pack(&Success::new(v.as_string()))), + Some("text/plain") => Ok(output::text(v.as_string())), + None => Ok(output::text(v.as_string())), // An incorrect content-type was requested _ => Err(warp::reject::custom(Error::InvalidType)), }, diff --git a/src/net/signup.rs b/src/net/signup.rs index 5f79e95d..0d8f9517 100644 --- a/src/net/signup.rs +++ b/src/net/signup.rs @@ -57,11 +57,11 @@ async fn handler( Ok(Value::Object(vars)) => match crate::iam::signup::signup(&mut session, vars).await { // Authentication was successful Ok(v) => match output.as_deref() { - Some("application/json") => Ok(output::json(&Success::new(v))), - Some("application/cbor") => Ok(output::cbor(&Success::new(v))), - Some("application/msgpack") => Ok(output::pack(&Success::new(v))), - Some("text/plain") => Ok(output::text(v)), - None => Ok(output::text(v)), + Some("application/json") => Ok(output::json(&Success::new(v.as_string()))), + Some("application/cbor") => Ok(output::cbor(&Success::new(v.as_string()))), + Some("application/msgpack") => Ok(output::pack(&Success::new(v.as_string()))), + Some("text/plain") => Ok(output::text(v.as_string())), + None => Ok(output::text(v.as_string())), // An incorrect content-type was requested _ => Err(warp::reject::custom(Error::InvalidType)), }, diff --git a/src/rpc/res.rs b/src/rpc/res.rs index 47198736..64f381d1 100644 --- a/src/rpc/res.rs +++ b/src/rpc/res.rs @@ -5,41 +5,27 @@ use surrealdb::sql::Value; use warp::ws::Message; #[derive(Serialize)] -enum Content { +enum Content { #[serde(rename = "result")] - Success(Value), + Success(T), #[serde(rename = "error")] Failure(Failure), } #[derive(Serialize)] -pub struct Response { - id: Option, +pub struct Response { + id: Option, #[serde(flatten)] - content: Content, + content: Content, } -impl Response { +impl Response { // Send the response to the channel pub async fn send(self, chn: Sender) { let res = serde_json::to_string(&self).unwrap(); let res = Message::text(res); let _ = chn.send(res).await; } - // Create a JSON RPC result response - pub fn success(id: Option, val: Value) -> Response { - Response { - id, - content: Content::Success(val), - } - } - // Create a JSON RPC failure response - pub fn failure(id: Option, err: Failure) -> Response { - Response { - id, - content: Content::Failure(err), - } - } } #[derive(Clone, Debug, Serialize)] @@ -84,3 +70,19 @@ impl Failure { } } } + +// Create a JSON RPC result response +pub fn success(id: Option, val: S) -> Response { + Response { + id, + content: Content::Success(val), + } +} + +// Create a JSON RPC failure response +pub fn failure(id: Option, err: Failure) -> Response { + Response { + id, + content: Content::Failure(err), + } +}