feat: refactor tasks
This commit is contained in:
@@ -1,12 +1,17 @@
|
||||
use async_graphql::dynamic::SchemaError;
|
||||
use async_graphql::{
|
||||
Error as GraphqlError, InputValueResult, Scalar, ScalarType, dynamic::SchemaError, to_value,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use once_cell::sync::OnceCell;
|
||||
use rust_decimal::{Decimal, prelude::FromPrimitive};
|
||||
use sea_orm::{
|
||||
Condition,
|
||||
Condition, EntityTrait,
|
||||
sea_query::{ArrayType, Expr, ExprTrait, IntoLikeExpr, SimpleExpr, Value as DbValue},
|
||||
};
|
||||
use seaography::{BuilderContext, FilterInfo, SeaographyError};
|
||||
use serde_json::Value as JsonValue;
|
||||
|
||||
use super::subscriber::FnFilterCondition;
|
||||
use crate::errors::RecorderResult;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Copy)]
|
||||
@@ -317,7 +322,7 @@ fn json_path_type_assert_expr(
|
||||
typestr: &str,
|
||||
) -> SimpleExpr {
|
||||
Expr::cust_with_exprs(
|
||||
format!("jsonb_path_exists($1, $2 || ' ? (@.type = \"{typestr}\")')"),
|
||||
format!("jsonb_path_exists($1, $2 || ' ? (@.type() = \"{typestr}\")')"),
|
||||
[col_expr.into(), json_path_expr(path)],
|
||||
)
|
||||
}
|
||||
@@ -767,8 +772,7 @@ where
|
||||
.map(|(i, v)| (JsonIndex::Num(i as u64), v))
|
||||
.collect(),
|
||||
_ => Err(SchemaError(format!(
|
||||
"Json filter input node must be an object or array, but got {}",
|
||||
node.to_string()
|
||||
"Json filter input node must be an object or array, but got {node}"
|
||||
)))?,
|
||||
};
|
||||
let mut conditions = Condition::all();
|
||||
@@ -866,6 +870,46 @@ where
|
||||
Ok(condition)
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct JsonFilterInput(pub serde_json::Value);
|
||||
|
||||
#[Scalar(name = "JsonFilterInput")]
|
||||
impl ScalarType for JsonFilterInput {
|
||||
fn parse(value: async_graphql::Value) -> InputValueResult<Self> {
|
||||
Ok(JsonFilterInput(value.into_json()?))
|
||||
}
|
||||
|
||||
fn to_value(&self) -> async_graphql::Value {
|
||||
async_graphql::Value::from_json(self.0.clone()).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub static JSONB_FILTER_INFO: OnceCell<FilterInfo> = OnceCell::new();
|
||||
|
||||
pub fn jsonb_filter_condition_function<T>(
|
||||
_context: &BuilderContext,
|
||||
column: &T::Column,
|
||||
) -> FnFilterCondition
|
||||
where
|
||||
T: EntityTrait,
|
||||
<T as EntityTrait>::Model: Sync,
|
||||
{
|
||||
let column = *column;
|
||||
Box::new(move |mut condition, filter| {
|
||||
let filter_value = to_value(filter.as_index_map())
|
||||
.map_err(|e| SeaographyError::AsyncGraphQLError(GraphqlError::new_with_source(e)))?;
|
||||
|
||||
let filter = JsonFilterInput::parse(filter_value)
|
||||
.map_err(|e| SeaographyError::AsyncGraphQLError(GraphqlError::new(format!("{e:?}"))))?;
|
||||
|
||||
let cond_where = prepare_json_filter_input(&Expr::col(column), filter.0)
|
||||
.map_err(|e| SeaographyError::AsyncGraphQLError(GraphqlError::new_with_source(e)))?;
|
||||
|
||||
condition = condition.add(cond_where);
|
||||
Ok(condition)
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::assert_matches::assert_matches;
|
||||
@@ -965,7 +1009,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
sql,
|
||||
"SELECT \"job\" FROM \"test_table\" WHERE jsonb_path_exists(\"test_table\".\"job\", \
|
||||
$1 || ' ? (@.type = \"string\")')"
|
||||
$1 || ' ? (@.type() = \"string\")')"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -981,7 +1025,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
sql,
|
||||
"SELECT \"job\" FROM \"test_table\" WHERE (CASE WHEN \
|
||||
((jsonb_path_exists(\"test_table\".\"job\", $1 || ' ? (@.type = \"array\")')) \
|
||||
((jsonb_path_exists(\"test_table\".\"job\", $1 || ' ? (@.type() = \"array\")')) \
|
||||
AND (jsonb_path_query_first(\"test_table\".\"job\", $2) @> $3)) THEN true ELSE \
|
||||
false END) = (true)"
|
||||
);
|
||||
@@ -1000,9 +1044,9 @@ mod tests {
|
||||
assert_eq!(
|
||||
sql,
|
||||
"SELECT \"job\" FROM \"test_table\" WHERE (CASE WHEN \
|
||||
((jsonb_path_exists(\"test_table\".\"job\", $1 || ' ? (@.type = \"array\")')) \
|
||||
((jsonb_path_exists(\"test_table\".\"job\", $1 || ' ? (@.type() = \"array\")')) \
|
||||
AND (jsonb_path_query_first(\"test_table\".\"job\", $2) @> $3)) THEN true WHEN \
|
||||
((jsonb_path_exists(\"test_table\".\"job\", $4 || ' ? (@.type = \"string\")')) \
|
||||
((jsonb_path_exists(\"test_table\".\"job\", $4 || ' ? (@.type() = \"string\")')) \
|
||||
AND CAST((jsonb_path_query_first(\"test_table\".\"job\", $5)) AS text) LIKE $6) \
|
||||
THEN true ELSE false END) = (true)"
|
||||
);
|
||||
@@ -1028,7 +1072,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
sql,
|
||||
"SELECT \"job\" FROM \"test_table\" WHERE \
|
||||
(jsonb_path_exists(\"test_table\".\"job\", $1 || ' ? (@.type = \"number\")')) \
|
||||
(jsonb_path_exists(\"test_table\".\"job\", $1 || ' ? (@.type() = \"number\")')) \
|
||||
AND (CAST((jsonb_path_query_first(\"test_table\".\"job\", $2)) AS numeric) \
|
||||
BETWEEN $3 AND $4)"
|
||||
);
|
||||
@@ -1048,7 +1092,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
sql,
|
||||
"SELECT \"job\" FROM \"test_table\" WHERE \
|
||||
(jsonb_path_exists(\"test_table\".\"job\", $1 || ' ? (@.type = \"string\")')) \
|
||||
(jsonb_path_exists(\"test_table\".\"job\", $1 || ' ? (@.type() = \"string\")')) \
|
||||
AND (CAST((jsonb_path_query_first(\"test_table\".\"job\", $2)) AS text) BETWEEN \
|
||||
$3 AND $4)"
|
||||
);
|
||||
@@ -1068,7 +1112,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
sql,
|
||||
"SELECT \"job\" FROM \"test_table\" WHERE \
|
||||
(jsonb_path_exists(\"test_table\".\"job\", $1 || ' ? (@.type = \"boolean\")')) \
|
||||
(jsonb_path_exists(\"test_table\".\"job\", $1 || ' ? (@.type() = \"boolean\")')) \
|
||||
AND (CAST((jsonb_path_query_first(\"test_table\".\"job\", $2)) AS boolean) \
|
||||
BETWEEN $3 AND $4)"
|
||||
);
|
||||
@@ -1090,7 +1134,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
sql,
|
||||
"SELECT \"job\" FROM \"test_table\" WHERE (jsonb_path_exists(\"test_table\".\"job\", \
|
||||
$1 || ' ? (@.type = \"string\")')) AND \
|
||||
$1 || ' ? (@.type() = \"string\")')) AND \
|
||||
CAST((jsonb_path_query_first(\"test_table\".\"job\", $2)) AS text) LIKE $3"
|
||||
);
|
||||
assert_eq!(params.len(), 3);
|
||||
@@ -1109,7 +1153,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
sql,
|
||||
"SELECT \"job\" FROM \"test_table\" WHERE (jsonb_path_exists(\"test_table\".\"job\", \
|
||||
$1 || ' ? (@.type = \"string\")')) AND \
|
||||
$1 || ' ? (@.type() = \"string\")')) AND \
|
||||
(starts_with(CAST((jsonb_path_query_first(\"test_table\".\"job\", $2)) AS text), $3))"
|
||||
);
|
||||
assert_eq!(params.len(), 3);
|
||||
@@ -1128,7 +1172,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
sql,
|
||||
"SELECT \"job\" FROM \"test_table\" WHERE (jsonb_path_exists(\"test_table\".\"job\", \
|
||||
$1 || ' ? (@.type = \"string\")')) AND \
|
||||
$1 || ' ? (@.type() = \"string\")')) AND \
|
||||
CAST((jsonb_path_query_first(\"test_table\".\"job\", $2)) AS text) LIKE $3"
|
||||
);
|
||||
assert_eq!(params.len(), 3);
|
||||
|
||||
@@ -1,18 +1,13 @@
|
||||
mod json;
|
||||
mod subscriber;
|
||||
|
||||
use async_graphql::{
|
||||
InputValueResult, Scalar, ScalarType,
|
||||
dynamic::{ObjectAccessor, TypeRef},
|
||||
};
|
||||
pub use json::prepare_json_filter_input;
|
||||
use std::borrow::Cow;
|
||||
|
||||
use async_graphql::dynamic::TypeRef;
|
||||
pub use json::{JSONB_FILTER_INFO, jsonb_filter_condition_function};
|
||||
use maplit::btreeset;
|
||||
use once_cell::sync::OnceCell;
|
||||
use sea_orm::{ColumnTrait, Condition, EntityTrait};
|
||||
use seaography::{
|
||||
BuilderContext, FilterInfo, FilterOperation as SeaographqlFilterOperation, SeaResult,
|
||||
};
|
||||
|
||||
pub static SUBSCRIBER_ID_FILTER_INFO: OnceCell<FilterInfo> = OnceCell::new();
|
||||
use seaography::{FilterInfo, FilterOperation as SeaographqlFilterOperation};
|
||||
pub use subscriber::{SUBSCRIBER_ID_FILTER_INFO, subscriber_id_condition_function};
|
||||
|
||||
pub fn init_custom_filter_info() {
|
||||
SUBSCRIBER_ID_FILTER_INFO.get_or_init(|| FilterInfo {
|
||||
@@ -20,49 +15,9 @@ pub fn init_custom_filter_info() {
|
||||
base_type: TypeRef::INT.into(),
|
||||
supported_operations: btreeset! { SeaographqlFilterOperation::Equals },
|
||||
});
|
||||
}
|
||||
|
||||
pub type FnFilterCondition =
|
||||
Box<dyn Fn(Condition, &ObjectAccessor) -> SeaResult<Condition> + Send + Sync>;
|
||||
|
||||
pub fn subscriber_id_condition_function<T>(
|
||||
_context: &BuilderContext,
|
||||
column: &T::Column,
|
||||
) -> FnFilterCondition
|
||||
where
|
||||
T: EntityTrait,
|
||||
<T as EntityTrait>::Model: Sync,
|
||||
{
|
||||
let column = *column;
|
||||
Box::new(move |mut condition, filter| {
|
||||
let subscriber_id_filter_info = SUBSCRIBER_ID_FILTER_INFO.get().unwrap();
|
||||
let operations = &subscriber_id_filter_info.supported_operations;
|
||||
for operation in operations {
|
||||
match operation {
|
||||
SeaographqlFilterOperation::Equals => {
|
||||
if let Some(value) = filter.get("eq") {
|
||||
let value: i32 = value.i64()?.try_into()?;
|
||||
let value = sea_orm::Value::Int(Some(value));
|
||||
condition = condition.add(column.eq(value));
|
||||
}
|
||||
}
|
||||
_ => unreachable!("unreachable filter operation for subscriber_id"),
|
||||
}
|
||||
}
|
||||
Ok(condition)
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct JsonFilterInput(pub serde_json::Value);
|
||||
|
||||
#[Scalar(name = "JsonFilterInput")]
|
||||
impl ScalarType for JsonFilterInput {
|
||||
fn parse(value: async_graphql::Value) -> InputValueResult<Self> {
|
||||
Ok(JsonFilterInput(value.into_json()?))
|
||||
}
|
||||
|
||||
fn to_value(&self) -> async_graphql::Value {
|
||||
async_graphql::Value::from_json(self.0.clone()).unwrap()
|
||||
}
|
||||
JSONB_FILTER_INFO.get_or_init(|| FilterInfo {
|
||||
type_name: String::from("JsonbFilterInput"),
|
||||
base_type: TypeRef::Named(Cow::Borrowed("serde_json::Value")).to_string(),
|
||||
supported_operations: btreeset! { SeaographqlFilterOperation::Equals },
|
||||
});
|
||||
}
|
||||
|
||||
39
apps/recorder/src/graphql/infra/filter/subscriber.rs
Normal file
39
apps/recorder/src/graphql/infra/filter/subscriber.rs
Normal file
@@ -0,0 +1,39 @@
|
||||
use async_graphql::dynamic::ObjectAccessor;
|
||||
use once_cell::sync::OnceCell;
|
||||
use sea_orm::{ColumnTrait, Condition, EntityTrait};
|
||||
use seaography::{
|
||||
BuilderContext, FilterInfo, FilterOperation as SeaographqlFilterOperation, SeaResult,
|
||||
};
|
||||
|
||||
pub static SUBSCRIBER_ID_FILTER_INFO: OnceCell<FilterInfo> = OnceCell::new();
|
||||
|
||||
pub type FnFilterCondition =
|
||||
Box<dyn Fn(Condition, &ObjectAccessor) -> SeaResult<Condition> + Send + Sync>;
|
||||
|
||||
pub fn subscriber_id_condition_function<T>(
|
||||
_context: &BuilderContext,
|
||||
column: &T::Column,
|
||||
) -> FnFilterCondition
|
||||
where
|
||||
T: EntityTrait,
|
||||
<T as EntityTrait>::Model: Sync,
|
||||
{
|
||||
let column = *column;
|
||||
Box::new(move |mut condition, filter| {
|
||||
let subscriber_id_filter_info = SUBSCRIBER_ID_FILTER_INFO.get().unwrap();
|
||||
let operations = &subscriber_id_filter_info.supported_operations;
|
||||
for operation in operations {
|
||||
match operation {
|
||||
SeaographqlFilterOperation::Equals => {
|
||||
if let Some(value) = filter.get("eq") {
|
||||
let value: i32 = value.i64()?.try_into()?;
|
||||
let value = sea_orm::Value::Int(Some(value));
|
||||
condition = condition.add(column.eq(value));
|
||||
}
|
||||
}
|
||||
_ => unreachable!("unreachable filter operation for subscriber_id"),
|
||||
}
|
||||
}
|
||||
Ok(condition)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user