feat: try views and seaography
This commit is contained in:
79
apps/recorder/src/graphql/infra/custom.rs
Normal file
79
apps/recorder/src/graphql/infra/custom.rs
Normal file
@@ -0,0 +1,79 @@
|
||||
use std::{pin::Pin, sync::Arc};
|
||||
|
||||
use async_graphql::dynamic::{
|
||||
Field, FieldFuture, InputValue, ResolverContext, TypeRef, ValueAccessor,
|
||||
};
|
||||
use sea_orm::EntityTrait;
|
||||
use seaography::{
|
||||
BuilderContext, EntityDeleteMutationBuilder, EntityObjectBuilder, FilterInputBuilder,
|
||||
GuardAction,
|
||||
};
|
||||
|
||||
use crate::{app::AppContextTrait, errors::RecorderResult};
|
||||
|
||||
pub type DeleteMutationFn = Arc<
|
||||
dyn Fn(
|
||||
&ResolverContext<'_>,
|
||||
Arc<dyn AppContextTrait>,
|
||||
Option<ValueAccessor<'_>>,
|
||||
) -> Pin<Box<dyn Future<Output = RecorderResult<Option<i32>>> + Send>>
|
||||
+ Send
|
||||
+ Sync,
|
||||
>;
|
||||
|
||||
pub fn generate_custom_entity_delete_mutation_field<T>(
|
||||
builder_context: &'static BuilderContext,
|
||||
mutation_fn: DeleteMutationFn,
|
||||
) -> Field
|
||||
where
|
||||
T: EntityTrait,
|
||||
<T as EntityTrait>::Model: Sync,
|
||||
{
|
||||
let entity_filter_input_builder = FilterInputBuilder {
|
||||
context: builder_context,
|
||||
};
|
||||
let entity_object_builder = EntityObjectBuilder {
|
||||
context: builder_context,
|
||||
};
|
||||
let entity_delete_mutation_builder = EntityDeleteMutationBuilder {
|
||||
context: builder_context,
|
||||
};
|
||||
let object_name: String = entity_object_builder.type_name::<T>();
|
||||
|
||||
let context = builder_context;
|
||||
|
||||
let guard = builder_context.guards.entity_guards.get(&object_name);
|
||||
|
||||
Field::new(
|
||||
entity_delete_mutation_builder.type_name::<T>(),
|
||||
TypeRef::named_nn(TypeRef::INT),
|
||||
move |ctx| {
|
||||
let mutation_fn = mutation_fn.clone();
|
||||
FieldFuture::new(async move {
|
||||
let guard_flag = if let Some(guard) = guard {
|
||||
(*guard)(&ctx)
|
||||
} else {
|
||||
GuardAction::Allow
|
||||
};
|
||||
|
||||
if let GuardAction::Block(reason) = guard_flag {
|
||||
return Err::<Option<_>, async_graphql::Error>(async_graphql::Error::new(
|
||||
reason.unwrap_or("Entity guard triggered.".into()),
|
||||
));
|
||||
}
|
||||
|
||||
let app_ctx = ctx.data::<Arc<dyn AppContextTrait>>()?;
|
||||
|
||||
let filters = ctx.args.get(&context.entity_delete_mutation.filter_field);
|
||||
|
||||
let result = mutation_fn(&ctx, app_ctx.clone(), filters).await?;
|
||||
|
||||
Ok(result.map(async_graphql::Value::from))
|
||||
})
|
||||
},
|
||||
)
|
||||
.argument(InputValue::new(
|
||||
&context.entity_delete_mutation.filter_field,
|
||||
TypeRef::named(entity_filter_input_builder.type_name(&object_name)),
|
||||
))
|
||||
}
|
||||
@@ -3,6 +3,7 @@ use async_graphql::{
|
||||
dynamic::{ResolverContext, Scalar, SchemaError},
|
||||
to_value,
|
||||
};
|
||||
use convert_case::Case;
|
||||
use itertools::Itertools;
|
||||
use rust_decimal::{Decimal, prelude::FromPrimitive};
|
||||
use sea_orm::{
|
||||
@@ -12,9 +13,13 @@ use sea_orm::{
|
||||
use seaography::{
|
||||
Builder as SeaographyBuilder, BuilderContext, FilterType, FnFilterCondition, SeaographyError,
|
||||
};
|
||||
use serde::{Serialize, de::DeserializeOwned};
|
||||
use serde_json::Value as JsonValue;
|
||||
|
||||
use crate::{errors::RecorderResult, graphql::infra::util::get_entity_column_key};
|
||||
use crate::{
|
||||
errors::RecorderResult, graphql::infra::util::get_entity_column_key,
|
||||
infra::json::convert_json_keys,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Copy)]
|
||||
pub enum JsonbFilterOperation {
|
||||
@@ -948,6 +953,64 @@ where
|
||||
);
|
||||
}
|
||||
|
||||
pub fn validate_jsonb_input_for_entity<T, S>(context: &mut BuilderContext, column: &T::Column)
|
||||
where
|
||||
T: EntityTrait,
|
||||
<T as EntityTrait>::Model: Sync,
|
||||
S: DeserializeOwned + Serialize,
|
||||
{
|
||||
let entity_column_key = get_entity_column_key::<T>(context, column);
|
||||
context.types.input_conversions.insert(
|
||||
entity_column_key.clone(),
|
||||
Box::new(move |_resolve_context, accessor| {
|
||||
let deserialized = accessor.deserialize::<S>().map_err(|err| {
|
||||
SeaographyError::TypeConversionError(
|
||||
err.message,
|
||||
format!("Json - {entity_column_key}"),
|
||||
)
|
||||
})?;
|
||||
let json_value = serde_json::to_value(deserialized).map_err(|err| {
|
||||
SeaographyError::TypeConversionError(
|
||||
err.to_string(),
|
||||
format!("Json - {entity_column_key}"),
|
||||
)
|
||||
})?;
|
||||
Ok(sea_orm::Value::Json(Some(Box::new(json_value))))
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn convert_jsonb_output_case_for_entity<T>(context: &mut BuilderContext, column: &T::Column)
|
||||
where
|
||||
T: EntityTrait,
|
||||
<T as EntityTrait>::Model: Sync,
|
||||
{
|
||||
let entity_column_key = get_entity_column_key::<T>(context, column);
|
||||
context.types.output_conversions.insert(
|
||||
entity_column_key.clone(),
|
||||
Box::new(move |value| {
|
||||
if let sea_orm::Value::Json(Some(json)) = value {
|
||||
let result = async_graphql::Value::from_json(convert_json_keys(
|
||||
json.as_ref().clone(),
|
||||
Case::Camel,
|
||||
))
|
||||
.map_err(|err| {
|
||||
SeaographyError::TypeConversionError(
|
||||
err.to_string(),
|
||||
format!("Json - {entity_column_key}"),
|
||||
)
|
||||
})?;
|
||||
Ok(result)
|
||||
} else {
|
||||
Err(SeaographyError::TypeConversionError(
|
||||
"value should be json".to_string(),
|
||||
format!("Json - {entity_column_key}"),
|
||||
))
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::assert_matches::assert_matches;
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
pub mod custom;
|
||||
pub mod json;
|
||||
pub mod util;
|
||||
|
||||
Reference in New Issue
Block a user