Improve RPC method return types (#1384)

This commit is contained in:
Rushmore Mushambi 2022-10-20 00:54:41 +02:00 committed by GitHub
parent c098fe3380
commit f0eaf2bd19
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 102 additions and 103 deletions

View file

@ -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(_)))
}

View file

@ -13,7 +13,7 @@ use surrealdb::sql::Value;
use surrealdb::Auth;
use surrealdb::Session;
pub async fn signin(session: &mut Session, vars: Object) -> Result<String, Error> {
pub async fn signin(session: &mut Session, vars: Object) -> Result<Value, Error> {
// 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<String, Error
let db = db.to_strand().as_string();
let sc = sc.to_strand().as_string();
// Attempt to signin to specified scope
let res = super::signin::sc(session, ns, db, sc, vars).await?;
// Return the result to the client
Ok(res)
super::signin::sc(session, ns, db, sc, vars).await
}
(Some(ns), Some(db), None) => {
// Get the provided user and pass
@ -44,9 +42,7 @@ pub async fn signin(session: &mut Session, vars: Object) -> Result<String, Error
let user = user.to_strand().as_string();
let pass = pass.to_strand().as_string();
// Attempt to signin to database
let res = super::signin::db(session, ns, db, user, pass).await?;
// Return the result to the client
Ok(res)
super::signin::db(session, ns, db, user, pass).await
}
// There is no username or password
_ => Err(Error::InvalidAuth),
@ -65,9 +61,7 @@ pub async fn signin(session: &mut Session, vars: Object) -> Result<String, Error
let user = user.to_strand().as_string();
let pass = pass.to_strand().as_string();
// Attempt to signin to namespace
let res = super::signin::ns(session, ns, user, pass).await?;
// Return the result to the client
Ok(res)
super::signin::ns(session, ns, user, pass).await
}
// There is no username or password
_ => Err(Error::InvalidAuth),
@ -85,9 +79,7 @@ pub async fn signin(session: &mut Session, vars: Object) -> Result<String, Error
let user = user.to_strand().as_string();
let pass = pass.to_strand().as_string();
// Attempt to signin to namespace
let res = super::signin::su(session, user, pass).await?;
// Return the result to the client
Ok(res)
super::signin::su(session, user, pass).await
}
// There is no username or password
_ => Err(Error::InvalidAuth),
@ -103,7 +95,7 @@ pub async fn sc(
db: String,
sc: String,
vars: Object,
) -> Result<String, Error> {
) -> Result<Value, Error> {
// 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<String, Error> {
) -> Result<Value, Error> {
// 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<String, Error> {
) -> Result<Value, Error> {
// 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<String, Error> {
pub async fn su(session: &mut Session, user: String, pass: String) -> Result<Value, Error> {
// 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

View file

@ -11,7 +11,7 @@ use surrealdb::sql::Value;
use surrealdb::Auth;
use surrealdb::Session;
pub async fn signup(session: &mut Session, vars: Object) -> Result<String, Error> {
pub async fn signup(session: &mut Session, vars: Object) -> Result<Value, Error> {
// 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<String, Error
let db = db.to_strand().as_string();
let sc = sc.to_strand().as_string();
// Attempt to signin to specified scope
let res = super::signup::sc(session, ns, db, sc, vars).await?;
// Return the result to the client
Ok(res)
super::signup::sc(session, ns, db, sc, vars).await
}
_ => Err(Error::InvalidAuth),
}
@ -38,7 +36,7 @@ pub async fn sc(
db: String,
sc: String,
vars: Object,
) -> Result<String, Error> {
) -> Result<Value, Error> {
// 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),
}

View file

@ -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<Value, Error> {
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<Value, Error> {
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<Value, Error> {
@ -369,7 +376,7 @@ impl Rpc {
// Methods for querying
// ------------------------------
async fn query(&self, sql: Strand) -> Result<Value, Error> {
async fn query(&self, sql: Strand) -> Result<impl Serialize, Error> {
// 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::<Vec<Value>>().into();
// Return the result to the client
Ok(res)
}
async fn query_with(&self, sql: Strand, mut vars: Object) -> Result<Value, Error> {
async fn query_with(&self, sql: Strand, mut vars: Object) -> Result<impl Serialize, Error> {
// 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::<Vec<Value>>().into();
// Return the result to the client
Ok(res)
}

View file

@ -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)),
},

View file

@ -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)),
},

View file

@ -5,41 +5,27 @@ use surrealdb::sql::Value;
use warp::ws::Message;
#[derive(Serialize)]
enum Content {
enum Content<T> {
#[serde(rename = "result")]
Success(Value),
Success(T),
#[serde(rename = "error")]
Failure(Failure),
}
#[derive(Serialize)]
pub struct Response {
id: Option<String>,
pub struct Response<T> {
id: Option<Value>,
#[serde(flatten)]
content: Content,
content: Content<T>,
}
impl Response {
impl<T: Serialize> Response<T> {
// Send the response to the channel
pub async fn send(self, chn: Sender<Message>) {
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<String>, val: Value) -> Response {
Response {
id,
content: Content::Success(val),
}
}
// Create a JSON RPC failure response
pub fn failure(id: Option<String>, 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<S: Serialize>(id: Option<Value>, val: S) -> Response<S> {
Response {
id,
content: Content::Success(val),
}
}
// Create a JSON RPC failure response
pub fn failure(id: Option<Value>, err: Failure) -> Response<Value> {
Response {
id,
content: Content::Failure(err),
}
}