backbone-integrations 0.8.0

Integration registry: connectors, integration accounts and an idempotent inbound event lane, with one OAuth flow (HMAC-bound state, PKCE)
Documentation
//! Integrations Module
//!
//! Generated by metaphor-schema. Enhanced with runtime implementations.
//!
//! This module provides:
//! - Domain entities and repositories
//! - Application services
//! - HTTP and gRPC handlers
//! - Route configuration
//! - State machine enforcement
//! - Validation rules runtime
//! - RBAC middleware
//! - Trigger execution system
//! - Computed fields
//! - Workflow orchestrator

#![recursion_limit = "1024"]
#![allow(unused_imports)]

// Generated modules
pub mod domain;
pub mod infrastructure;
pub mod application;
pub mod presentation;
pub mod seeders;
pub mod exports;

// The module's extension (hand-written; ADR-0031).
#[path = "lib.ext.rs"]
mod lib_ext;
pub use lib_ext::*;
/// This crate's module and builder under fixed names, for the extension's `impl` blocks.
#[allow(dead_code)]
pub(crate) type ThisModule = IntegrationsModule;
#[allow(dead_code)]
pub(crate) type ThisModuleBuilder = IntegrationsModuleBuilder;

// Re-exports - Infrastructure
pub use infrastructure::persistence::*;

// Re-exports - Application services
pub use application::service::IntegrationConnectorService;
pub use application::service::IntegrationAccountService;
pub use application::service::IntegrationEventService;

// Re-exports - Workflows
pub use application::workflows::*;

use std::sync::Arc;
use axum::Router;
use sqlx::PgPool;

/// Integrations module configuration
///
/// Use the builder pattern to configure and register this module:
///
/// ```text
/// let integrations = IntegrationsModule::builder()
///     .with_database(pool.clone())
///     .build()?;
///
/// // Unguarded full CRUD (trusted/admin); compose a guarded router for production.
/// let router = integrations.all_crud_routes();
/// ```
pub struct IntegrationsModule {
    pub(crate) integration_connector_service: Arc<IntegrationConnectorService>,
    #[allow(dead_code)]
    pub(crate) integration_account_service: Arc<IntegrationAccountService>,
    pub(crate) integration_event_service: Arc<IntegrationEventService>,
    /// The module's extension state (`lib.ext.rs`); its fields read through `Deref`.
    pub(crate) ext: ModuleExt,
}

impl std::ops::Deref for IntegrationsModule {
    type Target = ModuleExt;
    fn deref(&self) -> &Self::Target {
        &self.ext
    }
}

impl std::ops::DerefMut for IntegrationsModule {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.ext
    }
}

/// What the extension's `build` hook receives from the generated build (ADR-0031).
#[allow(dead_code)]
pub(crate) struct ModuleParts<'a> {
    pub(crate) db_pool: &'a PgPool,
    pub(crate) integration_connector_service: &'a Arc<IntegrationConnectorService>,
    pub(crate) integration_connector_repository: &'a Arc<IntegrationConnectorRepository>,
    pub(crate) integration_account_service: &'a Arc<IntegrationAccountService>,
    pub(crate) integration_account_repository: &'a Arc<IntegrationAccountRepository>,
    pub(crate) integration_event_service: &'a Arc<IntegrationEventService>,
    pub(crate) integration_event_repository: &'a Arc<IntegrationEventRepository>,
}

impl IntegrationsModule {
    /// Create a new module builder
    pub fn builder() -> IntegrationsModuleBuilder {
        IntegrationsModuleBuilder::new()
    }

    /// Mount ALL generated CRUD endpoints (12 per entity) with NO domain
    /// validation — the fully **unguarded** surface. A well-formed request can
    /// create invalid rows or soft-delete a referenced master out from under its
    /// dependents. Prefer a guarded composition (read + validated writes) for any
    /// real deployment; use this only in trusted/admin/seeding contexts.
    pub fn all_crud_routes(&self) -> Router {
        use presentation::http::{
            create_integration_connector_routes,
            create_integration_event_routes,
        };

        Router::new()
            .merge(create_integration_connector_routes(self.integration_connector_service.clone()))
            .merge(create_integration_event_routes(self.integration_event_service.clone()))
    }

    /// Deprecated alias for [`Self::all_crud_routes`]. `routes()` reads like
    /// "the routes" but mounts UNVALIDATED generic CRUD on every entity — a naive
    /// mount exposes unguarded writes. Compose a guarded router (read + validated
    /// writes) for production, or call `all_crud_routes()` to opt into the full
    /// unguarded surface explicitly.
    #[deprecated(note = "mounts unvalidated generic CRUD; prefer readonly_routes() + validated writes, or all_crud_routes() for the full/unguarded surface")]
    pub fn routes(&self) -> Router {
        self.all_crud_routes()
    }

    /// Read-only routes for every entity (GET endpoints only) — the safe base.
    ///
    /// Generic mutation can't reach here, so this surface cannot bypass a
    /// validated write service's invariants. Use this as the production base and
    /// merge validated write routes (or a write service's HTTP layer) onto it.
    pub fn readonly_routes(&self) -> Router {
        use presentation::http::{
            create_integration_connector_read_routes,
            create_integration_event_read_routes,
        };

        Router::new()
            .merge(create_integration_connector_read_routes(self.integration_connector_service.clone()))
            .merge(create_integration_event_read_routes(self.integration_event_service.clone()))
    }

}

/// Builder for IntegrationsModule
pub struct IntegrationsModuleBuilder {
    db_pool: Option<PgPool>,
    /// The builder's extension state (`lib.ext.rs`); its fields read through `Deref`.
    ext: ModuleBuilderExt,
}

impl std::ops::Deref for IntegrationsModuleBuilder {
    type Target = ModuleBuilderExt;
    fn deref(&self) -> &Self::Target {
        &self.ext
    }
}

impl std::ops::DerefMut for IntegrationsModuleBuilder {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.ext
    }
}

impl IntegrationsModuleBuilder {
    /// Create a new builder
    pub fn new() -> Self {
        Self {
            db_pool: None,
            ext: Default::default(),
        }
    }

    /// Set the database connection pool
    pub fn with_database(mut self, pool: PgPool) -> Self {
        self.db_pool = Some(pool);
        self
    }


    /// Build the module with configured dependencies
    pub fn build(self) -> anyhow::Result<IntegrationsModule> {
        let db_pool = self.db_pool
            .ok_or_else(|| anyhow::anyhow!("Database pool not configured"))?;

        // IntegrationConnector service
        let integration_connector_repository = Arc::new(IntegrationConnectorRepository::new(db_pool.clone()));
        let integration_connector_service = Arc::new(IntegrationConnectorService::with_repository(integration_connector_repository.clone()).with_guard(Arc::new(crate::application::service::IntegrationConnectorRowRules)));

        // IntegrationAccount service
        let integration_account_repository = Arc::new(IntegrationAccountRepository::new(db_pool.clone()));
        let integration_account_service = Arc::new(IntegrationAccountService::with_repository(integration_account_repository.clone()).with_guard(Arc::new(crate::application::service::IntegrationAccountRowRules)));

        // IntegrationEvent service
        let integration_event_repository = Arc::new(IntegrationEventRepository::new(db_pool.clone()));
        let integration_event_service = Arc::new(IntegrationEventService::with_repository(integration_event_repository.clone()).with_guard(Arc::new(crate::application::service::IntegrationEventRowRules)));


        // The extension builds its own state from what the generated build made.
        let ext = self.ext.build(&ModuleParts {
            db_pool: &db_pool,
            integration_connector_service: &integration_connector_service,
            integration_connector_repository: &integration_connector_repository,
            integration_account_service: &integration_account_service,
            integration_account_repository: &integration_account_repository,
            integration_event_service: &integration_event_service,
            integration_event_repository: &integration_event_repository,
        })?;

        Ok(IntegrationsModule {
            integration_connector_service,
            integration_account_service,
            integration_event_service,
            ext,
        })
    }
}

impl Default for IntegrationsModuleBuilder {
    fn default() -> Self {
        Self::new()
    }
}

// This directory's extension (hand-written; ADR-0031).
include!("mod.ext.rs");