Skip to main content

static_tr_plugin/
module.rs

1//! Static tenant resolver plugin module.
2
3use std::sync::{Arc, OnceLock};
4
5use async_trait::async_trait;
6use modkit::Module;
7use modkit::client_hub::ClientScope;
8use modkit::context::ModuleCtx;
9use modkit::gts::BaseModkitPluginV1;
10use tenant_resolver_sdk::{TenantResolverPluginClient, TenantResolverPluginSpecV1};
11use tracing::info;
12use types_registry_sdk::{RegisterResult, TypesRegistryClient};
13
14use crate::config::StaticTrPluginConfig;
15use crate::domain::Service;
16
17/// Static tenant resolver plugin module.
18///
19/// Provides tenant data from configuration with hierarchical support.
20///
21/// **Plugin registration pattern:**
22/// - Gateway registers the plugin schema (GTS type definition)
23/// - This plugin registers its instance (implementation metadata)
24/// - This plugin registers its scoped client (implementation in `ClientHub`)
25#[modkit::module(
26    name = "static-tr-plugin",
27    deps = ["types-registry"]
28)]
29pub struct StaticTrPlugin {
30    service: OnceLock<Arc<Service>>,
31}
32
33impl Default for StaticTrPlugin {
34    fn default() -> Self {
35        Self {
36            service: OnceLock::new(),
37        }
38    }
39}
40
41#[async_trait]
42impl Module for StaticTrPlugin {
43    async fn init(&self, ctx: &ModuleCtx) -> anyhow::Result<()> {
44        info!("Initializing {} module", Self::MODULE_NAME);
45
46        // Load configuration
47        let cfg: StaticTrPluginConfig = ctx.config()?;
48        info!(
49            vendor = %cfg.vendor,
50            priority = cfg.priority,
51            tenant_count = cfg.tenants.len(),
52            "Loaded plugin configuration"
53        );
54
55        // Generate plugin instance ID
56        let instance_id = TenantResolverPluginSpecV1::gts_make_instance_id(
57            "hyperspot.builtin.static_tenant_resolver.plugin.v1",
58        );
59
60        // Register plugin instance in types-registry
61        let registry = ctx.client_hub().get::<dyn TypesRegistryClient>()?;
62        let instance = BaseModkitPluginV1::<TenantResolverPluginSpecV1> {
63            id: instance_id.clone(),
64            vendor: cfg.vendor.clone(),
65            priority: cfg.priority,
66            properties: TenantResolverPluginSpecV1,
67        };
68        let instance_json = serde_json::to_value(&instance)?;
69
70        let results = registry.register(vec![instance_json]).await?;
71        RegisterResult::ensure_all_ok(&results)?;
72
73        // Create service from config
74        let service = Arc::new(Service::from_config(&cfg));
75        self.service
76            .set(service.clone())
77            .map_err(|_| anyhow::anyhow!("{} module already initialized", Self::MODULE_NAME))?;
78
79        // Register scoped client in ClientHub
80        let api: Arc<dyn TenantResolverPluginClient> = service;
81        ctx.client_hub()
82            .register_scoped::<dyn TenantResolverPluginClient>(
83                ClientScope::gts_id(&instance_id),
84                api,
85            );
86
87        info!(instance_id = %instance_id, "{} module initialized successfully", Self::MODULE_NAME);
88        Ok(())
89    }
90}