1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
use std::error::Error;
use crate::background_tasks::{BackgroundTask, BackgroundTasksManager};
use crate::executor::plugin_trait::RouterPlugin;
use crate::BoxError;
pub struct OnPluginInitPayload<'a, TRouterPlugin: RouterPlugin> {
config: &'a serde_json::Value,
bg_tasks_manager: &'a mut BackgroundTasksManager,
phantom: std::marker::PhantomData<TRouterPlugin>,
}
pub type OnPluginInitResult<TRouterPlugin> = Result<Option<TRouterPlugin>, Box<dyn Error>>;
impl<'a, TRouterPlugin> OnPluginInitPayload<'a, TRouterPlugin>
where
TRouterPlugin: RouterPlugin,
{
pub fn new(
config: &'a serde_json::Value,
bg_tasks_manager: &'a mut BackgroundTasksManager,
) -> Self {
Self {
config,
bg_tasks_manager,
phantom: std::marker::PhantomData,
}
}
/// Parse the plugin config into the expected config struct for the plugin.
/// The plugin can choose when and if to call this method.
///
/// [Refer to the docs for more details](https://the-guild.dev/graphql/hive/docs/router/extensibility/plugin_system#configuration)
///
/// Example:
/// ```
/// fn on_plugin_init(mut payload: OnPluginInitPayload<Self>) -> OnPluginInitResult<Self> {
/// let config = payload.config()?;
/// // use config to initialize plugin...
/// }
/// ```
pub fn config(&self) -> Result<TRouterPlugin::Config, Box<dyn Error>> {
let sonic_value = sonic_rs::to_value(self.config)?;
let config = sonic_rs::from_value(&sonic_value)?;
Ok(config)
}
/// Register a background task to be run by the router.
/// The registered task struct should implement the `BackgroundTask` trait.
///
/// [Refer to the docs for more details](https://the-guild.dev/graphql/hive/docs/router/extensibility/plugin_system#background-tasks)
///
/// Example:
/// ```
/// struct MyBackgroundTask {
/// // fields for the task...
/// }
///
/// #[async_trait]
/// impl BackgroundTask for MyBackgroundTask {
/// fn id(&self) -> &str {
/// "my_background_task"
/// }
/// async fn run(&self, token: CancellationToken) {
/// loop {
/// if token.is_cancelled() {
/// break;
/// }
/// // do background work...
/// }
/// }
/// }
///
/// impl RouterPlugin for MyPlugin {
/// // ...
/// fn on_plugin_init(mut payload: OnPluginInitPayload<Self>) -> OnPluginInitResult<Self> {
/// payload.register_background_task(MyBackgroundTask {
/// // initialize task fields...
/// });
/// // initialize plugin...
/// }
/// }
/// ```
pub fn register_background_task<T>(&mut self, task: T)
where
T: BackgroundTask + 'static,
{
self.bg_tasks_manager.register_task(task)
}
/// Returning this will disable the plugin and it won't be initialized.
/// This can be used if the plugin determines during initialization that it shouldn't run
/// (e.g. due to missing configuration or environment variables).
///
/// Example:
/// ```
/// fn on_plugin_init(payload: OnPluginInitPayload<Self>) -> OnPluginInitResult {
/// if some_condition {
/// return payload.disable_plugin();
/// }
/// // continue with initialization...
/// }
/// ```
pub fn disable_plugin(&self) -> OnPluginInitResult<TRouterPlugin> {
Ok(None)
}
/// Returning this will initialize the plugin with the provided instance.
/// Example:
/// ```
/// fn on_plugin_init(mut payload: OnPluginInitPayload<Self>) -> OnPluginInitResult<Self> {
/// let config = payload.config()?;
/// let plugin_instance = Self {
/// // initialize plugin fields from config...
/// };
/// payload.initialize_plugin(plugin_instance)
/// }
/// ```
pub fn initialize_plugin(&self, plugin: TRouterPlugin) -> OnPluginInitResult<TRouterPlugin> {
Ok(Some(plugin))
}
/// If the plugin struct implements `Default`, this method can be used to initialize the plugin with default values.
/// Example:
/// ```
/// #[derive(Default)]
/// struct MyPlugin {
/// values: Vec<String>,
/// }
///
/// impl RouterPlugin for MyPlugin {
/// // ...
/// fn on_plugin_init(payload: OnPluginInitPayload<Self>) -> OnPluginInitResult<Self> {
/// payload.initialize_plugin_with_defaults()
/// }
/// }
/// ```
pub fn initialize_plugin_with_defaults(&self) -> OnPluginInitResult<TRouterPlugin>
where
TRouterPlugin: Default,
{
Ok(Some(TRouterPlugin::default()))
}
/// Returning an error from this method will cause the router to fail initialization and the error will be logged.
/// This can be used if the plugin encounters an unrecoverable error during initialization.
/// Example:
/// ```
/// fn on_plugin_init(payload: OnPluginInitPayload<Self>) -> OnPluginInitResult<Self> {
/// if let Err(e) = do_some_initialization() {
/// return payload.error(e);
/// }
/// // continue with initialization...
/// }
/// ```
pub fn error<TError>(err: TError) -> OnPluginInitResult<TRouterPlugin>
where
TError: Error + Into<BoxError>,
{
Err(err.into())
}
}