feature: add mutation input object transformer
This commit is contained in:
parent
ee1b1ae5e6
commit
0300d7baf6
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -5632,7 +5632,7 @@ checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "seaography"
|
name = "seaography"
|
||||||
version = "1.1.4"
|
version = "1.1.4"
|
||||||
source = "git+https://github.com/lonelyhentxi/seaography.git?rev=0c4cc5c#0c4cc5ccd43730338802f98401120d5faeccd096"
|
source = "git+https://github.com/dumtruck/seaography.git?rev=10ba248#10ba2487fb356a0385c598290668a01e0ef21734"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-graphql",
|
"async-graphql",
|
||||||
"fnv",
|
"fnv",
|
||||||
|
@ -50,4 +50,4 @@ recorder = { path = "./apps/recorder" }
|
|||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
jwt-authorizer = { git = "https://github.com/blablacio/jwt-authorizer.git", rev = "e956774" }
|
jwt-authorizer = { git = "https://github.com/blablacio/jwt-authorizer.git", rev = "e956774" }
|
||||||
seaography = { git = "https://github.com/lonelyhentxi/seaography.git", rev = "0c4cc5c" }
|
seaography = { git = "https://github.com/dumtruck/seaography.git", rev = "10ba248" }
|
||||||
|
@ -97,16 +97,16 @@ where
|
|||||||
Ok(user_info) => {
|
Ok(user_info) => {
|
||||||
let subscriber_id = user_info.subscriber_auth.subscriber_id;
|
let subscriber_id = user_info.subscriber_auth.subscriber_id;
|
||||||
let validation_result = match context.field().name() {
|
let validation_result = match context.field().name() {
|
||||||
field if field == entity_create_one_mutation_field_name.as_str() => context
|
field if field == entity_create_one_mutation_field_name.as_str() => {
|
||||||
|
if let Some(data_value) = context
|
||||||
.args
|
.args
|
||||||
.try_get(&entity_create_one_mutation_data_field_name)
|
.get(&entity_create_one_mutation_data_field_name)
|
||||||
.and_then(|data_value| {
|
{
|
||||||
guard_data_object_accessor_with_subscriber_id(
|
guard_data_object_accessor_with_subscriber_id(
|
||||||
data_value,
|
data_value,
|
||||||
&column_name,
|
&column_name,
|
||||||
subscriber_id,
|
subscriber_id,
|
||||||
)
|
)
|
||||||
})
|
|
||||||
.map_err(|inner_error| {
|
.map_err(|inner_error| {
|
||||||
AuthError::from_graphql_subscribe_id_guard(
|
AuthError::from_graphql_subscribe_id_guard(
|
||||||
inner_error,
|
inner_error,
|
||||||
@ -114,21 +114,27 @@ where
|
|||||||
&entity_create_one_mutation_data_field_name,
|
&entity_create_one_mutation_data_field_name,
|
||||||
&column_name,
|
&column_name,
|
||||||
)
|
)
|
||||||
}),
|
})
|
||||||
field if field == entity_create_batch_mutation_field_name.as_str() => context
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
field if field == entity_create_batch_mutation_field_name.as_str() => {
|
||||||
|
if let Some(data_value) = context
|
||||||
.args
|
.args
|
||||||
.try_get(&entity_create_batch_mutation_data_field_name)
|
.get(&entity_create_batch_mutation_data_field_name)
|
||||||
.and_then(|data_value| {
|
{
|
||||||
data_value.list().and_then(|data_list| {
|
data_value
|
||||||
|
.list()
|
||||||
|
.and_then(|data_list| {
|
||||||
data_list.iter().try_for_each(|data_item_value| {
|
data_list.iter().try_for_each(|data_item_value| {
|
||||||
guard_data_object_accessor_with_subscriber_id(
|
guard_data_object_accessor_with_optional_subscriber_id(
|
||||||
data_item_value,
|
data_item_value,
|
||||||
&column_name,
|
&column_name,
|
||||||
subscriber_id,
|
subscriber_id,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
|
||||||
.map_err(|inner_error| {
|
.map_err(|inner_error| {
|
||||||
AuthError::from_graphql_subscribe_id_guard(
|
AuthError::from_graphql_subscribe_id_guard(
|
||||||
inner_error,
|
inner_error,
|
||||||
@ -136,10 +142,15 @@ where
|
|||||||
&entity_create_batch_mutation_data_field_name,
|
&entity_create_batch_mutation_data_field_name,
|
||||||
&column_name,
|
&column_name,
|
||||||
)
|
)
|
||||||
}),
|
})
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
field if field == entity_update_mutation_field_name.as_str() => {
|
field if field == entity_update_mutation_field_name.as_str() => {
|
||||||
match context.args.get(&entity_update_mutation_data_field_name) {
|
if let Some(data_value) =
|
||||||
Some(data_value) => {
|
context.args.get(&entity_update_mutation_data_field_name)
|
||||||
|
{
|
||||||
guard_data_object_accessor_with_optional_subscriber_id(
|
guard_data_object_accessor_with_optional_subscriber_id(
|
||||||
data_value,
|
data_value,
|
||||||
&column_name,
|
&column_name,
|
||||||
@ -153,8 +164,8 @@ where
|
|||||||
&column_name,
|
&column_name,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
} else {
|
||||||
None => Ok(()),
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => Ok(()),
|
_ => Ok(()),
|
||||||
|
@ -3,7 +3,7 @@ use once_cell::sync::OnceCell;
|
|||||||
use sea_orm::{DatabaseConnection, EntityTrait, Iterable};
|
use sea_orm::{DatabaseConnection, EntityTrait, Iterable};
|
||||||
use seaography::{Builder, BuilderContext, FilterType, FilterTypesMapHelper};
|
use seaography::{Builder, BuilderContext, FilterType, FilterTypesMapHelper};
|
||||||
|
|
||||||
use super::transformer::filter_condition_transformer;
|
use super::transformer::{filter_condition_transformer, mutation_input_object_transformer};
|
||||||
use crate::graphql::{
|
use crate::graphql::{
|
||||||
filter::{
|
filter::{
|
||||||
SUBSCRIBER_ID_FILTER_INFO, init_custom_filter_info, subscriber_id_condition_function,
|
SUBSCRIBER_ID_FILTER_INFO, init_custom_filter_info, subscriber_id_condition_function,
|
||||||
@ -48,13 +48,25 @@ where
|
|||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
context.filter_types.condition_functions.insert(
|
context.filter_types.condition_functions.insert(
|
||||||
entity_column_key,
|
entity_column_key.clone(),
|
||||||
subscriber_id_condition_function::<T>(context, column),
|
subscriber_id_condition_function::<T>(context, column),
|
||||||
);
|
);
|
||||||
context.transformers.filter_conditions_transformers.insert(
|
context.transformers.filter_conditions_transformers.insert(
|
||||||
entity_key,
|
entity_key.clone(),
|
||||||
filter_condition_transformer::<T>(context, column),
|
filter_condition_transformer::<T>(context, column),
|
||||||
);
|
);
|
||||||
|
context
|
||||||
|
.transformers
|
||||||
|
.mutation_input_object_transformers
|
||||||
|
.insert(
|
||||||
|
entity_key,
|
||||||
|
mutation_input_object_transformer::<T>(context, column),
|
||||||
|
);
|
||||||
|
context
|
||||||
|
.entity_input
|
||||||
|
.insert_skips
|
||||||
|
.push(entity_column_key.clone());
|
||||||
|
context.entity_input.update_skips.push(entity_column_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn schema(
|
pub fn schema(
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
use async_graphql::dynamic::ResolverContext;
|
use std::{collections::BTreeMap, sync::Arc};
|
||||||
use sea_orm::{ColumnTrait, Condition, EntityTrait};
|
|
||||||
use seaography::{BuilderContext, FnFilterConditionsTransformer};
|
|
||||||
|
|
||||||
|
use async_graphql::dynamic::ResolverContext;
|
||||||
|
use sea_orm::{ColumnTrait, Condition, EntityTrait, Value};
|
||||||
|
use seaography::{BuilderContext, FnFilterConditionsTransformer, FnMutationInputObjectTransformer};
|
||||||
|
|
||||||
|
use super::util::{get_column_key, get_entity_key};
|
||||||
use crate::auth::AuthUserInfo;
|
use crate::auth::AuthUserInfo;
|
||||||
|
|
||||||
pub fn filter_condition_transformer<T>(
|
pub fn filter_condition_transformer<T>(
|
||||||
@ -25,3 +28,56 @@ where
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn mutation_input_object_transformer<T>(
|
||||||
|
context: &BuilderContext,
|
||||||
|
column: &T::Column,
|
||||||
|
) -> FnMutationInputObjectTransformer
|
||||||
|
where
|
||||||
|
T: EntityTrait,
|
||||||
|
<T as EntityTrait>::Model: Sync,
|
||||||
|
{
|
||||||
|
let entity_key = get_entity_key::<T>(context);
|
||||||
|
let entity_name = context.entity_query_field.type_name.as_ref()(&entity_key);
|
||||||
|
let column_key = get_column_key::<T>(context, column);
|
||||||
|
let column_name = Arc::new(context.entity_object.column_name.as_ref()(
|
||||||
|
&entity_key,
|
||||||
|
&column_key,
|
||||||
|
));
|
||||||
|
let entity_create_one_mutation_field_name = Arc::new(format!(
|
||||||
|
"{}{}",
|
||||||
|
entity_name, context.entity_create_one_mutation.mutation_suffix
|
||||||
|
));
|
||||||
|
let entity_create_batch_mutation_field_name = Arc::new(format!(
|
||||||
|
"{}{}",
|
||||||
|
entity_name,
|
||||||
|
context.entity_create_batch_mutation.mutation_suffix.clone()
|
||||||
|
));
|
||||||
|
Box::new(
|
||||||
|
move |context: &ResolverContext,
|
||||||
|
mut input: BTreeMap<String, Value>|
|
||||||
|
-> BTreeMap<String, Value> {
|
||||||
|
let field_name = context.field().name();
|
||||||
|
if field_name == entity_create_one_mutation_field_name.as_str()
|
||||||
|
|| field_name == entity_create_batch_mutation_field_name.as_str()
|
||||||
|
{
|
||||||
|
match context.ctx.data::<AuthUserInfo>() {
|
||||||
|
Ok(user_info) => {
|
||||||
|
let subscriber_id = user_info.subscriber_auth.subscriber_id;
|
||||||
|
let value = input.get_mut(column_name.as_str());
|
||||||
|
if value.is_none() {
|
||||||
|
input.insert(
|
||||||
|
column_name.as_str().to_string(),
|
||||||
|
Value::Int(Some(subscriber_id)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
input
|
||||||
|
}
|
||||||
|
Err(err) => unreachable!("auth user info must be guarded: {:?}", err),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
input
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
@ -66,7 +66,7 @@ export function authContextFromInjector(injector: Injector): AuthContext {
|
|||||||
return {
|
return {
|
||||||
type: authProvider.authMethod,
|
type: authProvider.authMethod,
|
||||||
isAuthenticated$: authService.isAuthenticated$,
|
isAuthenticated$: authService.isAuthenticated$,
|
||||||
userData$: authService.userData$,
|
userData$: authService.authData$,
|
||||||
checkAuthResultEvent$: authService.checkAuthResultEvent$,
|
checkAuthResultEvent$: authService.checkAuthResultEvent$,
|
||||||
authService,
|
authService,
|
||||||
authProvider,
|
authProvider,
|
||||||
|
@ -20,14 +20,14 @@ export function useAuth() {
|
|||||||
[authContext.isAuthenticated$]
|
[authContext.isAuthenticated$]
|
||||||
);
|
);
|
||||||
|
|
||||||
const userData = useMemo(
|
const authData = useMemo(
|
||||||
() => atomWithObservable(() => authContext.userData$ as Observable<any>),
|
() => atomWithObservable(() => authContext.userData$ as Observable<any>),
|
||||||
[authContext]
|
[authContext]
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...authContext,
|
...authContext,
|
||||||
userData,
|
authData,
|
||||||
injector,
|
injector,
|
||||||
isAuthenticated,
|
isAuthenticated,
|
||||||
};
|
};
|
||||||
|
@ -6,8 +6,8 @@ export class AuthService {
|
|||||||
private authProvider = inject(AUTH_PROVIDER);
|
private authProvider = inject(AUTH_PROVIDER);
|
||||||
|
|
||||||
isAuthenticated$ = this.authProvider.isAuthenticated$;
|
isAuthenticated$ = this.authProvider.isAuthenticated$;
|
||||||
userData$ = this.authProvider.userData$;
|
|
||||||
checkAuthResultEvent$ = this.authProvider.checkAuthResultEvent$;
|
checkAuthResultEvent$ = this.authProvider.checkAuthResultEvent$;
|
||||||
|
authData$ = this.authProvider.authData$;
|
||||||
|
|
||||||
setup() {
|
setup() {
|
||||||
this.authProvider.setup();
|
this.authProvider.setup();
|
||||||
|
@ -7,7 +7,7 @@ export abstract class AuthProvider {
|
|||||||
abstract authMethod: AuthMethodType;
|
abstract authMethod: AuthMethodType;
|
||||||
abstract checkAuthResultEvent$: Observable<CheckAuthResultEventType>;
|
abstract checkAuthResultEvent$: Observable<CheckAuthResultEventType>;
|
||||||
abstract isAuthenticated$: Observable<boolean>;
|
abstract isAuthenticated$: Observable<boolean>;
|
||||||
abstract userData$: Observable<any>;
|
abstract authData$: Observable<any>;
|
||||||
abstract getAccessToken(): Observable<string | undefined>;
|
abstract getAccessToken(): Observable<string | undefined>;
|
||||||
abstract setup(): void;
|
abstract setup(): void;
|
||||||
abstract autoLoginPartialRoutesGuard(): Observable<boolean>;
|
abstract autoLoginPartialRoutesGuard(): Observable<boolean>;
|
||||||
|
@ -7,7 +7,7 @@ import { AUTH_METHOD } from '../defs';
|
|||||||
export class BasicAuthProvider extends AuthProvider {
|
export class BasicAuthProvider extends AuthProvider {
|
||||||
authMethod = AUTH_METHOD.BASIC;
|
authMethod = AUTH_METHOD.BASIC;
|
||||||
isAuthenticated$ = of(true);
|
isAuthenticated$ = of(true);
|
||||||
userData$ = of({});
|
authData$ = of({});
|
||||||
checkAuthResultEvent$: Observable<CheckAuthResultEventType> = NEVER;
|
checkAuthResultEvent$: Observable<CheckAuthResultEventType> = NEVER;
|
||||||
|
|
||||||
getAccessToken(): Observable<string | undefined> {
|
getAccessToken(): Observable<string | undefined> {
|
||||||
|
@ -25,7 +25,7 @@ export class OidcAuthProvider extends AuthProvider {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
get userData$() {
|
get authData$() {
|
||||||
return this.oidcSecurityService.userData$.pipe(map((s) => s.userData));
|
return this.oidcSecurityService.userData$.pipe(map((s) => s.userData));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,20 +56,21 @@ const CREATE_SUBSCRIPTION_MUTATION = gql`
|
|||||||
sourceUrl
|
sourceUrl
|
||||||
enabled
|
enabled
|
||||||
category
|
category
|
||||||
|
subscriberId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
function SubscriptionCreateRouteComponent() {
|
function SubscriptionCreateRouteComponent() {
|
||||||
const { userData } = useAuth();
|
const { authData } = useAuth();
|
||||||
console.log(JSON.stringify(userData, null, 2));
|
console.log(JSON.stringify(authData, null, 2));
|
||||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const form = useForm<SubscriptionFormValues>({
|
const form = useForm<SubscriptionFormValues>({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
displayName: '',
|
displayName: '',
|
||||||
sourceUrl: '',
|
sourceUrl: '',
|
||||||
category: 'Mikan',
|
category: 'mikan',
|
||||||
enabled: true,
|
enabled: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -129,7 +130,7 @@ function SubscriptionCreateRouteComponent() {
|
|||||||
disabled
|
disabled
|
||||||
value={field.value}
|
value={field.value}
|
||||||
onValueChange={field.onChange}
|
onValueChange={field.onChange}
|
||||||
defaultValue="Mikan"
|
defaultValue="mikan"
|
||||||
>
|
>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<SelectTrigger>
|
<SelectTrigger>
|
||||||
@ -137,11 +138,11 @@ function SubscriptionCreateRouteComponent() {
|
|||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectItem value="Mikan">Mikan</SelectItem>
|
<SelectItem value="mikan">mikan</SelectItem>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
<FormDescription>
|
<FormDescription>
|
||||||
Currently only Mikan source is supported
|
Currently only mikan source is supported
|
||||||
</FormDescription>
|
</FormDescription>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
Loading…
Reference in New Issue
Block a user