diff --git a/crates/stackless-integrations/src/providers/depot/api.rs b/crates/stackless-integrations/src/providers/depot/api.rs new file mode 100644 index 0000000..40d80ca --- /dev/null +++ b/crates/stackless-integrations/src/providers/depot/api.rs @@ -0,0 +1,131 @@ +//! `depot/api` integration. + +use std::collections::BTreeMap; + +use serde::Serialize; +use stackless_stripe_projects::catalog::verify::CatalogService; +use stackless_stripe_projects::provision::ProvisionContext; + +use super::FamilyResource; +use crate::error::IntegrationError; +use crate::hostable::{ConfigScope, Hostable, IntegrationHosting}; + +pub const RESOURCE_KIND: &str = "integration-depot"; + +#[derive(Debug, Serialize)] +pub struct DepotApiConfig {} + +impl CatalogService for DepotApiConfig { + const REFERENCE: &'static str = "depot/api"; +} + +#[derive(Debug)] +pub struct DepotApi; + +impl Hostable for DepotApi { + const PROVIDER: &'static str = "depot"; + const HOSTING: IntegrationHosting = IntegrationHosting::Managed; + const CONFIG_SCOPE: ConfigScope = ConfigScope::GlobalOnly; + const RESOURCE_KIND: &'static str = RESOURCE_KIND; + const OUTPUTS: &'static [&'static str] = &["api_key"]; +} + +impl FamilyResource for DepotApi { + type Config = DepotApiConfig; + const PROVIDER_PREFIX: &'static str = "DEPOT"; + // Provisional until pinned by `mise run discover depot/api`. + const OUTPUT_FIELDS: &'static [(&'static str, &'static str, bool)] = + &[("API_KEY", "api_key", true)]; + + fn build_config(ctx: &ProvisionContext<'_>) -> Result { + let _ = super::integration_config(ctx)?; + Ok(DepotApiConfig {}) + } +} + +pub fn validate_config( + name: &str, + config: &BTreeMap, +) -> Result<(), IntegrationError> { + let _ = (name, config); + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::ProviderOps; + use crate::resource::ResourcePayload; + use stackless_core::def::StackDef; + use stackless_stripe_projects::stripe::StripeProjects; + use stackless_stripe_projects::test_support; + + #[test] + fn config_matches_catalog() { + const FIXTURE: &str = include_str!(concat!( + env!("CARGO_MANIFEST_DIR"), + "/../stackless-stripe-projects/tests/fixtures/catalog.json" + )); + let catalog = stackless_stripe_projects::Catalog::from_json_envelope(FIXTURE).unwrap(); + let failures = stackless_stripe_projects::verify_service(&catalog, &DepotApiConfig {}); + assert!( + failures.is_empty(), + "depot/api catalog gaps:\n{}", + failures.join("\n") + ); + } + + const CATALOG_ENVELOPE: &str = r##"{"ok":true,"command":"projects catalog","data":{"last_updated":"2026-07-11T00:00:00Z","services":[{"id":"prvsvc_api","object":"v2.provisioning.provider_service_detail","provider_id":"prvdr_depot","provider_name":"Depot","service_id":"api","categories":["database"],"kind":"deployable","scope":"project","availability":"available","development":false,"livemode":true,"pricing":{"type":"component"},"configuration_schema":{"additionalProperties":false,"properties":{},"type":"object"}}]}}"##; + + fn test_def() -> StackDef { + StackDef::parse( + r#" +[stack] +name = "atto" +[stack.projects.stripe] +project = "project_1" +[integrations.res] +provider = "depot" +[services.api] +source = { repo = "r", ref = "main" } +env = { OUT = "${integrations.res.api_key}" } +health = { path = "/health" } +[services.api.local] +run = "true" +"#, + ) + .unwrap() + } + + #[tokio::test] + async fn provision_records_outputs() { + let runner = test_support::provision_script( + CATALOG_ENVELOPE, + serde_json::json!({"DEPOT_API_KEY": "val_api_key"}), + 0, + ); + let dir = tempfile::tempdir().unwrap(); + std::fs::write( + dir.path().join("stackless.toml"), + "[stack]\nname=\"atto\"\n", + ) + .unwrap(); + let stripe = StripeProjects::new(&runner, dir.path()); + + let resource = DepotApi + .provision( + &stripe.as_dyn(), + &test_def(), + dir.path(), + "demo", + "res", + "local", + false, + ) + .await + .unwrap(); + assert_eq!(resource.resource_kind, "integration-depot"); + let payload: ResourcePayload = serde_json::from_str(&resource.payload).unwrap(); + assert_eq!(payload.outputs["api_key"], "val_api_key"); + } +} diff --git a/crates/stackless-integrations/src/providers/depot/mod.rs b/crates/stackless-integrations/src/providers/depot/mod.rs new file mode 100644 index 0000000..7bc30e3 --- /dev/null +++ b/crates/stackless-integrations/src/providers/depot/mod.rs @@ -0,0 +1,11 @@ +//! Depot catalog resources via Stripe Projects. +//! +//! Output envelopes are provisional until pinned by `xtask discover`. + +pub mod api; + +#[allow(unused_imports)] +pub(crate) use crate::resource::{ + CatalogResource as FamilyResource, bool_optional, bool_required, int_optional, int_required, + integration_config, interp_optional, interp_required, +}; diff --git a/crates/stackless-integrations/src/providers/mod.rs b/crates/stackless-integrations/src/providers/mod.rs index 663fe44..2123817 100644 --- a/crates/stackless-integrations/src/providers/mod.rs +++ b/crates/stackless-integrations/src/providers/mod.rs @@ -14,6 +14,7 @@ pub mod cloudflare; pub mod composio; pub mod customerio; pub mod daytona; +pub mod depot; pub mod e2b; pub mod elevenlabs; pub mod exa; @@ -54,11 +55,11 @@ mod tests { use crate::providers::{ agentmail, agentphone, algolia, amplitude, auth0, base44_projects, blaxel, browserbase, - chatbase, chroma, clickhouse, cloudflare, composio, customerio, daytona, e2b, elevenlabs, - exa, firecrawl, flyio, gitlab, heygen, huggingface, inngest, kernel, laravel_cloud, - metronome, mixpanel, neon, openrouter, parallel, planetscale, postalform, posthog, prisma, - privy, railway, render_db, runloop, sentry, supabase, supermemory, turso, upstash, wix, - wordpress_com, workos, + chatbase, chroma, clickhouse, cloudflare, composio, customerio, daytona, depot, e2b, + elevenlabs, exa, firecrawl, flyio, gitlab, heygen, huggingface, inngest, kernel, + laravel_cloud, metronome, mixpanel, neon, openrouter, parallel, planetscale, postalform, + posthog, prisma, privy, railway, render_db, runloop, sentry, supabase, supermemory, turso, + upstash, wix, wordpress_com, workos, }; fn assert_outputs_match() { @@ -98,6 +99,7 @@ mod tests { assert_outputs_match::(); assert_outputs_match::(); assert_outputs_match::(); + assert_outputs_match::(); assert_outputs_match::(); assert_outputs_match::(); assert_outputs_match::(); diff --git a/crates/stackless-integrations/src/registry.rs b/crates/stackless-integrations/src/registry.rs index 018470f..9b4897d 100644 --- a/crates/stackless-integrations/src/registry.rs +++ b/crates/stackless-integrations/src/registry.rs @@ -92,6 +92,7 @@ register_providers! { (composio::project, ComposioProject), (customerio::workspace, CustomerioWorkspace), (daytona::sandbox, DaytonaSandbox), + (depot::api, DepotApi), (e2b::sandbox, E2BSandbox), (elevenlabs::tts, ElevenLabsTts), (exa::api, ExaApi),