Skip to main content

agent_orchestrator/resource/
registry.rs

1use crate::cli_types::{OrchestratorResource, ResourceKind};
2use crate::config::OrchestratorConfig;
3use anyhow::{Result, anyhow};
4
5use super::{
6    AgentResource, EnvStoreResource, ExecutionProfileResource, ProjectResource,
7    RuntimePolicyResource, SecretStoreResource, StepTemplateResource, TriggerResource,
8    WorkflowResource, WorkspaceResource,
9};
10use super::{
11    ApplyResult, Resource, agent, env_store, execution_profile, project, runtime_policy,
12    secret_store, step_template, trigger, workflow, workspace,
13};
14
15#[derive(Debug, Clone)]
16/// Tagged union over every builtin manifest resource type supported by the importer.
17pub enum RegisteredResource {
18    /// Workspace resource.
19    Workspace(WorkspaceResource),
20    /// Agent resource.
21    Agent(Box<AgentResource>),
22    /// Workflow resource.
23    Workflow(WorkflowResource),
24    /// Project resource.
25    Project(ProjectResource),
26    /// Runtime policy resource.
27    RuntimePolicy(RuntimePolicyResource),
28    /// Step template resource.
29    StepTemplate(StepTemplateResource),
30    /// Execution profile resource.
31    ExecutionProfile(ExecutionProfileResource),
32    /// Environment store resource.
33    EnvStore(EnvStoreResource),
34    /// Secret store resource.
35    SecretStore(SecretStoreResource),
36    /// Trigger resource.
37    Trigger(TriggerResource),
38}
39
40#[derive(Debug, Clone, Copy)]
41/// Registry entry mapping a manifest kind to its constructor.
42pub struct ResourceRegistration {
43    /// Resource kind dispatched by this entry.
44    pub kind: ResourceKind,
45    /// Builder that validates and converts a generic manifest into a typed resource.
46    pub build: fn(OrchestratorResource) -> Result<RegisteredResource>,
47}
48
49/// Returns the static registry for builtin manifest resource kinds.
50pub fn resource_registry() -> [ResourceRegistration; 10] {
51    [
52        ResourceRegistration {
53            kind: ResourceKind::Workspace,
54            build: workspace::build_workspace,
55        },
56        ResourceRegistration {
57            kind: ResourceKind::Agent,
58            build: agent::build_agent,
59        },
60        ResourceRegistration {
61            kind: ResourceKind::Workflow,
62            build: workflow::build_workflow,
63        },
64        ResourceRegistration {
65            kind: ResourceKind::Project,
66            build: project::build_project,
67        },
68        ResourceRegistration {
69            kind: ResourceKind::RuntimePolicy,
70            build: runtime_policy::build_runtime_policy,
71        },
72        ResourceRegistration {
73            kind: ResourceKind::StepTemplate,
74            build: step_template::build_step_template,
75        },
76        ResourceRegistration {
77            kind: ResourceKind::ExecutionProfile,
78            build: execution_profile::build_execution_profile,
79        },
80        ResourceRegistration {
81            kind: ResourceKind::EnvStore,
82            build: env_store::build_env_store,
83        },
84        ResourceRegistration {
85            kind: ResourceKind::SecretStore,
86            build: secret_store::build_secret_store,
87        },
88        ResourceRegistration {
89            kind: ResourceKind::Trigger,
90            build: trigger::build_trigger,
91        },
92    ]
93}
94
95impl RegisteredResource {
96    /// Return the metadata.project field if present on this resource
97    pub fn metadata_project(&self) -> Option<&str> {
98        let meta = match self {
99            Self::Workspace(r) => &r.metadata,
100            Self::Agent(r) => &r.metadata,
101            Self::Workflow(r) => &r.metadata,
102            Self::Project(r) => &r.metadata,
103            Self::RuntimePolicy(r) => &r.metadata,
104            Self::StepTemplate(r) => &r.metadata,
105            Self::ExecutionProfile(r) => &r.metadata,
106            Self::EnvStore(r) => &r.metadata,
107            Self::SecretStore(r) => &r.metadata,
108            Self::Trigger(r) => &r.metadata,
109        };
110        meta.project.as_deref()
111    }
112}
113
114/// Converts a generic manifest resource into the corresponding typed builtin resource.
115pub fn dispatch_resource(resource: OrchestratorResource) -> Result<RegisteredResource> {
116    let kind = resource.kind;
117    if let Some(registration) = resource_registry().iter().find(|entry| entry.kind == kind) {
118        return (registration.build)(resource);
119    }
120    Err(anyhow!("unsupported resource kind"))
121}
122
123// ── RegisteredResource impl ───────────────────────────────────────────────────
124
125impl Resource for RegisteredResource {
126    fn kind(&self) -> ResourceKind {
127        match self {
128            Self::Workspace(_) => ResourceKind::Workspace,
129            Self::Agent(_) => ResourceKind::Agent,
130            Self::Workflow(_) => ResourceKind::Workflow,
131            Self::Project(_) => ResourceKind::Project,
132            Self::RuntimePolicy(_) => ResourceKind::RuntimePolicy,
133            Self::StepTemplate(_) => ResourceKind::StepTemplate,
134            Self::ExecutionProfile(_) => ResourceKind::ExecutionProfile,
135            Self::EnvStore(_) => ResourceKind::EnvStore,
136            Self::SecretStore(_) => ResourceKind::SecretStore,
137            Self::Trigger(_) => ResourceKind::Trigger,
138        }
139    }
140
141    fn name(&self) -> &str {
142        match self {
143            Self::Workspace(resource) => &resource.metadata.name,
144            Self::Agent(resource) => &resource.metadata.name,
145            Self::Workflow(resource) => &resource.metadata.name,
146            Self::Project(resource) => &resource.metadata.name,
147            Self::RuntimePolicy(resource) => &resource.metadata.name,
148            Self::StepTemplate(resource) => &resource.metadata.name,
149            Self::ExecutionProfile(resource) => &resource.metadata.name,
150            Self::EnvStore(resource) => &resource.metadata.name,
151            Self::SecretStore(resource) => &resource.metadata.name,
152            Self::Trigger(resource) => &resource.metadata.name,
153        }
154    }
155
156    fn validate(&self) -> Result<()> {
157        match self {
158            Self::Workspace(resource) => resource.validate(),
159            Self::Agent(resource) => resource.validate(),
160            Self::Workflow(resource) => resource.validate(),
161            Self::Project(resource) => resource.validate(),
162            Self::RuntimePolicy(resource) => resource.validate(),
163            Self::StepTemplate(resource) => resource.validate(),
164            Self::ExecutionProfile(resource) => resource.validate(),
165            Self::EnvStore(resource) => resource.validate(),
166            Self::SecretStore(resource) => resource.validate(),
167            Self::Trigger(resource) => resource.validate(),
168        }
169    }
170
171    fn apply(&self, config: &mut OrchestratorConfig) -> Result<ApplyResult> {
172        match self {
173            Self::Workspace(resource) => resource.apply(config),
174            Self::Agent(resource) => resource.apply(config),
175            Self::Workflow(resource) => resource.apply(config),
176            Self::Project(resource) => resource.apply(config),
177            Self::RuntimePolicy(resource) => resource.apply(config),
178            Self::StepTemplate(resource) => resource.apply(config),
179            Self::ExecutionProfile(resource) => resource.apply(config),
180            Self::EnvStore(resource) => resource.apply(config),
181            Self::SecretStore(resource) => resource.apply(config),
182            Self::Trigger(resource) => resource.apply(config),
183        }
184    }
185
186    fn to_yaml(&self) -> Result<String> {
187        match self {
188            Self::Workspace(resource) => resource.to_yaml(),
189            Self::Agent(resource) => resource.to_yaml(),
190            Self::Workflow(resource) => resource.to_yaml(),
191            Self::Project(resource) => resource.to_yaml(),
192            Self::RuntimePolicy(resource) => resource.to_yaml(),
193            Self::StepTemplate(resource) => resource.to_yaml(),
194            Self::ExecutionProfile(resource) => resource.to_yaml(),
195            Self::EnvStore(resource) => resource.to_yaml(),
196            Self::SecretStore(resource) => resource.to_yaml(),
197            Self::Trigger(resource) => resource.to_yaml(),
198        }
199    }
200
201    fn get_from_project(
202        config: &OrchestratorConfig,
203        name: &str,
204        project_id: Option<&str>,
205    ) -> Option<Self> {
206        if let Some(workspace) = WorkspaceResource::get_from_project(config, name, project_id) {
207            return Some(Self::Workspace(workspace));
208        }
209        if let Some(agent) = AgentResource::get_from_project(config, name, project_id) {
210            return Some(Self::Agent(Box::new(agent)));
211        }
212        if let Some(workflow) = WorkflowResource::get_from_project(config, name, project_id) {
213            return Some(Self::Workflow(workflow));
214        }
215        if let Some(project) = ProjectResource::get_from_project(config, name, project_id) {
216            return Some(Self::Project(project));
217        }
218        if let Some(step_template) =
219            StepTemplateResource::get_from_project(config, name, project_id)
220        {
221            return Some(Self::StepTemplate(step_template));
222        }
223        if let Some(execution_profile) =
224            ExecutionProfileResource::get_from_project(config, name, project_id)
225        {
226            return Some(Self::ExecutionProfile(execution_profile));
227        }
228        if name == "runtime" {
229            if let Some(runtime_policy) =
230                RuntimePolicyResource::get_from_project(config, name, project_id)
231            {
232                return Some(Self::RuntimePolicy(runtime_policy));
233            }
234        }
235        if let Some(env_store) = EnvStoreResource::get_from_project(config, name, project_id) {
236            return Some(Self::EnvStore(env_store));
237        }
238        if let Some(secret_store) = SecretStoreResource::get_from_project(config, name, project_id)
239        {
240            return Some(Self::SecretStore(secret_store));
241        }
242        if let Some(trigger) = TriggerResource::get_from_project(config, name, project_id) {
243            return Some(Self::Trigger(trigger));
244        }
245        None
246    }
247
248    fn delete_from_project(
249        config: &mut OrchestratorConfig,
250        name: &str,
251        project_id: Option<&str>,
252    ) -> bool {
253        // Try each builtin kind in turn
254        if WorkspaceResource::delete_from_project(config, name, project_id) {
255            return true;
256        }
257        if AgentResource::delete_from_project(config, name, project_id) {
258            return true;
259        }
260        if WorkflowResource::delete_from_project(config, name, project_id) {
261            return true;
262        }
263        if ProjectResource::delete_from_project(config, name, project_id) {
264            return true;
265        }
266        if StepTemplateResource::delete_from_project(config, name, project_id) {
267            return true;
268        }
269        if ExecutionProfileResource::delete_from_project(config, name, project_id) {
270            return true;
271        }
272        if EnvStoreResource::delete_from_project(config, name, project_id) {
273            return true;
274        }
275        if SecretStoreResource::delete_from_project(config, name, project_id) {
276            return true;
277        }
278        if TriggerResource::delete_from_project(config, name, project_id) {
279            return true;
280        }
281        false
282    }
283}