Compare commits

...

3 Commits

Author SHA1 Message Date
5b001f9584 refactor: refactor graphql 2025-07-02 01:25:44 +08:00
d06acde882 fix: temp save 2025-07-01 03:45:56 +08:00
bacfe99ef2 fix: fix issues 2025-06-30 02:05:23 +08:00
45 changed files with 586 additions and 1210 deletions

12
.vscode/settings.json vendored
View File

@ -40,13 +40,9 @@
} }
], ],
"rust-analyzer.cargo.features": "all", "rust-analyzer.cargo.features": "all",
"rust-analyzer.testExplorer": true "rust-analyzer.testExplorer": true,
// https://github.com/rust-lang/rust/issues/141540 // https://github.com/rust-lang/rust/issues/141540
// "rust-analyzer.cargo.targetDir": "target/rust-analyzer", "rust-analyzer.runnables.extraEnv": {
// "rust-analyzer.check.extraEnv": { "CARGO_INCREMENTAL": "0",
// "CARGO_TARGET_DIR": "target/rust-analyzer" }
// },
// "rust-analyzer.cargo.extraEnv": {
// "CARGO_TARGET_DIR": "target/analyzer"
// }
} }

20
Cargo.lock generated
View File

@ -6793,6 +6793,7 @@ dependencies = [
"once_cell", "once_cell",
"opendal", "opendal",
"openidconnect", "openidconnect",
"paste",
"percent-encoding", "percent-encoding",
"polars", "polars",
"quick-xml", "quick-xml",
@ -6807,6 +6808,7 @@ dependencies = [
"sea-orm", "sea-orm",
"sea-orm-migration", "sea-orm-migration",
"seaography", "seaography",
"secrecy",
"serde", "serde",
"serde_json", "serde_json",
"serde_variant", "serde_variant",
@ -7643,16 +7645,16 @@ checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
[[package]] [[package]]
name = "seaography" name = "seaography"
version = "1.1.4" version = "1.1.4"
source = "git+https://github.com/dumtruck/seaography.git?rev=a787c3a#a787c3ab83cf1f8275894e1bc1ca3c766b54674b" source = "git+https://github.com/dumtruck/seaography.git?rev=9f7fc7c#9f7fc7cf05234abe35fd9144c895321dd2b5db62"
dependencies = [ dependencies = [
"async-graphql", "async-graphql",
"fnv", "fnv",
"heck 0.4.1", "heck 0.5.0",
"itertools 0.12.1", "itertools 0.14.0",
"lazy_static", "lazy_static",
"sea-orm", "sea-orm",
"serde_json", "serde_json",
"thiserror 1.0.69", "thiserror 2.0.12",
] ]
[[package]] [[package]]
@ -7669,6 +7671,16 @@ dependencies = [
"zeroize", "zeroize",
] ]
[[package]]
name = "secrecy"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e891af845473308773346dc847b2c23ee78fe442e0472ac50e22a18a93d3ae5a"
dependencies = [
"serde",
"zeroize",
]
[[package]] [[package]]
name = "security-framework" name = "security-framework"
version = "2.11.1" version = "2.11.1"

View File

@ -15,7 +15,7 @@ resolver = "2"
[profile.dev] [profile.dev]
debug = 0 debug = 0
# https://github.com/rust-lang/rust/issues/141540 # https://github.com/rust-lang/rust/issues/141540
incremental = false incremental = true # Then only change rust-analyzer incremental
# [simd not supported by cranelift](https://github.com/rust-lang/rustc_codegen_cranelift/issues/171) # [simd not supported by cranelift](https://github.com/rust-lang/rustc_codegen_cranelift/issues/171)
# codegen-backend = "cranelift" # codegen-backend = "cranelift"
@ -87,4 +87,4 @@ nanoid = "0.4.0"
webp = "0.3.0" webp = "0.3.0"
[patch.crates-io] [patch.crates-io]
seaography = { git = "https://github.com/dumtruck/seaography.git", rev = "a787c3a" } seaography = { git = "https://github.com/dumtruck/seaography.git", rev = "9f7fc7c" }

View File

@ -125,6 +125,7 @@ seaography = { version = "1.1", features = [
"with-bigdecimal", "with-bigdecimal",
"with-postgres-array", "with-postgres-array",
"with-json-as-scalar", "with-json-as-scalar",
"with-custom-as-json",
] } ] }
tower = { version = "0.5.2", features = ["util"] } tower = { version = "0.5.2", features = ["util"] }
tower-http = { version = "0.6", features = [ tower-http = { version = "0.6", features = [
@ -166,6 +167,8 @@ quick-xml = { version = "0.37.5", features = [
] } ] }
croner = "2.2.0" croner = "2.2.0"
ts-rs = "11.0.1" ts-rs = "11.0.1"
secrecy = { version = "0.10.3", features = ["serde"] }
paste = "1.0.15"
[dev-dependencies] [dev-dependencies]
inquire = { workspace = true } inquire = { workspace = true }

View File

@ -0,0 +1,6 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { SyncOneSubscriptionFeedsFullTaskInput } from "./SyncOneSubscriptionFeedsFullTaskInput";
import type { SyncOneSubscriptionFeedsIncrementalTaskInput } from "./SyncOneSubscriptionFeedsIncrementalTaskInput";
import type { SyncOneSubscriptionSourcesTaskInput } from "./SyncOneSubscriptionSourcesTaskInput";
export type SubscriberTaskInput = { "taskType": "sync_one_subscription_feeds_incremental" } & SyncOneSubscriptionFeedsIncrementalTaskInput | { "taskType": "sync_one_subscription_feeds_full" } & SyncOneSubscriptionFeedsFullTaskInput | { "taskType": "sync_one_subscription_sources" } & SyncOneSubscriptionSourcesTaskInput;

View File

@ -0,0 +1,6 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { SyncOneSubscriptionFeedsFullTask } from "./SyncOneSubscriptionFeedsFullTask";
import type { SyncOneSubscriptionFeedsIncrementalTask } from "./SyncOneSubscriptionFeedsIncrementalTask";
import type { SyncOneSubscriptionSourcesTask } from "./SyncOneSubscriptionSourcesTask";
export type SubscriberTaskType = { "taskType": "sync_one_subscription_feeds_incremental" } & SyncOneSubscriptionFeedsIncrementalTask | { "taskType": "sync_one_subscription_feeds_full" } & SyncOneSubscriptionFeedsFullTask | { "taskType": "sync_one_subscription_sources" } & SyncOneSubscriptionSourcesTask;

View File

@ -0,0 +1,3 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type SyncOneSubscriptionFeedsFullTask = { subscriptionId: number, subscriberId: number, cronId?: number | null, };

View File

@ -0,0 +1,3 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type SyncOneSubscriptionFeedsFullTaskInput = { subscriptionId: number, subscriberId?: number | null, cronId?: number | null, };

View File

@ -0,0 +1,3 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type SyncOneSubscriptionFeedsIncrementalTask = { subscriptionId: number, subscriberId: number, cronId?: number | null, };

View File

@ -0,0 +1,3 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type SyncOneSubscriptionFeedsIncrementalTaskInput = { subscriptionId: number, subscriberId?: number | null, cronId?: number | null, };

View File

@ -0,0 +1,3 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type SyncOneSubscriptionSourcesTask = { subscriptionId: number, subscriberId: number, cronId?: number | null, };

View File

@ -0,0 +1,3 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type SyncOneSubscriptionSourcesTaskInput = { subscriptionId: number, subscriberId?: number | null, cronId?: number | null, };

View File

@ -0,0 +1,6 @@
{
"name": "recorder",
"version": "0.0.1",
"private": true,
"type": "module"
}

View File

@ -313,4 +313,10 @@ impl From<http::method::InvalidMethod> for RecorderError {
} }
} }
impl From<async_graphql::Error> for RecorderError {
fn from(error: async_graphql::Error) -> Self {
seaography::SeaographyError::AsyncGraphQLError(error).into()
}
}
pub type RecorderResult<T> = Result<T, RecorderError>; pub type RecorderResult<T> = Result<T, RecorderError>;

View File

@ -101,7 +101,7 @@ pub fn register_credential3rd_to_schema_builder(
.schema .schema
.register(Credential3rdCheckAvailableInfo::generate_output_object()); .register(Credential3rdCheckAvailableInfo::generate_output_object());
let builder_context = builder.context; let builder_context = &builder.context;
{ {
let check_available_mutation_name = get_entity_custom_mutation_field_name::< let check_available_mutation_name = get_entity_custom_mutation_field_name::<
credential_3rd::Entity, credential_3rd::Entity,

View File

@ -1,20 +1,15 @@
use convert_case::Case;
use sea_orm::Iterable; use sea_orm::Iterable;
use seaography::{Builder as SeaographyBuilder, BuilderContext}; use seaography::{Builder as SeaographyBuilder, BuilderContext};
use crate::{ use crate::{
graphql::{ graphql::{
domains::subscribers::restrict_subscriber_for_entity, domains::{
infra::{ subscriber_tasks::restrict_subscriber_tasks_for_entity,
custom::register_entity_default_writable, subscribers::restrict_subscriber_for_entity,
json::{
convert_jsonb_output_case_for_entity, restrict_jsonb_filter_input_for_entity,
validate_jsonb_input_for_entity,
},
name::get_entity_and_column_name,
}, },
infra::{custom::register_entity_default_writable, name::get_entity_and_column_name},
}, },
models::{cron, subscriber_tasks}, models::cron,
}; };
fn skip_columns_for_entity_input(context: &mut BuilderContext) { fn skip_columns_for_entity_input(context: &mut BuilderContext) {
@ -22,7 +17,6 @@ fn skip_columns_for_entity_input(context: &mut BuilderContext) {
if matches!( if matches!(
column, column,
cron::Column::SubscriberTask cron::Column::SubscriberTask
| cron::Column::Id
| cron::Column::CronExpr | cron::Column::CronExpr
| cron::Column::Enabled | cron::Column::Enabled
| cron::Column::TimeoutMs | cron::Column::TimeoutMs
@ -49,16 +43,7 @@ fn skip_columns_for_entity_input(context: &mut BuilderContext) {
pub fn register_cron_to_schema_context(context: &mut BuilderContext) { pub fn register_cron_to_schema_context(context: &mut BuilderContext) {
restrict_subscriber_for_entity::<cron::Entity>(context, &cron::Column::SubscriberId); restrict_subscriber_for_entity::<cron::Entity>(context, &cron::Column::SubscriberId);
restrict_jsonb_filter_input_for_entity::<cron::Entity>(context, &cron::Column::SubscriberTask); restrict_subscriber_tasks_for_entity::<cron::Entity>(context, &cron::Column::SubscriberTask);
convert_jsonb_output_case_for_entity::<cron::Entity>(
context,
&cron::Column::SubscriberTask,
Case::Camel,
);
validate_jsonb_input_for_entity::<cron::Entity, Option<subscriber_tasks::SubscriberTask>>(
context,
&cron::Column::SubscriberTask,
);
skip_columns_for_entity_input(context); skip_columns_for_entity_input(context);
} }

View File

@ -1,12 +1,15 @@
use std::{ops::Deref, sync::Arc}; use std::{ops::Deref, sync::Arc};
use async_graphql::dynamic::{FieldValue, TypeRef, ValueAccessor}; use async_graphql::dynamic::{FieldValue, Scalar, TypeRef};
use convert_case::Case; use convert_case::Case;
use sea_orm::{ use sea_orm::{
ColumnTrait, ConnectionTrait, EntityTrait, Iterable, QueryFilter, QuerySelect, QueryTrait, ActiveModelBehavior, ColumnTrait, ConnectionTrait, EntityTrait, Iterable, QueryFilter,
prelude::Expr, sea_query::Query, QuerySelect, QueryTrait, prelude::Expr, sea_query::Query,
}; };
use seaography::{Builder as SeaographyBuilder, BuilderContext, GuardAction}; use seaography::{
Builder as SeaographyBuilder, BuilderContext, SeaographyError, prepare_active_model,
};
use ts_rs::TS;
use crate::{ use crate::{
auth::AuthUserInfo, auth::AuthUserInfo,
@ -20,59 +23,22 @@ use crate::{
generate_entity_default_insert_input_object, generate_entity_delete_mutation_field, generate_entity_default_insert_input_object, generate_entity_delete_mutation_field,
generate_entity_filtered_mutation_field, register_entity_default_readonly, generate_entity_filtered_mutation_field, register_entity_default_readonly,
}, },
json::{ json::{convert_jsonb_output_for_entity, restrict_jsonb_filter_input_for_entity},
convert_jsonb_output_case_for_entity, restrict_jsonb_filter_input_for_entity,
validate_jsonb_input_for_entity,
},
name::{ name::{
get_column_name, get_entity_and_column_name, get_entity_basic_type_name, get_entity_and_column_name, get_entity_basic_type_name,
get_entity_create_batch_mutation_data_field_name, get_entity_custom_mutation_field_name,
get_entity_create_batch_mutation_field_name,
get_entity_create_one_mutation_data_field_name,
get_entity_create_one_mutation_field_name, get_entity_custom_mutation_field_name,
get_entity_update_mutation_field_name,
}, },
}, },
}, },
models::subscriber_tasks, models::subscriber_tasks,
task::{ApalisJobs, ApalisSchema}, task::{ApalisJobs, ApalisSchema, SubscriberTaskTrait},
}; };
pub fn check_entity_and_task_subscriber_id_matches(
value_accessor: &ValueAccessor<'_>,
subscriber_id: i32,
subscriber_id_column_name: &str,
subscriber_task_column_name: &str,
) -> bool {
value_accessor.object().is_ok_and(|input_object| {
input_object
.get(subscriber_task_column_name)
.and_then(|subscriber_task_value| subscriber_task_value.object().ok())
.and_then(|subscriber_task_object| {
subscriber_task_object
.get("subscriber_id")
.and_then(|job_subscriber_id| job_subscriber_id.i64().ok())
})
.is_some_and(|subscriber_task_subscriber_id| {
subscriber_task_subscriber_id as i32
== input_object
.get(subscriber_id_column_name)
.and_then(|subscriber_id_object| subscriber_id_object.i64().ok())
.map(|subscriber_id| subscriber_id as i32)
.unwrap_or(subscriber_id)
})
})
}
fn skip_columns_for_entity_input(context: &mut BuilderContext) { fn skip_columns_for_entity_input(context: &mut BuilderContext) {
for column in subscriber_tasks::Column::iter() { for column in subscriber_tasks::Column::iter() {
if matches!( if matches!(
column, column,
subscriber_tasks::Column::Job subscriber_tasks::Column::Job | subscriber_tasks::Column::SubscriberId
| subscriber_tasks::Column::Id
| subscriber_tasks::Column::SubscriberId
| subscriber_tasks::Column::Priority
| subscriber_tasks::Column::MaxAttempts
) { ) {
continue; continue;
} }
@ -82,115 +48,77 @@ fn skip_columns_for_entity_input(context: &mut BuilderContext) {
} }
} }
pub fn restrict_subscriber_tasks_for_entity<T>(context: &mut BuilderContext, column: &T::Column)
where
T: EntityTrait,
<T as EntityTrait>::Model: Sync,
{
let entity_and_column = get_entity_and_column_name::<T>(context, column);
restrict_jsonb_filter_input_for_entity::<T>(context, column);
convert_jsonb_output_for_entity::<T>(context, column, Some(Case::Camel));
let entity_column_name = get_entity_and_column_name::<T>(context, column);
context.types.input_type_overwrites.insert(
entity_column_name.clone(),
TypeRef::Named(subscriber_tasks::SubscriberTask::ident().into()),
);
context.types.output_type_overwrites.insert(
entity_column_name.clone(),
TypeRef::Named(subscriber_tasks::SubscriberTask::ident().into()),
);
context.types.input_conversions.insert(
entity_column_name.clone(),
Box::new(move |resolve_context, value_accessor| {
let task: subscriber_tasks::SubscriberTaskInput = value_accessor.deserialize()?;
let subscriber_id = resolve_context
.data::<AuthUserInfo>()?
.subscriber_auth
.subscriber_id;
let task = subscriber_tasks::SubscriberTask::from_input(task, subscriber_id);
let json_value = serde_json::to_value(task).map_err(|err| {
SeaographyError::TypeConversionError(
err.to_string(),
format!("Json - {entity_column_name}"),
)
})?;
Ok(sea_orm::Value::Json(Some(Box::new(json_value))))
}),
);
context.entity_input.update_skips.push(entity_and_column);
}
pub fn register_subscriber_tasks_to_schema_context(context: &mut BuilderContext) { pub fn register_subscriber_tasks_to_schema_context(context: &mut BuilderContext) {
restrict_subscriber_for_entity::<subscriber_tasks::Entity>( restrict_subscriber_for_entity::<subscriber_tasks::Entity>(
context, context,
&subscriber_tasks::Column::SubscriberId, &subscriber_tasks::Column::SubscriberId,
); );
restrict_jsonb_filter_input_for_entity::<subscriber_tasks::Entity>( restrict_subscriber_tasks_for_entity::<subscriber_tasks::Entity>(
context,
&subscriber_tasks::Column::Job,
);
convert_jsonb_output_case_for_entity::<subscriber_tasks::Entity>(
context,
&subscriber_tasks::Column::Job,
Case::Camel,
);
validate_jsonb_input_for_entity::<subscriber_tasks::Entity, subscriber_tasks::SubscriberTask>(
context, context,
&subscriber_tasks::Column::Job, &subscriber_tasks::Column::Job,
); );
skip_columns_for_entity_input(context); skip_columns_for_entity_input(context);
context.guards.field_guards.insert(
get_entity_and_column_name::<subscriber_tasks::Entity>(
context,
&subscriber_tasks::Column::Job,
),
{
let create_one_mutation_field_name =
Arc::new(get_entity_create_one_mutation_field_name::<
subscriber_tasks::Entity,
>(context));
let create_one_mutation_data_field_name =
Arc::new(get_entity_create_one_mutation_data_field_name(context).to_string());
let create_batch_mutation_field_name =
Arc::new(get_entity_create_batch_mutation_field_name::<
subscriber_tasks::Entity,
>(context));
let create_batch_mutation_data_field_name =
Arc::new(get_entity_create_batch_mutation_data_field_name(context).to_string());
let update_mutation_field_name = Arc::new(get_entity_update_mutation_field_name::<
subscriber_tasks::Entity,
>(context));
let job_column_name = Arc::new(get_column_name::<subscriber_tasks::Entity>(
context,
&subscriber_tasks::Column::Job,
));
let subscriber_id_column_name = Arc::new(get_column_name::<subscriber_tasks::Entity>(
context,
&subscriber_tasks::Column::SubscriberId,
));
Box::new(move |resolve_context| {
let field_name = resolve_context.field().name();
let subscriber_id = resolve_context
.data_opt::<AuthUserInfo>()
.unwrap()
.subscriber_auth
.subscriber_id;
let matched_subscriber_id = match field_name {
field if field == create_one_mutation_field_name.as_str() => resolve_context
.args
.get(create_one_mutation_data_field_name.as_str())
.is_some_and(|value_accessor| {
check_entity_and_task_subscriber_id_matches(
&value_accessor,
subscriber_id,
subscriber_id_column_name.as_str(),
job_column_name.as_str(),
)
}),
field if field == create_batch_mutation_field_name.as_str() => resolve_context
.args
.get(create_batch_mutation_data_field_name.as_str())
.and_then(|value| value.list().ok())
.is_some_and(|list| {
list.iter().all(|value| {
check_entity_and_task_subscriber_id_matches(
&value,
subscriber_id,
subscriber_id_column_name.as_str(),
job_column_name.as_str(),
)
})
}),
field if field == update_mutation_field_name.as_str() => {
unreachable!("subscriberTask entity do not support update job")
}
_ => true,
};
if matched_subscriber_id {
GuardAction::Allow
} else {
GuardAction::Block(Some(
"subscriber_id mismatch between entity and job".to_string(),
))
}
})
},
);
} }
pub fn register_subscriber_tasks_to_schema_builder( pub fn register_subscriber_tasks_to_schema_builder(
mut builder: SeaographyBuilder, mut builder: SeaographyBuilder,
) -> SeaographyBuilder { ) -> SeaographyBuilder {
builder.schema = builder.schema.register(
Scalar::new(subscriber_tasks::SubscriberTask::ident())
.description(subscriber_tasks::SubscriberTask::decl()),
);
builder.register_enumeration::<subscriber_tasks::SubscriberTaskType>(); builder.register_enumeration::<subscriber_tasks::SubscriberTaskType>();
builder.register_enumeration::<subscriber_tasks::SubscriberTaskStatus>(); builder.register_enumeration::<subscriber_tasks::SubscriberTaskStatus>();
builder = register_entity_default_readonly!(builder, subscriber_tasks); builder = register_entity_default_readonly!(builder, subscriber_tasks);
let builder_context = builder.context; let builder_context = builder.context;
{ {
builder builder
.outputs .outputs
@ -278,23 +206,30 @@ pub fn register_subscriber_tasks_to_schema_builder(
subscriber_tasks::Entity, subscriber_tasks::Entity,
>(builder_context)); >(builder_context));
let create_one_mutation = let create_one_mutation =
generate_entity_create_one_mutation_field::<subscriber_tasks::Entity, TypeRef>( generate_entity_create_one_mutation_field::<subscriber_tasks::Entity>(
builder_context, builder_context,
None, Arc::new(move |resolver_ctx, app_ctx, input_object| {
Arc::new(|_resolver_ctx, app_ctx, input_object| {
let job_column_name = get_column_name::<subscriber_tasks::Entity>(
builder_context,
&subscriber_tasks::Column::Job,
);
let task = input_object
.get(job_column_name.as_str())
.unwrap()
.deserialize::<subscriber_tasks::SubscriberTask>()
.unwrap();
Box::pin(async move { Box::pin(async move {
let active_model: Result<subscriber_tasks::ActiveModel, _> =
prepare_active_model(builder_context, &input_object, resolver_ctx);
let task_service = app_ctx.task(); let task_service = app_ctx.task();
let active_model = active_model?;
let db = app_ctx.db();
let active_model = active_model.before_save(db, true).await?;
let task = active_model.job.unwrap();
let subscriber_id = active_model.subscriber_id.unwrap();
if task.get_subscriber_id() != subscriber_id {
Err(async_graphql::Error::new(
"subscriber_id does not match with job.subscriber_id",
))?;
}
let task_id = task_service.add_subscriber_task(task).await?.to_string(); let task_id = task_service.add_subscriber_task(task).await?.to_string();
let db = app_ctx.db(); let db = app_ctx.db();

View File

@ -7,7 +7,7 @@ use sea_orm::{ColumnTrait, Condition, EntityTrait, Iterable, Value as SeaValue};
use seaography::{ use seaography::{
Builder as SeaographyBuilder, BuilderContext, FilterInfo, Builder as SeaographyBuilder, BuilderContext, FilterInfo,
FilterOperation as SeaographqlFilterOperation, FilterType, FilterTypesMapHelper, FilterOperation as SeaographqlFilterOperation, FilterType, FilterTypesMapHelper,
FnFilterCondition, FnGuard, FnInputTypeNoneConversion, GuardAction, SeaResult, SeaographyError, FnFilterCondition, FnGuard, FnInputTypeNoneConversion, GuardAction, SeaResult,
}; };
use crate::{ use crate::{
@ -219,11 +219,10 @@ where
if let Some(value) = filter.get("eq") { if let Some(value) = filter.get("eq") {
let value: i32 = value.i64()?.try_into()?; let value: i32 = value.i64()?.try_into()?;
if value != subscriber_id { if value != subscriber_id {
return Err(SeaographyError::AsyncGraphQLError( return Err(async_graphql::Error::new(
async_graphql::Error::new( "subscriber_id and auth_info does not match",
"subscriber_id and auth_info does not match", )
), .into());
));
} }
} }
} }
@ -319,13 +318,11 @@ pub fn register_subscribers_to_schema_context(context: &mut BuilderContext) {
pub fn register_subscribers_to_schema_builder(mut builder: SeaographyBuilder) -> SeaographyBuilder { pub fn register_subscribers_to_schema_builder(mut builder: SeaographyBuilder) -> SeaographyBuilder {
{ {
let filter_types_map_helper = FilterTypesMapHelper {
context: builder.context,
};
builder.schema = builder builder.schema = builder
.schema .schema
.register(filter_types_map_helper.generate_filter_input(&SUBSCRIBER_ID_FILTER_INFO)); .register(FilterTypesMapHelper::generate_filter_input(
&SUBSCRIBER_ID_FILTER_INFO,
));
} }
builder = register_entity_default_readonly!(builder, subscribers); builder = register_entity_default_readonly!(builder, subscribers);

View File

@ -1,23 +1,11 @@
use std::sync::Arc;
use async_graphql::dynamic::{FieldValue, TypeRef};
use sea_orm::{ColumnTrait, EntityTrait, QueryFilter};
use seaography::{Builder as SeaographyBuilder, BuilderContext}; use seaography::{Builder as SeaographyBuilder, BuilderContext};
use crate::{ use crate::{
errors::RecorderError,
graphql::{ graphql::{
domains::subscribers::restrict_subscriber_for_entity, domains::subscribers::restrict_subscriber_for_entity,
infra::{ infra::custom::register_entity_default_writable,
custom::{generate_entity_filtered_mutation_field, register_entity_default_writable},
name::{get_entity_basic_type_name, get_entity_custom_mutation_field_name},
},
},
models::{subscriber_tasks, subscriptions},
task::{
SyncOneSubscriptionFeedsFullTask, SyncOneSubscriptionFeedsIncrementalTask,
SyncOneSubscriptionSourcesTask,
}, },
models::subscriptions,
}; };
pub fn register_subscriptions_to_schema_context(context: &mut BuilderContext) { pub fn register_subscriptions_to_schema_context(context: &mut BuilderContext) {
@ -32,162 +20,5 @@ pub fn register_subscriptions_to_schema_builder(
) -> SeaographyBuilder { ) -> SeaographyBuilder {
builder.register_enumeration::<subscriptions::SubscriptionCategory>(); builder.register_enumeration::<subscriptions::SubscriptionCategory>();
builder = register_entity_default_writable!(builder, subscriptions, false); builder = register_entity_default_writable!(builder, subscriptions, false);
let context = builder.context;
{
let sync_one_feeds_incremental_mutation_name = get_entity_custom_mutation_field_name::<
subscriptions::Entity,
>(context, "SyncOneFeedsIncremental");
let sync_one_feeds_incremental_mutation =
generate_entity_filtered_mutation_field::<subscriptions::Entity, _, _>(
builder.context,
sync_one_feeds_incremental_mutation_name,
TypeRef::named_nn(get_entity_basic_type_name::<subscriber_tasks::Entity>(
context,
)),
Arc::new(|_resolver_ctx, app_ctx, filters| {
Box::pin(async move {
let db = app_ctx.db();
let subscription_model = subscriptions::Entity::find()
.filter(filters)
.one(db)
.await?
.ok_or_else(|| {
RecorderError::from_entity_not_found::<subscriptions::Entity>()
})?;
let task_service = app_ctx.task();
let task_id = task_service
.add_subscriber_task(
SyncOneSubscriptionFeedsIncrementalTask::builder()
.subscriber_id(subscription_model.subscriber_id)
.subscription_id(subscription_model.id)
.build()
.into(),
)
.await?;
let task_model = subscriber_tasks::Entity::find()
.filter(subscriber_tasks::Column::Id.eq(task_id.to_string()))
.one(db)
.await?
.ok_or_else(|| {
RecorderError::from_entity_not_found::<subscriber_tasks::Entity>()
})?;
Ok(Some(FieldValue::owned_any(task_model)))
})
}),
);
builder.mutations.push(sync_one_feeds_incremental_mutation);
}
{
let sync_one_feeds_full_mutation_name = get_entity_custom_mutation_field_name::<
subscriptions::Entity,
>(builder.context, "SyncOneFeedsFull");
let sync_one_feeds_full_mutation =
generate_entity_filtered_mutation_field::<subscriptions::Entity, _, _>(
builder.context,
sync_one_feeds_full_mutation_name,
TypeRef::named_nn(get_entity_basic_type_name::<subscriber_tasks::Entity>(
context,
)),
Arc::new(|_resolver_ctx, app_ctx, filters| {
Box::pin(async move {
let db = app_ctx.db();
let subscription_model = subscriptions::Entity::find()
.filter(filters)
.one(db)
.await?
.ok_or_else(|| {
RecorderError::from_entity_not_found::<subscriptions::Entity>()
})?;
let task_service = app_ctx.task();
let task_id = task_service
.add_subscriber_task(
SyncOneSubscriptionFeedsFullTask::builder()
.subscriber_id(subscription_model.subscriber_id)
.subscription_id(subscription_model.id)
.build()
.into(),
)
.await?;
let task_model = subscriber_tasks::Entity::find()
.filter(subscriber_tasks::Column::Id.eq(task_id.to_string()))
.one(db)
.await?
.ok_or_else(|| {
RecorderError::from_entity_not_found::<subscriber_tasks::Entity>()
})?;
Ok(Some(FieldValue::owned_any(task_model)))
})
}),
);
builder.mutations.push(sync_one_feeds_full_mutation);
}
{
let sync_one_sources_mutation_name = get_entity_custom_mutation_field_name::<
subscriptions::Entity,
>(context, "SyncOneSources");
let sync_one_sources_mutation =
generate_entity_filtered_mutation_field::<subscriptions::Entity, _, _>(
builder.context,
sync_one_sources_mutation_name,
TypeRef::named_nn(get_entity_basic_type_name::<subscriber_tasks::Entity>(
context,
)),
Arc::new(|_resolver_ctx, app_ctx, filters| {
Box::pin(async move {
let db = app_ctx.db();
let subscription_model = subscriptions::Entity::find()
.filter(filters)
.one(db)
.await?
.ok_or_else(|| {
RecorderError::from_entity_not_found::<subscriptions::Entity>()
})?;
let task_service = app_ctx.task();
let task_id = task_service
.add_subscriber_task(
SyncOneSubscriptionSourcesTask::builder()
.subscriber_id(subscription_model.subscriber_id)
.subscription_id(subscription_model.id)
.build()
.into(),
)
.await?;
let task_model = subscriber_tasks::Entity::find()
.filter(subscriber_tasks::Column::Id.eq(task_id.to_string()))
.one(db)
.await?
.ok_or_else(|| {
RecorderError::from_entity_not_found::<subscriber_tasks::Entity>()
})?;
Ok(Some(FieldValue::owned_any(task_model)))
})
}),
);
builder.mutations.push(sync_one_sources_mutation);
}
builder builder
} }

View File

@ -4,27 +4,20 @@ use async_graphql::dynamic::{
Field, FieldFuture, FieldValue, InputObject, InputValue, Object, ObjectAccessor, Field, FieldFuture, FieldValue, InputObject, InputValue, Object, ObjectAccessor,
ResolverContext, TypeRef, ResolverContext, TypeRef,
}; };
use sea_orm::{ use sea_orm::{ActiveModelTrait, Condition, EntityTrait, IntoActiveModel};
ActiveModelTrait, Condition, EntityTrait, IntoActiveModel, QueryFilter, TransactionTrait,
};
use seaography::{ use seaography::{
Builder as SeaographyBuilder, BuilderContext, GuardAction, RelationBuilder, SeaographyError, Builder as SeaographyBuilder, BuilderContext, EntityCreateBatchMutationBuilder,
get_filter_conditions, prepare_active_model, EntityCreateOneMutationBuilder, EntityDeleteMutationBuilder, EntityInputBuilder,
EntityObjectBuilder, EntityUpdateMutationBuilder, GuardAction, RelationBuilder,
get_filter_conditions,
}; };
use crate::{ use crate::{
app::AppContextTrait, app::AppContextTrait,
errors::RecorderResult, errors::RecorderResult,
graphql::infra::name::{ graphql::infra::name::{
get_entity_and_column_name_from_column_str, get_entity_basic_type_name, get_entity_filter_input_type_name, get_entity_name,
get_entity_create_batch_mutation_data_field_name, get_entity_renormalized_filter_field_name,
get_entity_create_batch_mutation_field_name,
get_entity_create_one_mutation_data_field_name, get_entity_create_one_mutation_field_name,
get_entity_delete_mutation_field_name, get_entity_delete_mutation_filter_field_name,
get_entity_filter_input_type_name, get_entity_insert_data_input_type_name, get_entity_name,
get_entity_renormalized_filter_field_name, get_entity_update_data_input_type_name,
get_entity_update_mutation_data_field_name, get_entity_update_mutation_field_name,
get_entity_update_mutation_filter_field_name,
}, },
}; };
@ -41,9 +34,9 @@ pub type FilterMutationFn = Arc<
pub type CreateOneMutationFn<M> = Arc< pub type CreateOneMutationFn<M> = Arc<
dyn for<'a> Fn( dyn for<'a> Fn(
&ResolverContext<'a>, &'a ResolverContext<'a>,
Arc<dyn AppContextTrait>, Arc<dyn AppContextTrait>,
ObjectAccessor<'_>, ObjectAccessor<'a>,
) -> Pin<Box<dyn Future<Output = RecorderResult<M>> + Send + 'a>> ) -> Pin<Box<dyn Future<Output = RecorderResult<M>> + Send + 'a>>
+ Send + Send
+ Sync, + Sync,
@ -51,9 +44,9 @@ pub type CreateOneMutationFn<M> = Arc<
pub type CreateBatchMutationFn<M> = Arc< pub type CreateBatchMutationFn<M> = Arc<
dyn for<'a> Fn( dyn for<'a> Fn(
&ResolverContext<'a>, &'a ResolverContext<'a>,
Arc<dyn AppContextTrait>, Arc<dyn AppContextTrait>,
Vec<ObjectAccessor<'_>>, Vec<ObjectAccessor<'a>>,
) -> Pin<Box<dyn Future<Output = RecorderResult<Vec<M>>> + Send + 'a>> ) -> Pin<Box<dyn Future<Output = RecorderResult<Vec<M>>> + Send + 'a>>
+ Send + Send
+ Sync, + Sync,
@ -61,10 +54,10 @@ pub type CreateBatchMutationFn<M> = Arc<
pub type UpdateMutationFn<M> = Arc< pub type UpdateMutationFn<M> = Arc<
dyn for<'a> Fn( dyn for<'a> Fn(
&ResolverContext<'a>, &'a ResolverContext<'a>,
Arc<dyn AppContextTrait>, Arc<dyn AppContextTrait>,
Condition, Condition,
ObjectAccessor<'_>, ObjectAccessor<'a>,
) -> Pin<Box<dyn Future<Output = RecorderResult<Vec<M>>> + Send + 'a>> ) -> Pin<Box<dyn Future<Output = RecorderResult<Vec<M>>> + Send + 'a>>
+ Send + Send
+ Sync, + Sync,
@ -80,48 +73,46 @@ pub type DeleteMutationFn = Arc<
+ Sync, + Sync,
>; >;
pub fn generate_entity_default_insert_input_object<T>( pub fn generate_entity_default_insert_input_object<T>(context: &BuilderContext) -> InputObject
builder_context: &'static BuilderContext,
) -> InputObject
where where
T: EntityTrait, T: EntityTrait,
<T as EntityTrait>::Model: Sync, <T as EntityTrait>::Model: Sync,
{ {
let entity_input_builder = seaography::EntityInputBuilder { EntityInputBuilder::insert_input_object::<T>(context)
context: builder_context,
};
entity_input_builder.insert_input_object::<T>()
} }
pub fn generate_entity_default_update_input_object<T>( pub fn generate_entity_default_update_input_object<T>(context: &BuilderContext) -> InputObject
builder_context: &'static BuilderContext,
) -> InputObject
where where
T: EntityTrait, T: EntityTrait,
<T as EntityTrait>::Model: Sync, <T as EntityTrait>::Model: Sync,
{ {
let entity_input_builder = seaography::EntityInputBuilder { EntityInputBuilder::update_input_object::<T>(context)
context: builder_context,
};
entity_input_builder.update_input_object::<T>()
} }
pub fn generate_entity_default_basic_entity_object<T>( pub fn generate_entity_default_basic_entity_object<T>(context: &'static BuilderContext) -> Object
builder_context: &'static BuilderContext,
) -> Object
where where
T: EntityTrait, T: EntityTrait,
<T as EntityTrait>::Model: Sync, <T as EntityTrait>::Model: Sync,
{ {
let entity_object_builder = seaography::EntityObjectBuilder { let entity_object_builder = EntityObjectBuilder { context };
context: builder_context,
};
entity_object_builder.basic_to_object::<T>() entity_object_builder.basic_to_object::<T>()
} }
pub fn generate_entity_input_object<T>(
context: &'static BuilderContext,
is_insert: bool,
) -> InputObject
where
T: EntityTrait,
<T as EntityTrait>::Model: Sync,
{
if is_insert {
EntityInputBuilder::insert_input_object::<T>(context)
} else {
EntityInputBuilder::update_input_object::<T>(context)
}
}
pub fn generate_entity_filtered_mutation_field<E, N, R>( pub fn generate_entity_filtered_mutation_field<E, N, R>(
builder_context: &'static BuilderContext, builder_context: &'static BuilderContext,
field_name: N, field_name: N,
@ -138,11 +129,12 @@ where
let guard = builder_context.guards.entity_guards.get(&object_name); let guard = builder_context.guards.entity_guards.get(&object_name);
Field::new(field_name, type_ref, move |ctx| { Field::new(field_name, type_ref, move |resolve_context| {
let mutation_fn = mutation_fn.clone(); let mutation_fn = mutation_fn.clone();
FieldFuture::new(async move { FieldFuture::new(async move {
let guard_flag = if let Some(guard) = guard { let guard_flag = if let Some(guard) = guard {
(*guard)(&ctx) (*guard)(&resolve_context)
} else { } else {
GuardAction::Allow GuardAction::Allow
}; };
@ -153,15 +145,15 @@ where
)); ));
} }
let app_ctx = ctx.data::<Arc<dyn AppContextTrait>>()?; let filters = resolve_context
.args
.get(get_entity_renormalized_filter_field_name());
let filters = ctx.args.get(get_entity_renormalized_filter_field_name()); let filters = get_filter_conditions::<E>(&resolve_context, builder_context, filters);
let filters = get_filter_conditions::<E>(&ctx, builder_context, filters); let app_ctx = resolve_context.data::<Arc<dyn AppContextTrait>>()?;
let result = mutation_fn(&ctx, app_ctx.clone(), filters) let result = mutation_fn(&resolve_context, app_ctx.clone(), filters).await?;
.await
.map_err(async_graphql::Error::new_with_source)?;
Ok(result) Ok(result)
}) })
@ -172,314 +164,71 @@ where
)) ))
} }
pub fn generate_entity_create_one_mutation_field<E, ID>( pub fn generate_entity_create_one_mutation_field<E>(
builder_context: &'static BuilderContext, builder_context: &'static BuilderContext,
input_data_type_ref: Option<ID>,
mutation_fn: CreateOneMutationFn<E::Model>, mutation_fn: CreateOneMutationFn<E::Model>,
) -> Field ) -> Field
where where
E: EntityTrait, E: EntityTrait,
<E as EntityTrait>::Model: Sync, <E as EntityTrait>::Model: Sync,
ID: Into<TypeRef>,
{ {
let guard = builder_context let entity_create_one_mutation_builder = EntityCreateOneMutationBuilder {
.guards context: builder_context,
.entity_guards };
.get(&get_entity_name::<E>(builder_context)); entity_create_one_mutation_builder.to_field_with_mutation_fn::<E>(Arc::new(
let field_guards = &builder_context.guards.field_guards; move |resolver_ctx, input_object| {
Field::new(
get_entity_create_one_mutation_field_name::<E>(builder_context),
TypeRef::named_nn(get_entity_basic_type_name::<E>(builder_context)),
move |ctx| {
let mutation_fn = mutation_fn.clone(); 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 { Box::pin(async move {
return Err::<Option<_>, async_graphql::Error>(async_graphql::Error::new( let app_ctx = resolver_ctx.data::<Arc<dyn AppContextTrait>>()?;
reason.unwrap_or("Entity guard triggered.".into()),
));
}
let app_ctx = ctx.data::<Arc<dyn AppContextTrait>>()?; let result = mutation_fn(resolver_ctx, app_ctx.clone(), input_object).await?;
let value_accessor = ctx Ok(result)
.args
.get(get_entity_create_one_mutation_data_field_name(
builder_context,
))
.unwrap();
let input_object = value_accessor.object()?;
for (column, _) in input_object.iter() {
let field_guard = field_guards.get(
&get_entity_and_column_name_from_column_str::<E>(builder_context, column),
);
let field_guard_flag = if let Some(field_guard) = field_guard {
(*field_guard)(&ctx)
} else {
GuardAction::Allow
};
if let GuardAction::Block(reason) = field_guard_flag {
return match reason {
Some(reason) => Err::<Option<_>, async_graphql::Error>(
async_graphql::Error::new(reason),
),
None => Err::<Option<_>, async_graphql::Error>(
async_graphql::Error::new("Field guard triggered."),
),
};
}
}
let result = mutation_fn(&ctx, app_ctx.clone(), input_object)
.await
.map_err(async_graphql::Error::new_with_source)?;
Ok(Some(FieldValue::owned_any(result)))
}) })
}, },
)
.argument(InputValue::new(
get_entity_create_one_mutation_data_field_name(builder_context),
input_data_type_ref.map(|t| t.into()).unwrap_or_else(|| {
TypeRef::named_nn(get_entity_insert_data_input_type_name::<E>(builder_context))
}),
)) ))
} }
pub fn generate_entity_default_create_one_mutation_fn<T, A>(
builder_context: &'static BuilderContext,
active_model_hooks: bool,
) -> CreateOneMutationFn<T::Model>
where
T: EntityTrait,
<T as EntityTrait>::Model: Sync + IntoActiveModel<A>,
A: ActiveModelTrait<Entity = T> + sea_orm::ActiveModelBehavior + std::marker::Send + 'static,
{
Arc::new(move |resolve_context, app_ctx, input_object| {
let entity_input_builder = seaography::EntityInputBuilder {
context: builder_context,
};
let entity_object_builder = seaography::EntityObjectBuilder {
context: builder_context,
};
let active_model = prepare_active_model::<T, A>(
&entity_input_builder,
&entity_object_builder,
&input_object,
resolve_context,
)
.map_err(SeaographyError::AsyncGraphQLError);
Box::pin(async move {
if active_model_hooks {
let transaction = app_ctx.db().begin().await?;
let active_model = active_model?;
let active_model = active_model.before_save(&transaction, true).await?;
let result: T::Model = active_model.insert(&transaction).await?;
let result = A::after_save(result, &transaction, true).await?;
transaction.commit().await?;
Ok(result)
} else {
let db = app_ctx.db();
let active_model = active_model?;
let result: T::Model = active_model.insert(db).await?;
Ok(result)
}
})
})
}
pub fn generate_entity_default_create_one_mutation_field<E, A>( pub fn generate_entity_default_create_one_mutation_field<E, A>(
builder_context: &'static BuilderContext, builder_context: &'static BuilderContext,
active_model_hooks: bool, active_model_hooks: bool,
) -> Field ) -> Field
where where
E: EntityTrait, E: EntityTrait,
<E as EntityTrait>::Model: Sync, <E as EntityTrait>::Model: Sync + IntoActiveModel<A>,
<E as EntityTrait>::Model: IntoActiveModel<A>, A: ActiveModelTrait<Entity = E> + sea_orm::ActiveModelBehavior + std::marker::Send,
A: ActiveModelTrait<Entity = E> + sea_orm::ActiveModelBehavior + std::marker::Send + 'static,
{ {
generate_entity_create_one_mutation_field::<E, TypeRef>( let entity_create_one_mutation_builder = EntityCreateOneMutationBuilder {
builder_context, context: builder_context,
None, };
generate_entity_default_create_one_mutation_fn::<E, A>(builder_context, active_model_hooks), entity_create_one_mutation_builder.to_field::<E, A>(active_model_hooks)
)
} }
pub fn generate_entity_create_batch_mutation_field<E, ID>( pub fn generate_entity_create_batch_mutation_field<E, ID>(
builder_context: &'static BuilderContext, builder_context: &'static BuilderContext,
input_data_type_ref: Option<ID>,
mutation_fn: CreateBatchMutationFn<E::Model>, mutation_fn: CreateBatchMutationFn<E::Model>,
) -> Field ) -> Field
where where
E: EntityTrait, E: EntityTrait,
<E as EntityTrait>::Model: Sync, <E as EntityTrait>::Model: Sync,
ID: Into<TypeRef>,
{ {
let object_name: String = get_entity_name::<E>(builder_context); let entity_create_batch_mutation_builder = EntityCreateBatchMutationBuilder {
let guard = builder_context.guards.entity_guards.get(&object_name); context: builder_context,
let field_guards = &builder_context.guards.field_guards; };
entity_create_batch_mutation_builder.to_field_with_mutation_fn::<E>(Arc::new(
Field::new( move |resolver_ctx, input_objects| {
get_entity_create_batch_mutation_field_name::<E>(builder_context),
TypeRef::named_nn_list_nn(get_entity_basic_type_name::<E>(builder_context)),
move |ctx| {
let mutation_fn = mutation_fn.clone(); 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 { Box::pin(async move {
return match reason { let app_ctx = resolver_ctx.data::<Arc<dyn AppContextTrait>>()?;
Some(reason) => Err::<Option<_>, async_graphql::Error>(
async_graphql::Error::new(reason),
),
None => Err::<Option<_>, async_graphql::Error>(async_graphql::Error::new(
"Entity guard triggered.",
)),
};
}
let mut input_objects: Vec<ObjectAccessor<'_>> = vec![]; let result = mutation_fn(resolver_ctx, app_ctx.clone(), input_objects).await?;
let list = ctx
.args
.get(get_entity_create_batch_mutation_data_field_name(
builder_context,
))
.unwrap()
.list()?;
for input in list.iter() {
let input_object = input.object()?;
for (column, _) in input_object.iter() {
let field_guard =
field_guards.get(&get_entity_and_column_name_from_column_str::<E>(
builder_context,
column,
));
let field_guard_flag = if let Some(field_guard) = field_guard {
(*field_guard)(&ctx)
} else {
GuardAction::Allow
};
if let GuardAction::Block(reason) = field_guard_flag {
return match reason {
Some(reason) => Err::<Option<_>, async_graphql::Error>(
async_graphql::Error::new(reason),
),
None => Err::<Option<_>, async_graphql::Error>(
async_graphql::Error::new("Field guard triggered."),
),
};
}
}
input_objects.push(input_object);
}
let app_ctx = ctx.data::<Arc<dyn AppContextTrait>>()?;
let results = mutation_fn(&ctx, app_ctx.clone(), input_objects)
.await
.map_err(async_graphql::Error::new_with_source)?;
Ok(Some(FieldValue::list(
results.into_iter().map(FieldValue::owned_any),
)))
})
},
)
.argument(InputValue::new(
get_entity_create_batch_mutation_data_field_name(builder_context),
input_data_type_ref.map(|t| t.into()).unwrap_or_else(|| {
TypeRef::named_nn_list_nn(get_entity_insert_data_input_type_name::<E>(builder_context))
}),
))
}
pub fn generate_entity_default_create_batch_mutation_fn<E, A>(
builder_context: &'static BuilderContext,
active_model_hooks: bool,
) -> CreateBatchMutationFn<E::Model>
where
E: EntityTrait,
<E as EntityTrait>::Model: Sync,
<E as EntityTrait>::Model: IntoActiveModel<A>,
A: ActiveModelTrait<Entity = E> + sea_orm::ActiveModelBehavior + std::marker::Send + 'static,
{
Arc::new(move |resolve_context, app_ctx, input_objects| {
let entity_input_builder = seaography::EntityInputBuilder {
context: builder_context,
};
let entity_object_builder = seaography::EntityObjectBuilder {
context: builder_context,
};
let active_models = input_objects
.into_iter()
.map(|input_object| {
prepare_active_model::<E, A>(
&entity_input_builder,
&entity_object_builder,
&input_object,
resolve_context,
)
})
.collect::<Result<Vec<_>, _>>()
.map_err(SeaographyError::AsyncGraphQLError);
Box::pin(async move {
if active_model_hooks {
let transaction = app_ctx.db().begin().await?;
let mut before_save_models = vec![];
for active_model in active_models? {
let before_save_model = active_model.before_save(&transaction, false).await?;
before_save_models.push(before_save_model);
}
let models: Vec<E::Model> = E::insert_many(before_save_models)
.exec_with_returning_many(&transaction)
.await?;
let mut result = vec![];
for model in models {
let after_save_model = A::after_save(model, &transaction, false).await?;
result.push(after_save_model);
}
transaction.commit().await?;
Ok(result) Ok(result)
} else { })
let db = app_ctx.db(); },
let active_models = active_models?; ))
let results: Vec<E::Model> = E::insert_many(active_models)
.exec_with_returning_many(db)
.await?;
Ok(results)
}
})
})
} }
pub fn generate_entity_default_create_batch_mutation_field<E, A>( pub fn generate_entity_default_create_batch_mutation_field<E, A>(
@ -490,177 +239,45 @@ where
E: EntityTrait, E: EntityTrait,
<E as EntityTrait>::Model: Sync, <E as EntityTrait>::Model: Sync,
<E as EntityTrait>::Model: IntoActiveModel<A>, <E as EntityTrait>::Model: IntoActiveModel<A>,
A: ActiveModelTrait<Entity = E> + sea_orm::ActiveModelBehavior + std::marker::Send + 'static, A: ActiveModelTrait<Entity = E> + sea_orm::ActiveModelBehavior + std::marker::Send,
{ {
generate_entity_create_batch_mutation_field::<E, TypeRef>( let entity_create_batch_mutation_builder = EntityCreateBatchMutationBuilder {
builder_context, context: builder_context,
None, };
generate_entity_default_create_batch_mutation_fn::<E, A>( entity_create_batch_mutation_builder.to_field::<E, A>(active_model_hooks)
builder_context,
active_model_hooks,
),
)
} }
pub fn generate_entity_update_mutation_field<E, I>( pub fn generate_entity_update_mutation_field<E>(
builder_context: &'static BuilderContext, builder_context: &'static BuilderContext,
input_data_type_ref: Option<I>,
mutation_fn: UpdateMutationFn<E::Model>, mutation_fn: UpdateMutationFn<E::Model>,
) -> Field ) -> Field
where where
E: EntityTrait, E: EntityTrait,
<E as EntityTrait>::Model: Sync, <E as EntityTrait>::Model: Sync,
I: Into<TypeRef>,
{ {
let guard = builder_context let entity_update_mutation_builder = EntityUpdateMutationBuilder {
.guards context: builder_context,
.entity_guards };
.get(&get_entity_name::<E>(builder_context)); entity_update_mutation_builder.to_field_with_mutation_fn::<E>(Arc::new(
let field_guards = &builder_context.guards.field_guards; move |resolver_ctx, filters, input_object| {
Field::new(
get_entity_update_mutation_field_name::<E>(builder_context),
TypeRef::named_nn_list_nn(get_entity_basic_type_name::<E>(builder_context)),
move |ctx| {
let mutation_fn = mutation_fn.clone(); 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 match reason {
Some(reason) => Err::<Option<_>, async_graphql::Error>(
async_graphql::Error::new(reason),
),
None => Err::<Option<_>, async_graphql::Error>(async_graphql::Error::new(
"Entity guard triggered.",
)),
};
}
let app_ctx = ctx.data::<Arc<dyn AppContextTrait>>()?;
let filters = ctx.args.get(get_entity_update_mutation_filter_field_name(
builder_context,
));
let filter_condition = get_filter_conditions::<E>(&ctx, builder_context, filters);
let value_accessor = ctx
.args
.get(get_entity_update_mutation_data_field_name(builder_context))
.unwrap();
let input_object = value_accessor.object()?;
for (column, _) in input_object.iter() {
let field_guard = field_guards.get(
&get_entity_and_column_name_from_column_str::<E>(builder_context, column),
);
let field_guard_flag = if let Some(field_guard) = field_guard {
(*field_guard)(&ctx)
} else {
GuardAction::Allow
};
if let GuardAction::Block(reason) = field_guard_flag {
return match reason {
Some(reason) => Err::<Option<_>, async_graphql::Error>(
async_graphql::Error::new(reason),
),
None => Err::<Option<_>, async_graphql::Error>(
async_graphql::Error::new("Field guard triggered."),
),
};
}
}
let result = mutation_fn(&ctx, app_ctx.clone(), filter_condition, input_object)
.await
.map_err(async_graphql::Error::new_with_source)?;
Ok(Some(FieldValue::list(
result.into_iter().map(FieldValue::owned_any),
)))
})
},
)
.argument(InputValue::new(
get_entity_update_mutation_data_field_name(builder_context),
input_data_type_ref.map(|t| t.into()).unwrap_or_else(|| {
TypeRef::named_nn(get_entity_update_data_input_type_name::<E>(builder_context))
}),
))
.argument(InputValue::new(
get_entity_update_mutation_filter_field_name(builder_context),
TypeRef::named(get_entity_filter_input_type_name::<E>(builder_context)),
))
}
pub fn generate_entity_default_update_mutation_fn<T, A>(
builder_context: &'static BuilderContext,
active_model_hooks: bool,
) -> UpdateMutationFn<T::Model>
where
T: EntityTrait,
<T as EntityTrait>::Model: Sync + IntoActiveModel<A>,
A: ActiveModelTrait<Entity = T> + sea_orm::ActiveModelBehavior + std::marker::Send + 'static,
{
Arc::new(
move |resolve_context, app_ctx, filter_condition, input_object| {
let entity_input_builder = seaography::EntityInputBuilder {
context: builder_context,
};
let entity_object_builder = seaography::EntityObjectBuilder {
context: builder_context,
};
let active_model = prepare_active_model::<T, A>(
&entity_input_builder,
&entity_object_builder,
&input_object,
resolve_context,
)
.map_err(SeaographyError::AsyncGraphQLError);
Box::pin(async move { Box::pin(async move {
if active_model_hooks { let app_ctx = resolver_ctx.data::<Arc<dyn AppContextTrait>>()?;
let transaction = app_ctx.db().begin().await?;
let active_model = active_model?; let result = mutation_fn(
resolver_ctx,
app_ctx.clone(),
get_filter_conditions::<E>(resolver_ctx, builder_context, filters),
input_object,
)
.await
.map_err(async_graphql::Error::new_with_source)?;
let active_model = active_model.before_save(&transaction, false).await?; Ok(result)
let models = T::update_many()
.set(active_model)
.filter(filter_condition.clone())
.exec_with_returning(&transaction)
.await?;
let mut result = vec![];
for model in models {
result.push(A::after_save(model, &transaction, false).await?);
}
transaction.commit().await?;
Ok(result)
} else {
let db = app_ctx.db();
let active_model = active_model?;
let result = T::update_many()
.set(active_model)
.filter(filter_condition.clone())
.exec_with_returning(db)
.await?;
Ok(result)
}
}) })
}, },
) ))
} }
pub fn generate_entity_default_update_mutation_field<E, A>( pub fn generate_entity_default_update_mutation_field<E, A>(
@ -671,13 +288,12 @@ where
E: EntityTrait, E: EntityTrait,
<E as EntityTrait>::Model: Sync, <E as EntityTrait>::Model: Sync,
<E as EntityTrait>::Model: IntoActiveModel<A>, <E as EntityTrait>::Model: IntoActiveModel<A>,
A: ActiveModelTrait<Entity = E> + sea_orm::ActiveModelBehavior + std::marker::Send + 'static, A: ActiveModelTrait<Entity = E> + sea_orm::ActiveModelBehavior + std::marker::Send,
{ {
generate_entity_update_mutation_field::<E, TypeRef>( let entity_update_mutation_builder = EntityUpdateMutationBuilder {
builder_context, context: builder_context,
None, };
generate_entity_default_update_mutation_fn::<E, A>(builder_context, active_model_hooks), entity_update_mutation_builder.to_field::<E, A>(active_model_hooks)
)
} }
pub fn generate_entity_delete_mutation_field<E>( pub fn generate_entity_delete_mutation_field<E>(
@ -688,111 +304,42 @@ where
E: EntityTrait, E: EntityTrait,
<E as EntityTrait>::Model: Sync, <E as EntityTrait>::Model: Sync,
{ {
let object_name: String = get_entity_name::<E>(builder_context); let entity_delete_mutation_builder = EntityDeleteMutationBuilder {
let guard = builder_context.guards.entity_guards.get(&object_name); context: builder_context,
};
Field::new( entity_delete_mutation_builder.to_field_with_mutation_fn::<E>(Arc::new(
get_entity_delete_mutation_field_name::<E>(builder_context), move |resolver_ctx, filters| {
TypeRef::named_nn(TypeRef::INT),
move |ctx| {
let mutation_fn = mutation_fn.clone(); 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 { Box::pin(async move {
return Err::<Option<_>, async_graphql::Error>(async_graphql::Error::new( let app_ctx = resolver_ctx.data::<Arc<dyn AppContextTrait>>()?;
reason.unwrap_or("Entity guard triggered.".into()), let result = mutation_fn(
)); resolver_ctx,
} app_ctx.clone(),
get_filter_conditions::<E>(resolver_ctx, builder_context, filters),
)
.await
.map_err(async_graphql::Error::new_with_source)?;
let filters = ctx.args.get(get_entity_delete_mutation_filter_field_name( Ok(result)
builder_context,
));
let filter_condition = get_filter_conditions::<E>(&ctx, builder_context, filters);
let app_ctx = ctx.data::<Arc<dyn AppContextTrait>>()?;
let res = mutation_fn(&ctx, app_ctx.clone(), filter_condition)
.await
.map_err(async_graphql::Error::new_with_source)?;
Ok(Some(async_graphql::Value::from(res)))
}) })
}, },
)
.argument(InputValue::new(
get_entity_delete_mutation_filter_field_name(builder_context),
TypeRef::named(get_entity_filter_input_type_name::<E>(builder_context)),
)) ))
} }
pub fn generate_entity_default_delete_mutation_fn<E, A>(
_builder_context: &'static BuilderContext,
active_model_hooks: bool,
) -> DeleteMutationFn
where
E: EntityTrait,
<E as EntityTrait>::Model: Sync,
<E as EntityTrait>::Model: IntoActiveModel<A>,
A: ActiveModelTrait<Entity = E> + sea_orm::ActiveModelBehavior + std::marker::Send + 'static,
{
Arc::new(move |_resolve_context, app_ctx, filter_condition| {
Box::pin(async move {
if active_model_hooks {
let transaction = app_ctx.db().begin().await?;
let models: Vec<E::Model> = E::find()
.filter(filter_condition.clone())
.all(&transaction)
.await?;
let mut active_models: Vec<A> = vec![];
for model in models {
let active_model = model.into_active_model();
active_models.push(active_model.before_delete(&transaction).await?);
}
let result = E::delete_many()
.filter(filter_condition)
.exec(&transaction)
.await?;
for active_model in active_models {
active_model.after_delete(&transaction).await?;
}
transaction.commit().await?;
Ok(result.rows_affected)
} else {
let db = app_ctx.db();
let result = E::delete_many().filter(filter_condition).exec(db).await?;
Ok(result.rows_affected)
}
})
})
}
pub fn generate_entity_default_delete_mutation_field<E, A>( pub fn generate_entity_default_delete_mutation_field<E, A>(
builder_context: &'static BuilderContext, builder_context: &'static BuilderContext,
active_model_hooks: bool, active_model_hooks: bool,
) -> Field ) -> Field
where where
E: EntityTrait, E: EntityTrait,
<E as EntityTrait>::Model: Sync, <E as EntityTrait>::Model: Sync + IntoActiveModel<A>,
<E as EntityTrait>::Model: IntoActiveModel<A>, A: ActiveModelTrait<Entity = E> + sea_orm::ActiveModelBehavior + std::marker::Send,
A: ActiveModelTrait<Entity = E> + sea_orm::ActiveModelBehavior + std::marker::Send + 'static,
{ {
generate_entity_delete_mutation_field::<E>( let entity_delete_mutation_builder = EntityDeleteMutationBuilder {
builder_context, context: builder_context,
generate_entity_default_delete_mutation_fn::<E, A>(builder_context, active_model_hooks), };
) entity_delete_mutation_builder.to_field::<E, A>(active_model_hooks)
} }
pub fn register_entity_default_mutations<E, A>( pub fn register_entity_default_mutations<E, A>(
@ -802,30 +349,31 @@ pub fn register_entity_default_mutations<E, A>(
where where
E: EntityTrait, E: EntityTrait,
<E as EntityTrait>::Model: Sync + IntoActiveModel<A>, <E as EntityTrait>::Model: Sync + IntoActiveModel<A>,
A: ActiveModelTrait<Entity = E> + sea_orm::ActiveModelBehavior + std::marker::Send + 'static, A: ActiveModelTrait<Entity = E> + sea_orm::ActiveModelBehavior + std::marker::Send,
{ {
let builder_context = builder.context;
builder builder
.outputs .outputs
.push(generate_entity_default_basic_entity_object::<E>( .push(generate_entity_default_basic_entity_object::<E>(
builder.context, builder_context,
)); ));
builder.inputs.extend([ builder.inputs.extend([
generate_entity_default_insert_input_object::<E>(builder.context), generate_entity_default_insert_input_object::<E>(builder_context),
generate_entity_default_update_input_object::<E>(builder.context), generate_entity_default_update_input_object::<E>(builder_context),
]); ]);
builder.mutations.extend([ builder.mutations.extend([
generate_entity_default_create_one_mutation_field::<E, A>( generate_entity_default_create_one_mutation_field::<E, A>(
builder.context, builder_context,
active_model_hooks, active_model_hooks,
), ),
generate_entity_default_create_batch_mutation_field::<E, A>( generate_entity_default_create_batch_mutation_field::<E, A>(
builder.context, builder_context,
active_model_hooks, active_model_hooks,
), ),
generate_entity_default_update_mutation_field::<E, A>(builder.context, active_model_hooks), generate_entity_default_update_mutation_field::<E, A>(builder_context, active_model_hooks),
generate_entity_default_delete_mutation_field::<E, A>(builder.context, active_model_hooks), generate_entity_default_delete_mutation_field::<E, A>(builder_context, active_model_hooks),
]); ]);
builder builder
@ -859,7 +407,7 @@ pub(crate) fn register_entity_default_writable_impl<T, RE, A, I>(
where where
T: EntityTrait, T: EntityTrait,
<T as EntityTrait>::Model: Sync + IntoActiveModel<A>, <T as EntityTrait>::Model: Sync + IntoActiveModel<A>,
A: ActiveModelTrait<Entity = T> + sea_orm::ActiveModelBehavior + std::marker::Send + 'static, A: ActiveModelTrait<Entity = T> + sea_orm::ActiveModelBehavior + std::marker::Send,
RE: sea_orm::Iterable<Iterator = I> + RelationBuilder, RE: sea_orm::Iterable<Iterator = I> + RelationBuilder,
I: Iterator<Item = RE> + Clone + DoubleEndedIterator + ExactSizeIterator + FusedIterator, I: Iterator<Item = RE> + Clone + DoubleEndedIterator + ExactSizeIterator + FusedIterator,
{ {

View File

@ -911,18 +911,15 @@ where
Box::new( Box::new(
move |_resolve_context: &ResolverContext<'_>, condition, filter| { move |_resolve_context: &ResolverContext<'_>, condition, filter| {
if let Some(filter) = filter { if let Some(filter) = filter {
let filter_value = to_value(filter.as_index_map()).map_err(|e| { let filter_value =
SeaographyError::AsyncGraphQLError(GraphqlError::new_with_source(e)) to_value(filter.as_index_map()).map_err(GraphqlError::new_with_source)?;
})?;
let filter_json: JsonValue = filter_value.into_json().map_err(|e| { let filter_json: JsonValue = filter_value
SeaographyError::AsyncGraphQLError(GraphqlError::new(format!("{e:?}"))) .into_json()
})?; .map_err(GraphqlError::new_with_source)?;
let cond_where = prepare_jsonb_filter_input(&Expr::col(column), filter_json) let cond_where = prepare_jsonb_filter_input(&Expr::col(column), filter_json)
.map_err(|e| { .map_err(GraphqlError::new_with_source)?;
SeaographyError::AsyncGraphQLError(GraphqlError::new_with_source(e))
})?;
let condition = condition.add(cond_where); let condition = condition.add(cond_where);
Ok(condition) Ok(condition)
@ -946,14 +943,22 @@ where
T: EntityTrait, T: EntityTrait,
<T as EntityTrait>::Model: Sync, <T as EntityTrait>::Model: Sync,
{ {
let entity_column_name = get_entity_and_column_name::<T>(context, column);
context.filter_types.overwrites.insert( context.filter_types.overwrites.insert(
get_entity_and_column_name::<T>(context, column), get_entity_and_column_name::<T>(context, column),
Some(FilterType::Custom(JSONB_FILTER_NAME.to_string())), Some(FilterType::Custom(JSONB_FILTER_NAME.to_string())),
); );
context.filter_types.condition_functions.insert(
entity_column_name.clone(),
generate_jsonb_filter_condition_function::<T>(context, column),
);
} }
pub fn validate_jsonb_input_for_entity<T, S>(context: &mut BuilderContext, column: &T::Column) pub fn try_convert_jsonb_input_for_entity<T, S>(
where context: &mut BuilderContext,
column: &T::Column,
case: Option<Case<'static>>,
) where
T: EntityTrait, T: EntityTrait,
<T as EntityTrait>::Model: Sync, <T as EntityTrait>::Model: Sync,
S: DeserializeOwned + Serialize, S: DeserializeOwned + Serialize,
@ -962,49 +967,52 @@ where
context.types.input_conversions.insert( context.types.input_conversions.insert(
entity_column_name.clone(), entity_column_name.clone(),
Box::new(move |_resolve_context, accessor| { Box::new(move |_resolve_context, accessor| {
let deserialized = accessor.deserialize::<S>().map_err(|err| { let mut json_value: serde_json::Value = accessor.deserialize()?;
SeaographyError::TypeConversionError(
err.message, if let Some(case) = case {
format!("Json - {entity_column_name}"), json_value = convert_json_keys(json_value, case);
) }
})?;
let json_value = serde_json::to_value(deserialized).map_err(|err| { serde_json::from_value::<S>(json_value.clone()).map_err(|err| {
SeaographyError::TypeConversionError( SeaographyError::TypeConversionError(
err.to_string(), err.to_string(),
format!("Json - {entity_column_name}"), format!("Json - {entity_column_name}"),
) )
})?; })?;
Ok(sea_orm::Value::Json(Some(Box::new(json_value)))) Ok(sea_orm::Value::Json(Some(Box::new(json_value))))
}), }),
); );
} }
pub fn convert_jsonb_output_case_for_entity<T>( pub fn convert_jsonb_output_for_entity<T>(
context: &mut BuilderContext, context: &mut BuilderContext,
column: &T::Column, column: &T::Column,
case: Case<'static>, case: Option<Case<'static>>,
) where ) where
T: EntityTrait, T: EntityTrait,
<T as EntityTrait>::Model: Sync, <T as EntityTrait>::Model: Sync,
{ {
let entity_column_key = get_entity_and_column_name::<T>(context, column); let entity_column_name = get_entity_and_column_name::<T>(context, column);
context.types.output_conversions.insert( context.types.output_conversions.insert(
entity_column_key.clone(), entity_column_name.clone(),
Box::new(move |value| { Box::new(move |value| {
if let sea_orm::Value::Json(Some(json)) = value { if let sea_orm::Value::Json(Some(json)) = value {
let result = let mut json_value = json.as_ref().clone();
async_graphql::Value::from_json(convert_json_keys(json.as_ref().clone(), case)) if let Some(case) = case {
.map_err(|err| { json_value = convert_json_keys(json_value, case);
SeaographyError::TypeConversionError( }
err.to_string(), let result = async_graphql::Value::from_json(json_value).map_err(|err| {
format!("Json - {entity_column_key}"), SeaographyError::TypeConversionError(
) err.to_string(),
})?; format!("Json - {entity_column_name}"),
)
})?;
Ok(result) Ok(result)
} else { } else {
Err(SeaographyError::TypeConversionError( Err(SeaographyError::TypeConversionError(
"value should be json".to_string(), "value should be json".to_string(),
format!("Json - {entity_column_key}"), format!("Json - {entity_column_name}"),
)) ))
} }
}), }),

View File

@ -78,7 +78,7 @@ where
context.filter_input.type_name.as_ref()(&entity_name) context.filter_input.type_name.as_ref()(&entity_name)
} }
pub fn get_entity_insert_data_input_type_name<T>(context: &BuilderContext) -> String pub fn get_entity_insert_input_type_name<T>(context: &BuilderContext) -> String
where where
T: EntityTrait, T: EntityTrait,
<T as EntityTrait>::Model: Sync, <T as EntityTrait>::Model: Sync,
@ -87,7 +87,7 @@ where
format!("{entity_name}{}", context.entity_input.insert_suffix) format!("{entity_name}{}", context.entity_input.insert_suffix)
} }
pub fn get_entity_update_data_input_type_name<T>(context: &BuilderContext) -> String pub fn get_entity_update_input_type_name<T>(context: &BuilderContext) -> String
where where
T: EntityTrait, T: EntityTrait,
<T as EntityTrait>::Model: Sync, <T as EntityTrait>::Model: Sync,

View File

@ -128,19 +128,13 @@ impl ActiveModelBehavior for ActiveModel {
Model::calculate_next_run(cron_expr).map_err(|e| DbErr::Custom(e.to_string()))?; Model::calculate_next_run(cron_expr).map_err(|e| DbErr::Custom(e.to_string()))?;
self.next_run = Set(Some(next_run)); self.next_run = Set(Some(next_run));
} }
if let ActiveValue::Set(Some(subscriber_id)) = self.subscriber_id { if let ActiveValue::Set(Some(subscriber_id)) = self.subscriber_id
if let ActiveValue::Set(Some(ref subscriber_task)) = self.subscriber_task { && let ActiveValue::Set(Some(ref subscriber_task)) = self.subscriber_task
if subscriber_task.get_subscriber_id() != subscriber_id { && subscriber_task.get_subscriber_id() != subscriber_id
return Err(DbErr::Custom( {
"Subscriber task subscriber_id does not match cron subscriber_id" return Err(DbErr::Custom(
.to_string(), "Cron subscriber_id does not match subscriber_task.subscriber_id".to_string(),
)); ));
}
} else {
return Err(DbErr::Custom(
"Cron subscriber_id is set but subscriber_task is not set".to_string(),
));
}
} }
Ok(self) Ok(self)

View File

@ -1,9 +1,10 @@
use async_trait::async_trait; use async_trait::async_trait;
use sea_orm::entity::prelude::*; use sea_orm::{ActiveValue, entity::prelude::*};
use crate::task::SubscriberTaskTrait;
pub use crate::task::{ pub use crate::task::{
SubscriberTask, SubscriberTaskType, SubscriberTaskTypeEnum, SubscriberTaskTypeVariant, SubscriberTask, SubscriberTaskInput, SubscriberTaskType, SubscriberTaskTypeEnum,
SubscriberTaskTypeVariantIter, SubscriberTaskTypeVariant, SubscriberTaskTypeVariantIter,
}; };
#[derive(Clone, Debug, PartialEq, Eq, DeriveActiveEnum, EnumIter, DeriveDisplay)] #[derive(Clone, Debug, PartialEq, Eq, DeriveActiveEnum, EnumIter, DeriveDisplay)]
@ -84,4 +85,19 @@ pub enum RelatedEntity {
} }
#[async_trait] #[async_trait]
impl ActiveModelBehavior for ActiveModel {} impl ActiveModelBehavior for ActiveModel {
async fn before_save<C>(mut self, _db: &C, _insert: bool) -> Result<Self, DbErr>
where
C: ConnectionTrait,
{
if let ActiveValue::Set(subscriber_id) = self.subscriber_id
&& let ActiveValue::Set(ref job) = self.job
&& job.get_subscriber_id() != subscriber_id
{
return Err(DbErr::Custom(
"SubscriberTask subscriber_id does not match job.subscriber_id".to_string(),
));
}
Ok(self)
}
}

View File

@ -42,9 +42,13 @@ where
} }
pub trait SubscriberTaskTrait: AsyncTaskTrait { pub trait SubscriberTaskTrait: AsyncTaskTrait {
type InputType: Serialize + DeserializeOwned + Sized + Send;
fn get_subscriber_id(&self) -> i32; fn get_subscriber_id(&self) -> i32;
fn get_cron_id(&self) -> Option<i32>; fn get_cron_id(&self) -> Option<i32>;
fn from_input(input: Self::InputType, subscriber_id: i32) -> Self;
} }
pub trait SystemTaskTrait: AsyncTaskTrait {} pub trait SystemTaskTrait: AsyncTaskTrait {}

View File

@ -12,9 +12,10 @@ pub use core::{
pub use config::TaskConfig; pub use config::TaskConfig;
pub use r#extern::{ApalisJobs, ApalisSchema}; pub use r#extern::{ApalisJobs, ApalisSchema};
pub use registry::{ pub use registry::{
OptimizeImageTask, SubscriberTask, SubscriberTaskType, SubscriberTaskTypeEnum, OptimizeImageTask, SubscriberTask, SubscriberTaskInput, SubscriberTaskType,
SubscriberTaskTypeVariant, SubscriberTaskTypeVariantIter, SyncOneSubscriptionFeedsFullTask, SubscriberTaskTypeEnum, SubscriberTaskTypeVariant, SubscriberTaskTypeVariantIter,
SyncOneSubscriptionFeedsIncrementalTask, SyncOneSubscriptionSourcesTask, SystemTask, SyncOneSubscriptionFeedsFullTask, SyncOneSubscriptionFeedsIncrementalTask,
SystemTaskType, SystemTaskTypeEnum, SystemTaskTypeVariant, SystemTaskTypeVariantIter, SyncOneSubscriptionSourcesTask, SystemTask, SystemTaskType, SystemTaskTypeEnum,
SystemTaskTypeVariant, SystemTaskTypeVariantIter,
}; };
pub use service::TaskService; pub use service::TaskService;

View File

@ -2,8 +2,8 @@ mod subscriber;
mod system; mod system;
pub use subscriber::{ pub use subscriber::{
SubscriberTask, SubscriberTaskType, SubscriberTaskTypeEnum, SubscriberTaskTypeVariant, SubscriberTask, SubscriberTaskInput, SubscriberTaskType, SubscriberTaskTypeEnum,
SubscriberTaskTypeVariantIter, SyncOneSubscriptionFeedsFullTask, SubscriberTaskTypeVariant, SubscriberTaskTypeVariantIter, SyncOneSubscriptionFeedsFullTask,
SyncOneSubscriptionFeedsIncrementalTask, SyncOneSubscriptionSourcesTask, SyncOneSubscriptionFeedsIncrementalTask, SyncOneSubscriptionSourcesTask,
}; };
pub use system::{ pub use system::{

View File

@ -6,15 +6,36 @@ macro_rules! register_subscriber_task_type {
} }
) => { ) => {
$(#[$type_meta])* $(#[$type_meta])*
#[derive(typed_builder::TypedBuilder)] #[derive(typed_builder::TypedBuilder, ts_rs::TS, serde::Serialize, serde::Deserialize)]
#[ts(export, rename_all = "camelCase")]
$task_vis struct $task_name { $task_vis struct $task_name {
$($(#[$field_meta])* pub $field_name: $field_type,)* $($(#[$field_meta])* pub $field_name: $field_type,)*
pub subscriber_id: i32, pub subscriber_id: i32,
#[builder(default = None)] #[builder(default = None)]
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cron_id: Option<i32>, pub cron_id: Option<i32>,
} }
paste::paste! {
$(#[$type_meta])*
#[derive(ts_rs::TS, serde::Serialize, serde::Deserialize)]
#[serde(rename_all = "camelCase")]
#[ts(export, rename_all = "camelCase")]
$task_vis struct [<$task_name Input>] {
$($(#[$field_meta])* pub $field_name: $field_type,)*
#[serde(default, skip_serializing_if = "Option::is_none")]
pub subscriber_id: Option<i32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cron_id: Option<i32>,
}
}
impl $crate::task::SubscriberTaskTrait for $task_name { impl $crate::task::SubscriberTaskTrait for $task_name {
paste::paste! {
type InputType = [<$task_name Input>];
}
fn get_subscriber_id(&self) -> i32 { fn get_subscriber_id(&self) -> i32 {
self.subscriber_id self.subscriber_id
} }
@ -22,6 +43,14 @@ macro_rules! register_subscriber_task_type {
fn get_cron_id(&self) -> Option<i32> { fn get_cron_id(&self) -> Option<i32> {
self.cron_id self.cron_id
} }
fn from_input(input: Self::InputType, subscriber_id: i32) -> Self {
Self {
$($field_name: input.$field_name,)*
cron_id: input.cron_id,
subscriber_id: input.subscriber_id.unwrap_or(subscriber_id),
}
}
} }
} }
} }

View File

@ -2,7 +2,6 @@ mod base;
mod subscription; mod subscription;
use sea_orm::{DeriveActiveEnum, DeriveDisplay, EnumIter, FromJsonQueryResult}; use sea_orm::{DeriveActiveEnum, DeriveDisplay, EnumIter, FromJsonQueryResult};
use serde::{Deserialize, Serialize};
pub use subscription::{ pub use subscription::{
SyncOneSubscriptionFeedsFullTask, SyncOneSubscriptionFeedsIncrementalTask, SyncOneSubscriptionFeedsFullTask, SyncOneSubscriptionFeedsIncrementalTask,
SyncOneSubscriptionSourcesTask, SyncOneSubscriptionSourcesTask,
@ -12,7 +11,7 @@ macro_rules! register_subscriber_task_types {
( (
task_type_enum: { task_type_enum: {
$(#[$type_enum_meta:meta])* $(#[$type_enum_meta:meta])*
pub enum $type_enum_name:ident { $type_vis:vis enum $type_enum_name:ident {
$( $(
$(#[$variant_meta:meta])* $(#[$variant_meta:meta])*
$variant:ident => $string_value:literal $variant:ident => $string_value:literal
@ -21,7 +20,7 @@ macro_rules! register_subscriber_task_types {
}, },
task_enum: { task_enum: {
$(#[$task_enum_meta:meta])* $(#[$task_enum_meta:meta])*
pub enum $task_enum_name:ident { $task_vis:vis enum $task_enum_name:ident {
$( $(
$(#[$task_variant_meta:meta])* $(#[$task_variant_meta:meta])*
$task_variant:ident($task_type:ty) $task_variant:ident($task_type:ty)
@ -30,8 +29,9 @@ macro_rules! register_subscriber_task_types {
} }
) => { ) => {
$(#[$type_enum_meta])* $(#[$type_enum_meta])*
#[derive(serde::Serialize, serde::Deserialize)]
#[sea_orm(rs_type = "String", db_type = "Text")] #[sea_orm(rs_type = "String", db_type = "Text")]
pub enum $type_enum_name { $type_vis enum $type_enum_name {
$( $(
$(#[$variant_meta])* $(#[$variant_meta])*
#[serde(rename = $string_value)] #[serde(rename = $string_value)]
@ -42,8 +42,10 @@ macro_rules! register_subscriber_task_types {
$(#[$task_enum_meta])* $(#[$task_enum_meta])*
#[derive(ts_rs::TS, serde::Serialize, serde::Deserialize)]
#[serde(tag = "task_type")] #[serde(tag = "task_type")]
pub enum $task_enum_name { #[ts(export,rename = "SubscriberTaskType", rename_all = "camelCase", tag = "taskType")]
$task_vis enum $task_enum_name {
$( $(
$(#[$task_variant_meta])* $(#[$task_variant_meta])*
#[serde(rename = $string_value)] #[serde(rename = $string_value)]
@ -51,6 +53,20 @@ macro_rules! register_subscriber_task_types {
)* )*
} }
paste::paste! {
$(#[$task_enum_meta])*
#[derive(ts_rs::TS, serde::Serialize, serde::Deserialize)]
#[serde(tag = "taskType", rename_all = "camelCase")]
#[ts(export,rename_all = "camelCase", tag = "taskType")]
$task_vis enum [<$task_enum_name Input>] {
$(
$(#[$task_variant_meta])*
#[serde(rename = $string_value)]
$task_variant(<$task_type as $crate::task::SubscriberTaskTrait>::InputType),
)*
}
}
impl TryFrom<$task_enum_name> for serde_json::Value { impl TryFrom<$task_enum_name> for serde_json::Value {
type Error = $crate::errors::RecorderError; type Error = $crate::errors::RecorderError;
@ -87,6 +103,10 @@ macro_rules! register_subscriber_task_types {
} }
impl $crate::task::SubscriberTaskTrait for $task_enum_name { impl $crate::task::SubscriberTaskTrait for $task_enum_name {
paste::paste! {
type InputType = [<$task_enum_name Input>];
}
fn get_subscriber_id(&self) -> i32 { fn get_subscriber_id(&self) -> i32 {
match self { match self {
$(Self::$task_variant(t) => $(Self::$task_variant(t) =>
@ -100,6 +120,14 @@ macro_rules! register_subscriber_task_types {
<$task_type as $crate::task::SubscriberTaskTrait>::get_cron_id(t),)* <$task_type as $crate::task::SubscriberTaskTrait>::get_cron_id(t),)*
} }
} }
fn from_input(input: Self::InputType, subscriber_id: i32) -> Self {
match input {
$(Self::InputType::$task_variant(t) =>
Self::$task_variant(<$task_type as $crate::task::SubscriberTaskTrait>::from_input(t, subscriber_id)),)*
}
}
} }
$( $(
@ -117,8 +145,6 @@ register_subscriber_task_types!(
#[derive( #[derive(
Clone, Clone,
Debug, Debug,
Serialize,
Deserialize,
PartialEq, PartialEq,
Eq, Eq,
Copy, Copy,
@ -133,7 +159,7 @@ register_subscriber_task_types!(
} }
}, },
task_enum: { task_enum: {
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, FromJsonQueryResult)] #[derive(Clone, Debug, PartialEq, Eq, FromJsonQueryResult)]
pub enum SubscriberTask { pub enum SubscriberTask {
SyncOneSubscriptionFeedsIncremental(SyncOneSubscriptionFeedsIncrementalTask), SyncOneSubscriptionFeedsIncremental(SyncOneSubscriptionFeedsIncrementalTask),
SyncOneSubscriptionFeedsFull(SyncOneSubscriptionFeedsFullTask), SyncOneSubscriptionFeedsFull(SyncOneSubscriptionFeedsFullTask),

View File

@ -1,5 +1,4 @@
use sea_orm::prelude::*; use sea_orm::prelude::*;
use serde::{Deserialize, Serialize};
use super::base::register_subscriber_task_type; use super::base::register_subscriber_task_type;
use crate::{errors::RecorderResult, models::subscriptions::SubscriptionTrait}; use crate::{errors::RecorderResult, models::subscriptions::SubscriptionTrait};
@ -40,7 +39,7 @@ macro_rules! register_subscription_task_type {
} }
register_subscription_task_type! { register_subscription_task_type! {
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub struct SyncOneSubscriptionFeedsIncrementalTask { pub struct SyncOneSubscriptionFeedsIncrementalTask {
} => async |subscription, ctx| -> RecorderResult<()> { } => async |subscription, ctx| -> RecorderResult<()> {
subscription.sync_feeds_incremental(ctx).await?; subscription.sync_feeds_incremental(ctx).await?;
@ -49,7 +48,7 @@ register_subscription_task_type! {
} }
register_subscription_task_type! { register_subscription_task_type! {
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub struct SyncOneSubscriptionFeedsFullTask { pub struct SyncOneSubscriptionFeedsFullTask {
} => async |subscription, ctx| -> RecorderResult<()> { } => async |subscription, ctx| -> RecorderResult<()> {
subscription.sync_feeds_full(ctx).await?; subscription.sync_feeds_full(ctx).await?;
@ -58,7 +57,7 @@ register_subscription_task_type! {
} }
register_subscription_task_type! { register_subscription_task_type! {
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub struct SyncOneSubscriptionSourcesTask { pub struct SyncOneSubscriptionSourcesTask {
} => async |subscription, ctx| -> RecorderResult<()> { } => async |subscription, ctx| -> RecorderResult<()> {
subscription.sync_sources(ctx).await?; subscription.sync_sources(ctx).await?;

View File

@ -0,0 +1,10 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"rootDir": ".",
"composite": true,
"module": "ESNext",
"moduleResolution": "bundler"
},
"include": ["bindings"]
}

View File

@ -12,6 +12,12 @@ const config: CodegenConfig = {
}, },
config: { config: {
enumsAsConst: true, enumsAsConst: true,
scalars: {
SubscriberTaskType: {
input: 'recorder/bindings/SubscriberTaskInput#SubscriberTaskInput',
output: 'recorder/bindings/SubscriberTaskType#SubscriberTaskType',
},
},
}, },
}, },
}, },

View File

@ -11,6 +11,7 @@
"codegen-watch": "graphql-codegen --config graphql-codegen.ts --watch" "codegen-watch": "graphql-codegen --config graphql-codegen.ts --watch"
}, },
"dependencies": { "dependencies": {
"recorder": "workspace:*",
"@abraham/reflection": "^0.13.0", "@abraham/reflection": "^0.13.0",
"@apollo/client": "^3.13.8", "@apollo/client": "^3.13.8",
"@codemirror/language": "6.11.1", "@codemirror/language": "6.11.1",

View File

@ -89,6 +89,7 @@ query GetSubscriptionDetail ($id: Int!) {
} }) { } }) {
nodes { nodes {
id id
subscriberId
displayName displayName
createdAt createdAt
updatedAt updatedAt
@ -137,30 +138,6 @@ query GetSubscriptionDetail ($id: Int!) {
} }
`; `;
export const SYNC_SUBSCRIPTION_FEEDS_INCREMENTAL = gql`
mutation SyncSubscriptionFeedsIncremental($filter: SubscriptionsFilterInput!) {
subscriptionsSyncOneFeedsIncremental(filter: $filter) {
id
}
}
`;
export const SYNC_SUBSCRIPTION_FEEDS_FULL = gql`
mutation SyncSubscriptionFeedsFull($filter: SubscriptionsFilterInput!) {
subscriptionsSyncOneFeedsFull(filter: $filter) {
id
}
}
`;
export const SYNC_SUBSCRIPTION_SOURCES = gql`
mutation SyncSubscriptionSources($filter: SubscriptionsFilterInput!) {
subscriptionsSyncOneSources(filter: $filter) {
id
}
}
`;
export const SubscriptionFormTypedMikanSeasonSchema = export const SubscriptionFormTypedMikanSeasonSchema =
MikanSubscriptionSeasonSourceUrlSchema.and( MikanSubscriptionSeasonSourceUrlSchema.and(
type({ type({

View File

@ -1,10 +1,5 @@
import { import type { GetTasksQuery } from '@/infra/graphql/gql/graphql';
type GetTasksQuery,
SubscriberTaskTypeEnum,
} from '@/infra/graphql/gql/graphql';
import { gql } from '@apollo/client'; import { gql } from '@apollo/client';
import { type } from 'arktype';
import { SubscriptionSchema } from './subscriptions';
export const GET_TASKS = gql` export const GET_TASKS = gql`
query GetTasks($filter: SubscriberTasksFilterInput!, $orderBy: SubscriberTasksOrderInput!, $pagination: PaginationInput!) { query GetTasks($filter: SubscriberTasksFilterInput!, $orderBy: SubscriberTasksOrderInput!, $pagination: PaginationInput!) {
@ -25,7 +20,11 @@ export const GET_TASKS = gql`
lockAt, lockAt,
lockBy, lockBy,
doneAt, doneAt,
priority priority,
subscription {
displayName
sourceUrl
}
} }
paginationInfo { paginationInfo {
total total
@ -35,6 +34,14 @@ export const GET_TASKS = gql`
} }
`; `;
export const INSERT_SUBSCRIBER_TASK = gql`
mutation InsertSubscriberTask($data: SubscriberTasksInsertInput!) {
subscriberTasksCreateOne(data: $data) {
id
}
}
`;
export const DELETE_TASKS = gql` export const DELETE_TASKS = gql`
mutation DeleteTasks($filter: SubscriberTasksFilterInput!) { mutation DeleteTasks($filter: SubscriberTasksFilterInput!) {
subscriberTasksDelete(filter: $filter) subscriberTasksDelete(filter: $filter)
@ -60,27 +67,4 @@ export const RETRY_TASKS = gql`
} }
`; `;
export const TaskTypedSyncOneSubscriptionFeedsIncrementalSchema = type({ export type TaskDto = GetTasksQuery['subscriberTasks']['nodes'][number];
taskType: `'${SubscriberTaskTypeEnum.SyncOneSubscriptionFeedsIncremental}'`,
}).and(SubscriptionSchema);
export const TaskTypedSyncOneSubscriptionFeedsFullSchema = type({
taskType: `'${SubscriberTaskTypeEnum.SyncOneSubscriptionFeedsFull}'`,
}).and(SubscriptionSchema);
export const TaskTypedSyncOneSubscriptionSourcesSchema = type({
taskType: `'${SubscriberTaskTypeEnum.SyncOneSubscriptionSources}'`,
}).and(SubscriptionSchema);
export const TaskTypedSchema = TaskTypedSyncOneSubscriptionFeedsFullSchema.or(
TaskTypedSyncOneSubscriptionFeedsIncrementalSchema
).or(TaskTypedSyncOneSubscriptionSourcesSchema);
export type TaskTypedDto = typeof TaskTypedSchema.infer;
export type TaskDto = Omit<
GetTasksQuery['subscriberTasks']['nodes'][number],
'job'
> & {
job: TaskTypedDto;
};

View File

@ -0,0 +1,7 @@
import { AuthService } from '@/domains/auth/auth.service';
import { Injectable, inject } from '@outposts/injection-js';
@Injectable()
export class SubscriberService {
authService = inject(AuthService);
}

View File

@ -26,11 +26,9 @@ type Documents = {
"\n mutation InsertSubscription($data: SubscriptionsInsertInput!) {\n subscriptionsCreateOne(data: $data) {\n id\n createdAt\n updatedAt\n displayName\n category\n sourceUrl\n enabled\n credentialId\n }\n }\n": typeof types.InsertSubscriptionDocument, "\n mutation InsertSubscription($data: SubscriptionsInsertInput!) {\n subscriptionsCreateOne(data: $data) {\n id\n createdAt\n updatedAt\n displayName\n category\n sourceUrl\n enabled\n credentialId\n }\n }\n": typeof types.InsertSubscriptionDocument,
"\n mutation UpdateSubscriptions(\n $data: SubscriptionsUpdateInput!,\n $filter: SubscriptionsFilterInput!,\n ) {\n subscriptionsUpdate (\n data: $data\n filter: $filter\n ) {\n id\n createdAt\n updatedAt\n displayName\n category\n sourceUrl\n enabled\n }\n}\n": typeof types.UpdateSubscriptionsDocument, "\n mutation UpdateSubscriptions(\n $data: SubscriptionsUpdateInput!,\n $filter: SubscriptionsFilterInput!,\n ) {\n subscriptionsUpdate (\n data: $data\n filter: $filter\n ) {\n id\n createdAt\n updatedAt\n displayName\n category\n sourceUrl\n enabled\n }\n}\n": typeof types.UpdateSubscriptionsDocument,
"\n mutation DeleteSubscriptions($filter: SubscriptionsFilterInput) {\n subscriptionsDelete(filter: $filter)\n }\n": typeof types.DeleteSubscriptionsDocument, "\n mutation DeleteSubscriptions($filter: SubscriptionsFilterInput) {\n subscriptionsDelete(filter: $filter)\n }\n": typeof types.DeleteSubscriptionsDocument,
"\nquery GetSubscriptionDetail ($id: Int!) {\n subscriptions(filter: { id: {\n eq: $id\n } }) {\n nodes {\n id\n displayName\n createdAt\n updatedAt\n category\n sourceUrl\n enabled\n feed {\n nodes {\n id\n createdAt\n updatedAt\n token\n feedType\n feedSource\n }\n }\n subscriberTask {\n nodes {\n id\n taskType\n status\n }\n }\n credential3rd {\n id\n username\n }\n bangumi {\n nodes {\n createdAt\n updatedAt\n id\n mikanBangumiId\n displayName\n season\n seasonRaw\n fansub\n mikanFansubId\n rssLink\n posterLink\n homepage\n }\n }\n }\n }\n}\n": typeof types.GetSubscriptionDetailDocument, "\nquery GetSubscriptionDetail ($id: Int!) {\n subscriptions(filter: { id: {\n eq: $id\n } }) {\n nodes {\n id\n subscriberId\n displayName\n createdAt\n updatedAt\n category\n sourceUrl\n enabled\n feed {\n nodes {\n id\n createdAt\n updatedAt\n token\n feedType\n feedSource\n }\n }\n subscriberTask {\n nodes {\n id\n taskType\n status\n }\n }\n credential3rd {\n id\n username\n }\n bangumi {\n nodes {\n createdAt\n updatedAt\n id\n mikanBangumiId\n displayName\n season\n seasonRaw\n fansub\n mikanFansubId\n rssLink\n posterLink\n homepage\n }\n }\n }\n }\n}\n": typeof types.GetSubscriptionDetailDocument,
"\n mutation SyncSubscriptionFeedsIncremental($filter: SubscriptionsFilterInput!) {\n subscriptionsSyncOneFeedsIncremental(filter: $filter) {\n id\n }\n }\n": typeof types.SyncSubscriptionFeedsIncrementalDocument, "\n query GetTasks($filter: SubscriberTasksFilterInput!, $orderBy: SubscriberTasksOrderInput!, $pagination: PaginationInput!) {\n subscriberTasks(\n pagination: $pagination\n filter: $filter\n orderBy: $orderBy\n ) {\n nodes {\n id,\n job,\n taskType,\n status,\n attempts,\n maxAttempts,\n runAt,\n lastError,\n lockAt,\n lockBy,\n doneAt,\n priority,\n subscription {\n displayName\n sourceUrl\n }\n }\n paginationInfo {\n total\n pages\n }\n }\n }\n": typeof types.GetTasksDocument,
"\n mutation SyncSubscriptionFeedsFull($filter: SubscriptionsFilterInput!) {\n subscriptionsSyncOneFeedsFull(filter: $filter) {\n id\n }\n }\n": typeof types.SyncSubscriptionFeedsFullDocument, "\n mutation InsertSubscriberTask($data: SubscriberTasksInsertInput!) {\n subscriberTasksCreateOne(data: $data) {\n id\n }\n }\n": typeof types.InsertSubscriberTaskDocument,
"\n mutation SyncSubscriptionSources($filter: SubscriptionsFilterInput!) {\n subscriptionsSyncOneSources(filter: $filter) {\n id\n }\n }\n": typeof types.SyncSubscriptionSourcesDocument,
"\n query GetTasks($filter: SubscriberTasksFilterInput!, $orderBy: SubscriberTasksOrderInput!, $pagination: PaginationInput!) {\n subscriberTasks(\n pagination: $pagination\n filter: $filter\n orderBy: $orderBy\n ) {\n nodes {\n id,\n job,\n taskType,\n status,\n attempts,\n maxAttempts,\n runAt,\n lastError,\n lockAt,\n lockBy,\n doneAt,\n priority\n }\n paginationInfo {\n total\n pages\n }\n }\n }\n": typeof types.GetTasksDocument,
"\n mutation DeleteTasks($filter: SubscriberTasksFilterInput!) {\n subscriberTasksDelete(filter: $filter)\n }\n": typeof types.DeleteTasksDocument, "\n mutation DeleteTasks($filter: SubscriberTasksFilterInput!) {\n subscriberTasksDelete(filter: $filter)\n }\n": typeof types.DeleteTasksDocument,
"\n mutation RetryTasks($filter: SubscriberTasksFilterInput!) {\n subscriberTasksRetryOne(filter: $filter) {\n id,\n job,\n taskType,\n status,\n attempts,\n maxAttempts,\n runAt,\n lastError,\n lockAt,\n lockBy,\n doneAt,\n priority\n }\n }\n": typeof types.RetryTasksDocument, "\n mutation RetryTasks($filter: SubscriberTasksFilterInput!) {\n subscriberTasksRetryOne(filter: $filter) {\n id,\n job,\n taskType,\n status,\n attempts,\n maxAttempts,\n runAt,\n lastError,\n lockAt,\n lockBy,\n doneAt,\n priority\n }\n }\n": typeof types.RetryTasksDocument,
}; };
@ -47,11 +45,9 @@ const documents: Documents = {
"\n mutation InsertSubscription($data: SubscriptionsInsertInput!) {\n subscriptionsCreateOne(data: $data) {\n id\n createdAt\n updatedAt\n displayName\n category\n sourceUrl\n enabled\n credentialId\n }\n }\n": types.InsertSubscriptionDocument, "\n mutation InsertSubscription($data: SubscriptionsInsertInput!) {\n subscriptionsCreateOne(data: $data) {\n id\n createdAt\n updatedAt\n displayName\n category\n sourceUrl\n enabled\n credentialId\n }\n }\n": types.InsertSubscriptionDocument,
"\n mutation UpdateSubscriptions(\n $data: SubscriptionsUpdateInput!,\n $filter: SubscriptionsFilterInput!,\n ) {\n subscriptionsUpdate (\n data: $data\n filter: $filter\n ) {\n id\n createdAt\n updatedAt\n displayName\n category\n sourceUrl\n enabled\n }\n}\n": types.UpdateSubscriptionsDocument, "\n mutation UpdateSubscriptions(\n $data: SubscriptionsUpdateInput!,\n $filter: SubscriptionsFilterInput!,\n ) {\n subscriptionsUpdate (\n data: $data\n filter: $filter\n ) {\n id\n createdAt\n updatedAt\n displayName\n category\n sourceUrl\n enabled\n }\n}\n": types.UpdateSubscriptionsDocument,
"\n mutation DeleteSubscriptions($filter: SubscriptionsFilterInput) {\n subscriptionsDelete(filter: $filter)\n }\n": types.DeleteSubscriptionsDocument, "\n mutation DeleteSubscriptions($filter: SubscriptionsFilterInput) {\n subscriptionsDelete(filter: $filter)\n }\n": types.DeleteSubscriptionsDocument,
"\nquery GetSubscriptionDetail ($id: Int!) {\n subscriptions(filter: { id: {\n eq: $id\n } }) {\n nodes {\n id\n displayName\n createdAt\n updatedAt\n category\n sourceUrl\n enabled\n feed {\n nodes {\n id\n createdAt\n updatedAt\n token\n feedType\n feedSource\n }\n }\n subscriberTask {\n nodes {\n id\n taskType\n status\n }\n }\n credential3rd {\n id\n username\n }\n bangumi {\n nodes {\n createdAt\n updatedAt\n id\n mikanBangumiId\n displayName\n season\n seasonRaw\n fansub\n mikanFansubId\n rssLink\n posterLink\n homepage\n }\n }\n }\n }\n}\n": types.GetSubscriptionDetailDocument, "\nquery GetSubscriptionDetail ($id: Int!) {\n subscriptions(filter: { id: {\n eq: $id\n } }) {\n nodes {\n id\n subscriberId\n displayName\n createdAt\n updatedAt\n category\n sourceUrl\n enabled\n feed {\n nodes {\n id\n createdAt\n updatedAt\n token\n feedType\n feedSource\n }\n }\n subscriberTask {\n nodes {\n id\n taskType\n status\n }\n }\n credential3rd {\n id\n username\n }\n bangumi {\n nodes {\n createdAt\n updatedAt\n id\n mikanBangumiId\n displayName\n season\n seasonRaw\n fansub\n mikanFansubId\n rssLink\n posterLink\n homepage\n }\n }\n }\n }\n}\n": types.GetSubscriptionDetailDocument,
"\n mutation SyncSubscriptionFeedsIncremental($filter: SubscriptionsFilterInput!) {\n subscriptionsSyncOneFeedsIncremental(filter: $filter) {\n id\n }\n }\n": types.SyncSubscriptionFeedsIncrementalDocument, "\n query GetTasks($filter: SubscriberTasksFilterInput!, $orderBy: SubscriberTasksOrderInput!, $pagination: PaginationInput!) {\n subscriberTasks(\n pagination: $pagination\n filter: $filter\n orderBy: $orderBy\n ) {\n nodes {\n id,\n job,\n taskType,\n status,\n attempts,\n maxAttempts,\n runAt,\n lastError,\n lockAt,\n lockBy,\n doneAt,\n priority,\n subscription {\n displayName\n sourceUrl\n }\n }\n paginationInfo {\n total\n pages\n }\n }\n }\n": types.GetTasksDocument,
"\n mutation SyncSubscriptionFeedsFull($filter: SubscriptionsFilterInput!) {\n subscriptionsSyncOneFeedsFull(filter: $filter) {\n id\n }\n }\n": types.SyncSubscriptionFeedsFullDocument, "\n mutation InsertSubscriberTask($data: SubscriberTasksInsertInput!) {\n subscriberTasksCreateOne(data: $data) {\n id\n }\n }\n": types.InsertSubscriberTaskDocument,
"\n mutation SyncSubscriptionSources($filter: SubscriptionsFilterInput!) {\n subscriptionsSyncOneSources(filter: $filter) {\n id\n }\n }\n": types.SyncSubscriptionSourcesDocument,
"\n query GetTasks($filter: SubscriberTasksFilterInput!, $orderBy: SubscriberTasksOrderInput!, $pagination: PaginationInput!) {\n subscriberTasks(\n pagination: $pagination\n filter: $filter\n orderBy: $orderBy\n ) {\n nodes {\n id,\n job,\n taskType,\n status,\n attempts,\n maxAttempts,\n runAt,\n lastError,\n lockAt,\n lockBy,\n doneAt,\n priority\n }\n paginationInfo {\n total\n pages\n }\n }\n }\n": types.GetTasksDocument,
"\n mutation DeleteTasks($filter: SubscriberTasksFilterInput!) {\n subscriberTasksDelete(filter: $filter)\n }\n": types.DeleteTasksDocument, "\n mutation DeleteTasks($filter: SubscriberTasksFilterInput!) {\n subscriberTasksDelete(filter: $filter)\n }\n": types.DeleteTasksDocument,
"\n mutation RetryTasks($filter: SubscriberTasksFilterInput!) {\n subscriberTasksRetryOne(filter: $filter) {\n id,\n job,\n taskType,\n status,\n attempts,\n maxAttempts,\n runAt,\n lastError,\n lockAt,\n lockBy,\n doneAt,\n priority\n }\n }\n": types.RetryTasksDocument, "\n mutation RetryTasks($filter: SubscriberTasksFilterInput!) {\n subscriberTasksRetryOne(filter: $filter) {\n id,\n job,\n taskType,\n status,\n attempts,\n maxAttempts,\n runAt,\n lastError,\n lockAt,\n lockBy,\n doneAt,\n priority\n }\n }\n": types.RetryTasksDocument,
}; };
@ -121,23 +117,15 @@ export function gql(source: "\n mutation DeleteSubscriptions($filter: Subscri
/** /**
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. * The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/ */
export function gql(source: "\nquery GetSubscriptionDetail ($id: Int!) {\n subscriptions(filter: { id: {\n eq: $id\n } }) {\n nodes {\n id\n displayName\n createdAt\n updatedAt\n category\n sourceUrl\n enabled\n feed {\n nodes {\n id\n createdAt\n updatedAt\n token\n feedType\n feedSource\n }\n }\n subscriberTask {\n nodes {\n id\n taskType\n status\n }\n }\n credential3rd {\n id\n username\n }\n bangumi {\n nodes {\n createdAt\n updatedAt\n id\n mikanBangumiId\n displayName\n season\n seasonRaw\n fansub\n mikanFansubId\n rssLink\n posterLink\n homepage\n }\n }\n }\n }\n}\n"): (typeof documents)["\nquery GetSubscriptionDetail ($id: Int!) {\n subscriptions(filter: { id: {\n eq: $id\n } }) {\n nodes {\n id\n displayName\n createdAt\n updatedAt\n category\n sourceUrl\n enabled\n feed {\n nodes {\n id\n createdAt\n updatedAt\n token\n feedType\n feedSource\n }\n }\n subscriberTask {\n nodes {\n id\n taskType\n status\n }\n }\n credential3rd {\n id\n username\n }\n bangumi {\n nodes {\n createdAt\n updatedAt\n id\n mikanBangumiId\n displayName\n season\n seasonRaw\n fansub\n mikanFansubId\n rssLink\n posterLink\n homepage\n }\n }\n }\n }\n}\n"]; export function gql(source: "\nquery GetSubscriptionDetail ($id: Int!) {\n subscriptions(filter: { id: {\n eq: $id\n } }) {\n nodes {\n id\n subscriberId\n displayName\n createdAt\n updatedAt\n category\n sourceUrl\n enabled\n feed {\n nodes {\n id\n createdAt\n updatedAt\n token\n feedType\n feedSource\n }\n }\n subscriberTask {\n nodes {\n id\n taskType\n status\n }\n }\n credential3rd {\n id\n username\n }\n bangumi {\n nodes {\n createdAt\n updatedAt\n id\n mikanBangumiId\n displayName\n season\n seasonRaw\n fansub\n mikanFansubId\n rssLink\n posterLink\n homepage\n }\n }\n }\n }\n}\n"): (typeof documents)["\nquery GetSubscriptionDetail ($id: Int!) {\n subscriptions(filter: { id: {\n eq: $id\n } }) {\n nodes {\n id\n subscriberId\n displayName\n createdAt\n updatedAt\n category\n sourceUrl\n enabled\n feed {\n nodes {\n id\n createdAt\n updatedAt\n token\n feedType\n feedSource\n }\n }\n subscriberTask {\n nodes {\n id\n taskType\n status\n }\n }\n credential3rd {\n id\n username\n }\n bangumi {\n nodes {\n createdAt\n updatedAt\n id\n mikanBangumiId\n displayName\n season\n seasonRaw\n fansub\n mikanFansubId\n rssLink\n posterLink\n homepage\n }\n }\n }\n }\n}\n"];
/** /**
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. * The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/ */
export function gql(source: "\n mutation SyncSubscriptionFeedsIncremental($filter: SubscriptionsFilterInput!) {\n subscriptionsSyncOneFeedsIncremental(filter: $filter) {\n id\n }\n }\n"): (typeof documents)["\n mutation SyncSubscriptionFeedsIncremental($filter: SubscriptionsFilterInput!) {\n subscriptionsSyncOneFeedsIncremental(filter: $filter) {\n id\n }\n }\n"]; export function gql(source: "\n query GetTasks($filter: SubscriberTasksFilterInput!, $orderBy: SubscriberTasksOrderInput!, $pagination: PaginationInput!) {\n subscriberTasks(\n pagination: $pagination\n filter: $filter\n orderBy: $orderBy\n ) {\n nodes {\n id,\n job,\n taskType,\n status,\n attempts,\n maxAttempts,\n runAt,\n lastError,\n lockAt,\n lockBy,\n doneAt,\n priority,\n subscription {\n displayName\n sourceUrl\n }\n }\n paginationInfo {\n total\n pages\n }\n }\n }\n"): (typeof documents)["\n query GetTasks($filter: SubscriberTasksFilterInput!, $orderBy: SubscriberTasksOrderInput!, $pagination: PaginationInput!) {\n subscriberTasks(\n pagination: $pagination\n filter: $filter\n orderBy: $orderBy\n ) {\n nodes {\n id,\n job,\n taskType,\n status,\n attempts,\n maxAttempts,\n runAt,\n lastError,\n lockAt,\n lockBy,\n doneAt,\n priority,\n subscription {\n displayName\n sourceUrl\n }\n }\n paginationInfo {\n total\n pages\n }\n }\n }\n"];
/** /**
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. * The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/ */
export function gql(source: "\n mutation SyncSubscriptionFeedsFull($filter: SubscriptionsFilterInput!) {\n subscriptionsSyncOneFeedsFull(filter: $filter) {\n id\n }\n }\n"): (typeof documents)["\n mutation SyncSubscriptionFeedsFull($filter: SubscriptionsFilterInput!) {\n subscriptionsSyncOneFeedsFull(filter: $filter) {\n id\n }\n }\n"]; export function gql(source: "\n mutation InsertSubscriberTask($data: SubscriberTasksInsertInput!) {\n subscriberTasksCreateOne(data: $data) {\n id\n }\n }\n"): (typeof documents)["\n mutation InsertSubscriberTask($data: SubscriberTasksInsertInput!) {\n subscriberTasksCreateOne(data: $data) {\n id\n }\n }\n"];
/**
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function gql(source: "\n mutation SyncSubscriptionSources($filter: SubscriptionsFilterInput!) {\n subscriptionsSyncOneSources(filter: $filter) {\n id\n }\n }\n"): (typeof documents)["\n mutation SyncSubscriptionSources($filter: SubscriptionsFilterInput!) {\n subscriptionsSyncOneSources(filter: $filter) {\n id\n }\n }\n"];
/**
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/
export function gql(source: "\n query GetTasks($filter: SubscriberTasksFilterInput!, $orderBy: SubscriberTasksOrderInput!, $pagination: PaginationInput!) {\n subscriberTasks(\n pagination: $pagination\n filter: $filter\n orderBy: $orderBy\n ) {\n nodes {\n id,\n job,\n taskType,\n status,\n attempts,\n maxAttempts,\n runAt,\n lastError,\n lockAt,\n lockBy,\n doneAt,\n priority\n }\n paginationInfo {\n total\n pages\n }\n }\n }\n"): (typeof documents)["\n query GetTasks($filter: SubscriberTasksFilterInput!, $orderBy: SubscriberTasksOrderInput!, $pagination: PaginationInput!) {\n subscriberTasks(\n pagination: $pagination\n filter: $filter\n orderBy: $orderBy\n ) {\n nodes {\n id,\n job,\n taskType,\n status,\n attempts,\n maxAttempts,\n runAt,\n lastError,\n lockAt,\n lockBy,\n doneAt,\n priority\n }\n paginationInfo {\n total\n pages\n }\n }\n }\n"];
/** /**
* The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. * The gql function is used to parse GraphQL queries into a document that can be used by GraphQL clients.
*/ */

View File

@ -1,4 +1,6 @@
/* eslint-disable */ /* eslint-disable */
import { SubscriberTaskInput } from 'recorder/bindings/SubscriberTaskInput';
import { SubscriberTaskType } from 'recorder/bindings/SubscriberTaskType';
import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'; import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core';
export type Maybe<T> = T | null; export type Maybe<T> = T | null;
export type InputMaybe<T> = Maybe<T>; export type InputMaybe<T> = Maybe<T>;
@ -17,6 +19,8 @@ export type Scalars = {
/** The `JSON` scalar type represents raw JSON values */ /** The `JSON` scalar type represents raw JSON values */
Json: { input: any; output: any; } Json: { input: any; output: any; }
JsonbFilterInput: { input: any; output: any; } JsonbFilterInput: { input: any; output: any; }
/** type SubscriberTaskType = { "taskType": "sync_one_subscription_feeds_incremental" } & SyncOneSubscriptionFeedsIncrementalTask | { "taskType": "sync_one_subscription_feeds_full" } & SyncOneSubscriptionFeedsFullTask | { "taskType": "sync_one_subscription_sources" } & SyncOneSubscriptionSourcesTask; */
SubscriberTaskType: { input: SubscriberTaskInput; output: SubscriberTaskType; }
}; };
export type Bangumi = { export type Bangumi = {
@ -350,7 +354,7 @@ export type Cron = {
status: CronStatusEnum; status: CronStatusEnum;
subscriber?: Maybe<Subscribers>; subscriber?: Maybe<Subscribers>;
subscriberId?: Maybe<Scalars['Int']['output']>; subscriberId?: Maybe<Scalars['Int']['output']>;
subscriberTask?: Maybe<Scalars['Json']['output']>; subscriberTask?: Maybe<Scalars['SubscriberTaskType']['output']>;
subscription?: Maybe<Subscriptions>; subscription?: Maybe<Subscriptions>;
subscriptionId?: Maybe<Scalars['Int']['output']>; subscriptionId?: Maybe<Scalars['Int']['output']>;
timeoutMs: Scalars['Int']['output']; timeoutMs: Scalars['Int']['output'];
@ -373,7 +377,7 @@ export type CronBasic = {
priority: Scalars['Int']['output']; priority: Scalars['Int']['output'];
status: CronStatusEnum; status: CronStatusEnum;
subscriberId?: Maybe<Scalars['Int']['output']>; subscriberId?: Maybe<Scalars['Int']['output']>;
subscriberTask?: Maybe<Scalars['Json']['output']>; subscriberTask?: Maybe<Scalars['SubscriberTaskType']['output']>;
subscriptionId?: Maybe<Scalars['Int']['output']>; subscriptionId?: Maybe<Scalars['Int']['output']>;
timeoutMs: Scalars['Int']['output']; timeoutMs: Scalars['Int']['output'];
updatedAt: Scalars['String']['output']; updatedAt: Scalars['String']['output'];
@ -419,9 +423,8 @@ export type CronFilterInput = {
export type CronInsertInput = { export type CronInsertInput = {
cronExpr: Scalars['String']['input']; cronExpr: Scalars['String']['input'];
enabled?: InputMaybe<Scalars['Boolean']['input']>; enabled?: InputMaybe<Scalars['Boolean']['input']>;
id?: InputMaybe<Scalars['Int']['input']>;
maxAttempts?: InputMaybe<Scalars['Int']['input']>; maxAttempts?: InputMaybe<Scalars['Int']['input']>;
subscriberTask?: InputMaybe<Scalars['Json']['input']>; subscriberTask?: InputMaybe<Scalars['SubscriberTaskType']['input']>;
timeoutMs?: InputMaybe<Scalars['Int']['input']>; timeoutMs?: InputMaybe<Scalars['Int']['input']>;
}; };
@ -1166,9 +1169,6 @@ export type Mutation = {
subscriptionsCreateBatch: Array<SubscriptionsBasic>; subscriptionsCreateBatch: Array<SubscriptionsBasic>;
subscriptionsCreateOne: SubscriptionsBasic; subscriptionsCreateOne: SubscriptionsBasic;
subscriptionsDelete: Scalars['Int']['output']; subscriptionsDelete: Scalars['Int']['output'];
subscriptionsSyncOneFeedsFull: SubscriberTasksBasic;
subscriptionsSyncOneFeedsIncremental: SubscriberTasksBasic;
subscriptionsSyncOneSources: SubscriberTasksBasic;
subscriptionsUpdate: Array<SubscriptionsBasic>; subscriptionsUpdate: Array<SubscriptionsBasic>;
}; };
@ -1397,21 +1397,6 @@ export type MutationSubscriptionsDeleteArgs = {
}; };
export type MutationSubscriptionsSyncOneFeedsFullArgs = {
filter?: InputMaybe<SubscriptionsFilterInput>;
};
export type MutationSubscriptionsSyncOneFeedsIncrementalArgs = {
filter?: InputMaybe<SubscriptionsFilterInput>;
};
export type MutationSubscriptionsSyncOneSourcesArgs = {
filter?: InputMaybe<SubscriptionsFilterInput>;
};
export type MutationSubscriptionsUpdateArgs = { export type MutationSubscriptionsUpdateArgs = {
data: SubscriptionsUpdateInput; data: SubscriptionsUpdateInput;
filter?: InputMaybe<SubscriptionsFilterInput>; filter?: InputMaybe<SubscriptionsFilterInput>;
@ -1607,7 +1592,7 @@ export type SubscriberTasks = {
attempts: Scalars['Int']['output']; attempts: Scalars['Int']['output'];
doneAt?: Maybe<Scalars['String']['output']>; doneAt?: Maybe<Scalars['String']['output']>;
id: Scalars['String']['output']; id: Scalars['String']['output'];
job: Scalars['Json']['output']; job: Scalars['SubscriberTaskType']['output'];
lastError?: Maybe<Scalars['String']['output']>; lastError?: Maybe<Scalars['String']['output']>;
lockAt?: Maybe<Scalars['String']['output']>; lockAt?: Maybe<Scalars['String']['output']>;
lockBy?: Maybe<Scalars['String']['output']>; lockBy?: Maybe<Scalars['String']['output']>;
@ -1627,7 +1612,7 @@ export type SubscriberTasksBasic = {
attempts: Scalars['Int']['output']; attempts: Scalars['Int']['output'];
doneAt?: Maybe<Scalars['String']['output']>; doneAt?: Maybe<Scalars['String']['output']>;
id: Scalars['String']['output']; id: Scalars['String']['output'];
job: Scalars['Json']['output']; job: Scalars['SubscriberTaskType']['output'];
lastError?: Maybe<Scalars['String']['output']>; lastError?: Maybe<Scalars['String']['output']>;
lockAt?: Maybe<Scalars['String']['output']>; lockAt?: Maybe<Scalars['String']['output']>;
lockBy?: Maybe<Scalars['String']['output']>; lockBy?: Maybe<Scalars['String']['output']>;
@ -1674,10 +1659,7 @@ export type SubscriberTasksFilterInput = {
}; };
export type SubscriberTasksInsertInput = { export type SubscriberTasksInsertInput = {
id?: InputMaybe<Scalars['String']['input']>; job: Scalars['SubscriberTaskType']['input'];
job: Scalars['Json']['input'];
maxAttempts: Scalars['Int']['input'];
priority: Scalars['Int']['input'];
subscriberId?: InputMaybe<Scalars['Int']['input']>; subscriberId?: InputMaybe<Scalars['Int']['input']>;
}; };
@ -2184,28 +2166,7 @@ export type GetSubscriptionDetailQueryVariables = Exact<{
}>; }>;
export type GetSubscriptionDetailQuery = { __typename?: 'Query', subscriptions: { __typename?: 'SubscriptionsConnection', nodes: Array<{ __typename?: 'Subscriptions', id: number, displayName: string, createdAt: string, updatedAt: string, category: SubscriptionCategoryEnum, sourceUrl: string, enabled: boolean, feed: { __typename?: 'FeedsConnection', nodes: Array<{ __typename?: 'Feeds', id: number, createdAt: string, updatedAt: string, token: string, feedType: FeedTypeEnum, feedSource: FeedSourceEnum }> }, subscriberTask: { __typename?: 'SubscriberTasksConnection', nodes: Array<{ __typename?: 'SubscriberTasks', id: string, taskType: SubscriberTaskTypeEnum, status: SubscriberTaskStatusEnum }> }, credential3rd?: { __typename?: 'Credential3rd', id: number, username?: string | null } | null, bangumi: { __typename?: 'BangumiConnection', nodes: Array<{ __typename?: 'Bangumi', createdAt: string, updatedAt: string, id: number, mikanBangumiId?: string | null, displayName: string, season: number, seasonRaw?: string | null, fansub?: string | null, mikanFansubId?: string | null, rssLink?: string | null, posterLink?: string | null, homepage?: string | null }> } }> } }; export type GetSubscriptionDetailQuery = { __typename?: 'Query', subscriptions: { __typename?: 'SubscriptionsConnection', nodes: Array<{ __typename?: 'Subscriptions', id: number, subscriberId: number, displayName: string, createdAt: string, updatedAt: string, category: SubscriptionCategoryEnum, sourceUrl: string, enabled: boolean, feed: { __typename?: 'FeedsConnection', nodes: Array<{ __typename?: 'Feeds', id: number, createdAt: string, updatedAt: string, token: string, feedType: FeedTypeEnum, feedSource: FeedSourceEnum }> }, subscriberTask: { __typename?: 'SubscriberTasksConnection', nodes: Array<{ __typename?: 'SubscriberTasks', id: string, taskType: SubscriberTaskTypeEnum, status: SubscriberTaskStatusEnum }> }, credential3rd?: { __typename?: 'Credential3rd', id: number, username?: string | null } | null, bangumi: { __typename?: 'BangumiConnection', nodes: Array<{ __typename?: 'Bangumi', createdAt: string, updatedAt: string, id: number, mikanBangumiId?: string | null, displayName: string, season: number, seasonRaw?: string | null, fansub?: string | null, mikanFansubId?: string | null, rssLink?: string | null, posterLink?: string | null, homepage?: string | null }> } }> } };
export type SyncSubscriptionFeedsIncrementalMutationVariables = Exact<{
filter: SubscriptionsFilterInput;
}>;
export type SyncSubscriptionFeedsIncrementalMutation = { __typename?: 'Mutation', subscriptionsSyncOneFeedsIncremental: { __typename?: 'SubscriberTasksBasic', id: string } };
export type SyncSubscriptionFeedsFullMutationVariables = Exact<{
filter: SubscriptionsFilterInput;
}>;
export type SyncSubscriptionFeedsFullMutation = { __typename?: 'Mutation', subscriptionsSyncOneFeedsFull: { __typename?: 'SubscriberTasksBasic', id: string } };
export type SyncSubscriptionSourcesMutationVariables = Exact<{
filter: SubscriptionsFilterInput;
}>;
export type SyncSubscriptionSourcesMutation = { __typename?: 'Mutation', subscriptionsSyncOneSources: { __typename?: 'SubscriberTasksBasic', id: string } };
export type GetTasksQueryVariables = Exact<{ export type GetTasksQueryVariables = Exact<{
filter: SubscriberTasksFilterInput; filter: SubscriberTasksFilterInput;
@ -2214,7 +2175,14 @@ export type GetTasksQueryVariables = Exact<{
}>; }>;
export type GetTasksQuery = { __typename?: 'Query', subscriberTasks: { __typename?: 'SubscriberTasksConnection', nodes: Array<{ __typename?: 'SubscriberTasks', id: string, job: any, taskType: SubscriberTaskTypeEnum, status: SubscriberTaskStatusEnum, attempts: number, maxAttempts: number, runAt: string, lastError?: string | null, lockAt?: string | null, lockBy?: string | null, doneAt?: string | null, priority: number }>, paginationInfo?: { __typename?: 'PaginationInfo', total: number, pages: number } | null } }; export type GetTasksQuery = { __typename?: 'Query', subscriberTasks: { __typename?: 'SubscriberTasksConnection', nodes: Array<{ __typename?: 'SubscriberTasks', id: string, job: SubscriberTaskType, taskType: SubscriberTaskTypeEnum, status: SubscriberTaskStatusEnum, attempts: number, maxAttempts: number, runAt: string, lastError?: string | null, lockAt?: string | null, lockBy?: string | null, doneAt?: string | null, priority: number, subscription?: { __typename?: 'Subscriptions', displayName: string, sourceUrl: string } | null }>, paginationInfo?: { __typename?: 'PaginationInfo', total: number, pages: number } | null } };
export type InsertSubscriberTaskMutationVariables = Exact<{
data: SubscriberTasksInsertInput;
}>;
export type InsertSubscriberTaskMutation = { __typename?: 'Mutation', subscriberTasksCreateOne: { __typename?: 'SubscriberTasksBasic', id: string } };
export type DeleteTasksMutationVariables = Exact<{ export type DeleteTasksMutationVariables = Exact<{
filter: SubscriberTasksFilterInput; filter: SubscriberTasksFilterInput;
@ -2228,7 +2196,7 @@ export type RetryTasksMutationVariables = Exact<{
}>; }>;
export type RetryTasksMutation = { __typename?: 'Mutation', subscriberTasksRetryOne: { __typename?: 'SubscriberTasksBasic', id: string, job: any, taskType: SubscriberTaskTypeEnum, status: SubscriberTaskStatusEnum, attempts: number, maxAttempts: number, runAt: string, lastError?: string | null, lockAt?: string | null, lockBy?: string | null, doneAt?: string | null, priority: number } }; export type RetryTasksMutation = { __typename?: 'Mutation', subscriberTasksRetryOne: { __typename?: 'SubscriberTasksBasic', id: string, job: SubscriberTaskType, taskType: SubscriberTaskTypeEnum, status: SubscriberTaskStatusEnum, attempts: number, maxAttempts: number, runAt: string, lastError?: string | null, lockAt?: string | null, lockBy?: string | null, doneAt?: string | null, priority: number } };
export const GetCredential3rdDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetCredential3rd"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Credential3rdFilterInput"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orderBy"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Credential3rdOrderInput"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"pagination"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"PaginationInput"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"credential3rd"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}},{"kind":"Argument","name":{"kind":"Name","value":"orderBy"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orderBy"}}},{"kind":"Argument","name":{"kind":"Name","value":"pagination"},"value":{"kind":"Variable","name":{"kind":"Name","value":"pagination"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"cookies"}},{"kind":"Field","name":{"kind":"Name","value":"username"}},{"kind":"Field","name":{"kind":"Name","value":"password"}},{"kind":"Field","name":{"kind":"Name","value":"userAgent"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"credentialType"}}]}},{"kind":"Field","name":{"kind":"Name","value":"paginationInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"}},{"kind":"Field","name":{"kind":"Name","value":"pages"}}]}}]}}]}}]} as unknown as DocumentNode<GetCredential3rdQuery, GetCredential3rdQueryVariables>; export const GetCredential3rdDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetCredential3rd"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Credential3rdFilterInput"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orderBy"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Credential3rdOrderInput"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"pagination"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"PaginationInput"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"credential3rd"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}},{"kind":"Argument","name":{"kind":"Name","value":"orderBy"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orderBy"}}},{"kind":"Argument","name":{"kind":"Name","value":"pagination"},"value":{"kind":"Variable","name":{"kind":"Name","value":"pagination"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"cookies"}},{"kind":"Field","name":{"kind":"Name","value":"username"}},{"kind":"Field","name":{"kind":"Name","value":"password"}},{"kind":"Field","name":{"kind":"Name","value":"userAgent"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"credentialType"}}]}},{"kind":"Field","name":{"kind":"Name","value":"paginationInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"}},{"kind":"Field","name":{"kind":"Name","value":"pages"}}]}}]}}]}}]} as unknown as DocumentNode<GetCredential3rdQuery, GetCredential3rdQueryVariables>;
@ -2243,10 +2211,8 @@ export const GetSubscriptionsDocument = {"kind":"Document","definitions":[{"kind
export const InsertSubscriptionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"InsertSubscription"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"data"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SubscriptionsInsertInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subscriptionsCreateOne"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"data"},"value":{"kind":"Variable","name":{"kind":"Name","value":"data"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"category"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrl"}},{"kind":"Field","name":{"kind":"Name","value":"enabled"}},{"kind":"Field","name":{"kind":"Name","value":"credentialId"}}]}}]}}]} as unknown as DocumentNode<InsertSubscriptionMutation, InsertSubscriptionMutationVariables>; export const InsertSubscriptionDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"InsertSubscription"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"data"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SubscriptionsInsertInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subscriptionsCreateOne"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"data"},"value":{"kind":"Variable","name":{"kind":"Name","value":"data"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"category"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrl"}},{"kind":"Field","name":{"kind":"Name","value":"enabled"}},{"kind":"Field","name":{"kind":"Name","value":"credentialId"}}]}}]}}]} as unknown as DocumentNode<InsertSubscriptionMutation, InsertSubscriptionMutationVariables>;
export const UpdateSubscriptionsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateSubscriptions"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"data"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SubscriptionsUpdateInput"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SubscriptionsFilterInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subscriptionsUpdate"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"data"},"value":{"kind":"Variable","name":{"kind":"Name","value":"data"}}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"category"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrl"}},{"kind":"Field","name":{"kind":"Name","value":"enabled"}}]}}]}}]} as unknown as DocumentNode<UpdateSubscriptionsMutation, UpdateSubscriptionsMutationVariables>; export const UpdateSubscriptionsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"UpdateSubscriptions"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"data"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SubscriptionsUpdateInput"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SubscriptionsFilterInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subscriptionsUpdate"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"data"},"value":{"kind":"Variable","name":{"kind":"Name","value":"data"}}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"category"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrl"}},{"kind":"Field","name":{"kind":"Name","value":"enabled"}}]}}]}}]} as unknown as DocumentNode<UpdateSubscriptionsMutation, UpdateSubscriptionsMutationVariables>;
export const DeleteSubscriptionsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteSubscriptions"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"SubscriptionsFilterInput"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subscriptionsDelete"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}}]}]}}]} as unknown as DocumentNode<DeleteSubscriptionsMutation, DeleteSubscriptionsMutationVariables>; export const DeleteSubscriptionsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteSubscriptions"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"SubscriptionsFilterInput"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subscriptionsDelete"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}}]}]}}]} as unknown as DocumentNode<DeleteSubscriptionsMutation, DeleteSubscriptionsMutationVariables>;
export const GetSubscriptionDetailDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetSubscriptionDetail"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subscriptions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"id"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}]}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"category"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrl"}},{"kind":"Field","name":{"kind":"Name","value":"enabled"}},{"kind":"Field","name":{"kind":"Name","value":"feed"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"token"}},{"kind":"Field","name":{"kind":"Name","value":"feedType"}},{"kind":"Field","name":{"kind":"Name","value":"feedSource"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscriberTask"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"taskType"}},{"kind":"Field","name":{"kind":"Name","value":"status"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"credential3rd"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"username"}}]}},{"kind":"Field","name":{"kind":"Name","value":"bangumi"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"mikanBangumiId"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"season"}},{"kind":"Field","name":{"kind":"Name","value":"seasonRaw"}},{"kind":"Field","name":{"kind":"Name","value":"fansub"}},{"kind":"Field","name":{"kind":"Name","value":"mikanFansubId"}},{"kind":"Field","name":{"kind":"Name","value":"rssLink"}},{"kind":"Field","name":{"kind":"Name","value":"posterLink"}},{"kind":"Field","name":{"kind":"Name","value":"homepage"}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode<GetSubscriptionDetailQuery, GetSubscriptionDetailQueryVariables>; export const GetSubscriptionDetailDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetSubscriptionDetail"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subscriptions"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"id"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"eq"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}]}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"subscriberId"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"category"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrl"}},{"kind":"Field","name":{"kind":"Name","value":"enabled"}},{"kind":"Field","name":{"kind":"Name","value":"feed"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"token"}},{"kind":"Field","name":{"kind":"Name","value":"feedType"}},{"kind":"Field","name":{"kind":"Name","value":"feedSource"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"subscriberTask"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"taskType"}},{"kind":"Field","name":{"kind":"Name","value":"status"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"credential3rd"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"username"}}]}},{"kind":"Field","name":{"kind":"Name","value":"bangumi"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}},{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"mikanBangumiId"}},{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"season"}},{"kind":"Field","name":{"kind":"Name","value":"seasonRaw"}},{"kind":"Field","name":{"kind":"Name","value":"fansub"}},{"kind":"Field","name":{"kind":"Name","value":"mikanFansubId"}},{"kind":"Field","name":{"kind":"Name","value":"rssLink"}},{"kind":"Field","name":{"kind":"Name","value":"posterLink"}},{"kind":"Field","name":{"kind":"Name","value":"homepage"}}]}}]}}]}}]}}]}}]} as unknown as DocumentNode<GetSubscriptionDetailQuery, GetSubscriptionDetailQueryVariables>;
export const SyncSubscriptionFeedsIncrementalDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"SyncSubscriptionFeedsIncremental"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SubscriptionsFilterInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subscriptionsSyncOneFeedsIncremental"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode<SyncSubscriptionFeedsIncrementalMutation, SyncSubscriptionFeedsIncrementalMutationVariables>; export const GetTasksDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetTasks"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SubscriberTasksFilterInput"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orderBy"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SubscriberTasksOrderInput"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"pagination"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"PaginationInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subscriberTasks"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"pagination"},"value":{"kind":"Variable","name":{"kind":"Name","value":"pagination"}}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}},{"kind":"Argument","name":{"kind":"Name","value":"orderBy"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orderBy"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"job"}},{"kind":"Field","name":{"kind":"Name","value":"taskType"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"attempts"}},{"kind":"Field","name":{"kind":"Name","value":"maxAttempts"}},{"kind":"Field","name":{"kind":"Name","value":"runAt"}},{"kind":"Field","name":{"kind":"Name","value":"lastError"}},{"kind":"Field","name":{"kind":"Name","value":"lockAt"}},{"kind":"Field","name":{"kind":"Name","value":"lockBy"}},{"kind":"Field","name":{"kind":"Name","value":"doneAt"}},{"kind":"Field","name":{"kind":"Name","value":"priority"}},{"kind":"Field","name":{"kind":"Name","value":"subscription"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"displayName"}},{"kind":"Field","name":{"kind":"Name","value":"sourceUrl"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"paginationInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"}},{"kind":"Field","name":{"kind":"Name","value":"pages"}}]}}]}}]}}]} as unknown as DocumentNode<GetTasksQuery, GetTasksQueryVariables>;
export const SyncSubscriptionFeedsFullDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"SyncSubscriptionFeedsFull"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SubscriptionsFilterInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subscriptionsSyncOneFeedsFull"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode<SyncSubscriptionFeedsFullMutation, SyncSubscriptionFeedsFullMutationVariables>; export const InsertSubscriberTaskDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"InsertSubscriberTask"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"data"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SubscriberTasksInsertInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subscriberTasksCreateOne"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"data"},"value":{"kind":"Variable","name":{"kind":"Name","value":"data"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode<InsertSubscriberTaskMutation, InsertSubscriberTaskMutationVariables>;
export const SyncSubscriptionSourcesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"SyncSubscriptionSources"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SubscriptionsFilterInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subscriptionsSyncOneSources"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}}]}}]}}]} as unknown as DocumentNode<SyncSubscriptionSourcesMutation, SyncSubscriptionSourcesMutationVariables>;
export const GetTasksDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetTasks"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SubscriberTasksFilterInput"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"orderBy"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SubscriberTasksOrderInput"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"pagination"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"PaginationInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subscriberTasks"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"pagination"},"value":{"kind":"Variable","name":{"kind":"Name","value":"pagination"}}},{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}},{"kind":"Argument","name":{"kind":"Name","value":"orderBy"},"value":{"kind":"Variable","name":{"kind":"Name","value":"orderBy"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"nodes"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"job"}},{"kind":"Field","name":{"kind":"Name","value":"taskType"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"attempts"}},{"kind":"Field","name":{"kind":"Name","value":"maxAttempts"}},{"kind":"Field","name":{"kind":"Name","value":"runAt"}},{"kind":"Field","name":{"kind":"Name","value":"lastError"}},{"kind":"Field","name":{"kind":"Name","value":"lockAt"}},{"kind":"Field","name":{"kind":"Name","value":"lockBy"}},{"kind":"Field","name":{"kind":"Name","value":"doneAt"}},{"kind":"Field","name":{"kind":"Name","value":"priority"}}]}},{"kind":"Field","name":{"kind":"Name","value":"paginationInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"total"}},{"kind":"Field","name":{"kind":"Name","value":"pages"}}]}}]}}]}}]} as unknown as DocumentNode<GetTasksQuery, GetTasksQueryVariables>;
export const DeleteTasksDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteTasks"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SubscriberTasksFilterInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subscriberTasksDelete"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}}]}]}}]} as unknown as DocumentNode<DeleteTasksMutation, DeleteTasksMutationVariables>; export const DeleteTasksDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"DeleteTasks"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SubscriberTasksFilterInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subscriberTasksDelete"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}}]}]}}]} as unknown as DocumentNode<DeleteTasksMutation, DeleteTasksMutationVariables>;
export const RetryTasksDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"RetryTasks"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SubscriberTasksFilterInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subscriberTasksRetryOne"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"job"}},{"kind":"Field","name":{"kind":"Name","value":"taskType"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"attempts"}},{"kind":"Field","name":{"kind":"Name","value":"maxAttempts"}},{"kind":"Field","name":{"kind":"Name","value":"runAt"}},{"kind":"Field","name":{"kind":"Name","value":"lastError"}},{"kind":"Field","name":{"kind":"Name","value":"lockAt"}},{"kind":"Field","name":{"kind":"Name","value":"lockBy"}},{"kind":"Field","name":{"kind":"Name","value":"doneAt"}},{"kind":"Field","name":{"kind":"Name","value":"priority"}}]}}]}}]} as unknown as DocumentNode<RetryTasksMutation, RetryTasksMutationVariables>; export const RetryTasksDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"RetryTasks"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"SubscriberTasksFilterInput"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"subscriberTasksRetryOne"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"job"}},{"kind":"Field","name":{"kind":"Name","value":"taskType"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"attempts"}},{"kind":"Field","name":{"kind":"Name","value":"maxAttempts"}},{"kind":"Field","name":{"kind":"Name","value":"runAt"}},{"kind":"Field","name":{"kind":"Name","value":"lastError"}},{"kind":"Field","name":{"kind":"Name","value":"lockAt"}},{"kind":"Field","name":{"kind":"Name","value":"lockBy"}},{"kind":"Field","name":{"kind":"Name","value":"doneAt"}},{"kind":"Field","name":{"kind":"Name","value":"priority"}}]}}]}}]} as unknown as DocumentNode<RetryTasksMutation, RetryTasksMutationVariables>;

View File

@ -6,18 +6,11 @@ import {
DialogTitle, DialogTitle,
} from '@/components/ui/dialog'; } from '@/components/ui/dialog';
import { Spinner } from '@/components/ui/spinner'; import { Spinner } from '@/components/ui/spinner';
import { INSERT_SUBSCRIBER_TASK } from '@/domains/recorder/schema/tasks';
import { import {
SYNC_SUBSCRIPTION_FEEDS_FULL, type InsertSubscriberTaskMutation,
SYNC_SUBSCRIPTION_FEEDS_INCREMENTAL, type InsertSubscriberTaskMutationVariables,
SYNC_SUBSCRIPTION_SOURCES, SubscriberTaskTypeEnum,
} from '@/domains/recorder/schema/subscriptions';
import type {
SyncSubscriptionFeedsFullMutation,
SyncSubscriptionFeedsFullMutationVariables,
SyncSubscriptionFeedsIncrementalMutation,
SyncSubscriptionFeedsIncrementalMutationVariables,
SyncSubscriptionSourcesMutation,
SyncSubscriptionSourcesMutationVariables,
} from '@/infra/graphql/gql/graphql'; } from '@/infra/graphql/gql/graphql';
import { useMutation } from '@apollo/client'; import { useMutation } from '@apollo/client';
import { useNavigate } from '@tanstack/react-router'; import { useNavigate } from '@tanstack/react-router';
@ -36,29 +29,13 @@ export interface SubscriptionSyncViewProps {
export const SubscriptionSyncView = memo( export const SubscriptionSyncView = memo(
({ id, onComplete }: SubscriptionSyncViewProps) => { ({ id, onComplete }: SubscriptionSyncViewProps) => {
const [syncSubscriptionFeedsIncremental, { loading: loadingIncremental }] = const [insertSubscriberTask, { loading: loadingInsert }] = useMutation<
useMutation< InsertSubscriberTaskMutation,
SyncSubscriptionFeedsIncrementalMutation, InsertSubscriberTaskMutationVariables
SyncSubscriptionFeedsIncrementalMutationVariables >(INSERT_SUBSCRIBER_TASK, {
>(SYNC_SUBSCRIPTION_FEEDS_INCREMENTAL, {
onCompleted: (data) => {
toast.success('Sync completed');
onComplete(data.subscriptionsSyncOneFeedsIncremental);
},
onError: (error) => {
toast.error('Failed to sync subscription', {
description: error.message,
});
},
});
const [syncSubscriptionFeedsFull, { loading: loadingFull }] = useMutation<
SyncSubscriptionFeedsFullMutation,
SyncSubscriptionFeedsFullMutationVariables
>(SYNC_SUBSCRIPTION_FEEDS_FULL, {
onCompleted: (data) => { onCompleted: (data) => {
toast.success('Sync completed'); toast.success('Sync completed');
onComplete(data.subscriptionsSyncOneFeedsFull); onComplete(data.subscriberTasksCreateOne);
}, },
onError: (error) => { onError: (error) => {
toast.error('Failed to sync subscription', { toast.error('Failed to sync subscription', {
@ -67,22 +44,7 @@ export const SubscriptionSyncView = memo(
}, },
}); });
const [syncSubscriptionSources, { loading: loadingSources }] = useMutation< const loading = loadingInsert;
SyncSubscriptionSourcesMutation,
SyncSubscriptionSourcesMutationVariables
>(SYNC_SUBSCRIPTION_SOURCES, {
onCompleted: (data) => {
toast.success('Sync completed');
onComplete(data.subscriptionsSyncOneSources);
},
onError: (error) => {
toast.error('Failed to sync subscription', {
description: error.message,
});
},
});
const loading = loadingIncremental || loadingFull || loadingSources;
return ( return (
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
@ -90,8 +52,15 @@ export const SubscriptionSyncView = memo(
size="lg" size="lg"
variant="outline" variant="outline"
onClick={() => onClick={() =>
syncSubscriptionSources({ insertSubscriberTask({
variables: { filter: { id: { eq: id } } }, variables: {
data: {
job: {
subscriptionId: id,
taskType: SubscriberTaskTypeEnum.SyncOneSubscriptionSources,
},
},
},
}) })
} }
> >
@ -102,8 +71,16 @@ export const SubscriptionSyncView = memo(
size="lg" size="lg"
variant="outline" variant="outline"
onClick={() => onClick={() =>
syncSubscriptionFeedsIncremental({ insertSubscriberTask({
variables: { filter: { id: { eq: id } } }, variables: {
data: {
job: {
subscriptionId: id,
taskType:
SubscriberTaskTypeEnum.SyncOneSubscriptionFeedsIncremental,
},
},
},
}) })
} }
> >
@ -114,8 +91,16 @@ export const SubscriptionSyncView = memo(
size="lg" size="lg"
variant="outline" variant="outline"
onClick={() => onClick={() =>
syncSubscriptionFeedsFull({ insertSubscriberTask({
variables: { filter: { id: { eq: id } } }, variables: {
data: {
job: {
subscriptionId: id,
taskType:
SubscriberTaskTypeEnum.SyncOneSubscriptionFeedsFull,
},
},
},
}) })
} }
> >

View File

@ -32,6 +32,7 @@ import {
} from '@tanstack/react-router'; } from '@tanstack/react-router';
import { format } from 'date-fns'; import { format } from 'date-fns';
import { ArrowLeft, RefreshCw } from 'lucide-react'; import { ArrowLeft, RefreshCw } from 'lucide-react';
import { useMemo } from 'react';
import { toast } from 'sonner'; import { toast } from 'sonner';
import { prettyTaskType } from './-pretty-task-type'; import { prettyTaskType } from './-pretty-task-type';
import { getStatusBadge } from './-status-badge'; import { getStatusBadge } from './-status-badge';
@ -104,6 +105,16 @@ function TaskDetailRouteComponent() {
}, },
}); });
const job = useMemo(() => {
if (!task) {
return null;
}
return {
...task.job,
subscription: task.subscription,
};
}, [task]);
if (loading) { if (loading) {
return <DetailCardSkeleton />; return <DetailCardSkeleton />;
} }
@ -247,14 +258,14 @@ function TaskDetailRouteComponent() {
</div> </div>
{/* Job Details */} {/* Job Details */}
{task.job && ( {job && (
<> <>
<Separator /> <Separator />
<div className="space-y-2"> <div className="space-y-2">
<Label className="font-medium text-sm">Job Details</Label> <Label className="font-medium text-sm">Job Details</Label>
<div className="rounded-md bg-muted p-3"> <div className="rounded-md bg-muted p-3">
<pre className="overflow-x-auto whitespace-pre-wrap text-sm"> <pre className="overflow-x-auto whitespace-pre-wrap text-sm">
<code>{JSON.stringify(task.job, null, 2)}</code> <code>{JSON.stringify(job, null, 2)}</code>
</pre> </pre>
</div> </div>
</div> </div>

View File

@ -11,5 +11,10 @@
"@/*": ["./src/*"] "@/*": ["./src/*"]
} }
}, },
"include": ["src"] "include": ["src"],
"references": [
{
"path": "../../apps/recorder"
}
]
} }

View File

@ -11,8 +11,8 @@ prepare-dev-testcontainers:
docker pull ghcr.io/dumtruck/konobangu-testing-torrents:latest docker pull ghcr.io/dumtruck/konobangu-testing-torrents:latest
docker pull postgres:17-alpine docker pull postgres:17-alpine
dev-optimize-images: export-recorder-ts-bindings:
npx -y zx apps/recorder/examples/optimize_image.mjs cargo test export_bindings -p recorder
dev-webui: dev-webui:
pnpm run --filter=webui dev pnpm run --filter=webui dev
@ -27,7 +27,7 @@ dev-proxy:
pnpm run --parallel --filter=proxy dev pnpm run --parallel --filter=proxy dev
dev-recorder: dev-recorder:
watchexec -r -e rs,toml,yaml,json,env -- cargo run -p recorder --bin recorder_cli -- --environment=development --graceful-shutdown=false watchexec -r -e rs,toml,yaml,json -- cargo run -p recorder --bin recorder_cli -- --environment=development --graceful-shutdown=false
prod-recorder: prod-webui prod-recorder: prod-webui
cargo run --release -p recorder --bin recorder_cli -- --environment=production --working-dir=apps/recorder --graceful-shutdown=false cargo run --release -p recorder --bin recorder_cli -- --environment=production --working-dir=apps/recorder --graceful-shutdown=false

View File

@ -3,8 +3,9 @@ extern crate proc_macro;
use convert_case::{Case, Casing}; use convert_case::{Case, Casing};
use darling::{FromDeriveInput, FromField, ast::Data, util::Ignored}; use darling::{FromDeriveInput, FromField, ast::Data, util::Ignored};
use proc_macro::TokenStream; use proc_macro::TokenStream;
use proc_macro2::Ident;
use quote::{format_ident, quote}; use quote::{format_ident, quote};
use syn::{Attribute, DeriveInput, Generics, Ident, parse_macro_input}; use syn::{Attribute, DeriveInput, Generics, parse_macro_input};
#[derive(snafu::Snafu, Debug)] #[derive(snafu::Snafu, Debug)]
enum GeneratorError { enum GeneratorError {

5
pnpm-lock.yaml generated
View File

@ -67,6 +67,8 @@ importers:
specifier: ^2.9.99 specifier: ^2.9.99
version: 2.9.99 version: 2.9.99
apps/recorder: {}
apps/webui: apps/webui:
dependencies: dependencies:
'@abraham/reflection': '@abraham/reflection':
@ -249,6 +251,9 @@ importers:
recharts: recharts:
specifier: ^2.15.3 specifier: ^2.15.3
version: 2.15.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0) version: 2.15.3(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
recorder:
specifier: workspace:*
version: link:../recorder
rxjs: rxjs:
specifier: ^7.8.2 specifier: ^7.8.2
version: 7.8.2 version: 7.8.2

View File

@ -4,9 +4,13 @@
{ {
"path": "./apps/email-playground" "path": "./apps/email-playground"
}, },
{
"path": "./apps/recorder"
},
{ {
"path": "./apps/webui" "path": "./apps/webui"
}, },
{ {
"path": "./packages/email" "path": "./packages/email"
}, },