agent_orchestrator/resource/
env_store.rs1use crate::cli_types::{EnvStoreSpec, OrchestratorResource, ResourceKind, ResourceSpec};
2use crate::config::{EnvStoreConfig, OrchestratorConfig};
3use anyhow::{Result, anyhow};
4
5use super::{ApplyResult, RegisteredResource, Resource, ResourceMetadata};
6
7#[derive(Debug, Clone)]
8pub struct EnvStoreResource {
10 pub metadata: ResourceMetadata,
12 pub spec: EnvStoreSpec,
14}
15
16impl Resource for EnvStoreResource {
17 fn kind(&self) -> ResourceKind {
18 ResourceKind::EnvStore
19 }
20
21 fn name(&self) -> &str {
22 &self.metadata.name
23 }
24
25 fn validate(&self) -> Result<()> {
26 super::validate_resource_name(self.name())?;
27 Ok(())
28 }
29
30 fn apply(&self, config: &mut OrchestratorConfig) -> Result<ApplyResult> {
31 let incoming = EnvStoreConfig {
32 data: self.spec.data.clone(),
33 };
34 let project = config.ensure_project(self.metadata.project.as_deref());
35 Ok(super::helpers::apply_to_map(
36 &mut project.env_stores,
37 self.name(),
38 incoming,
39 ))
40 }
41
42 fn to_yaml(&self) -> Result<String> {
43 super::manifest_yaml(
44 ResourceKind::EnvStore,
45 &self.metadata,
46 ResourceSpec::EnvStore(self.spec.clone()),
47 )
48 }
49
50 fn get_from_project(
51 config: &OrchestratorConfig,
52 name: &str,
53 project_id: Option<&str>,
54 ) -> Option<Self> {
55 config
56 .project(project_id)?
57 .env_stores
58 .get(name)
59 .map(|store| Self {
60 metadata: super::metadata_with_name(name),
61 spec: EnvStoreSpec {
62 data: store.data.clone(),
63 },
64 })
65 }
66
67 fn delete_from_project(
68 config: &mut OrchestratorConfig,
69 name: &str,
70 project_id: Option<&str>,
71 ) -> bool {
72 config
73 .project_mut(project_id)
74 .map(|project| project.env_stores.remove(name).is_some())
75 .unwrap_or(false)
76 }
77}
78
79pub(super) fn build_env_store(resource: OrchestratorResource) -> Result<RegisteredResource> {
81 let OrchestratorResource {
82 kind,
83 metadata,
84 spec,
85 ..
86 } = resource;
87 if kind != ResourceKind::EnvStore {
88 return Err(anyhow!("resource kind/spec mismatch for EnvStore"));
89 }
90 match spec {
91 ResourceSpec::EnvStore(spec) => Ok(RegisteredResource::EnvStore(EnvStoreResource {
92 metadata,
93 spec,
94 })),
95 _ => Err(anyhow!("resource kind/spec mismatch for EnvStore")),
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102 use crate::resource::test_fixtures::make_config;
103
104 fn make_env_store(name: &str) -> EnvStoreResource {
105 EnvStoreResource {
106 metadata: super::super::metadata_with_name(name),
107 spec: EnvStoreSpec {
108 data: [("KEY".to_string(), "value".to_string())].into(),
109 },
110 }
111 }
112
113 #[test]
114 fn env_store_apply_and_get() {
115 let mut config = make_config();
116 let store = make_env_store("my-env");
117 assert_eq!(
118 store.apply(&mut config).expect("apply"),
119 ApplyResult::Created
120 );
121
122 let loaded =
123 EnvStoreResource::get_from(&config, "my-env").expect("env store should be present");
124 assert_eq!(loaded.spec.data.get("KEY").unwrap(), "value");
125 assert_eq!(loaded.kind(), ResourceKind::EnvStore);
126 }
127
128 #[test]
129 fn env_store_apply_unchanged() {
130 let mut config = make_config();
131 let store = make_env_store("es-unchanged");
132 assert_eq!(
133 store.apply(&mut config).expect("apply"),
134 ApplyResult::Created
135 );
136 assert_eq!(
137 store.apply(&mut config).expect("apply"),
138 ApplyResult::Unchanged
139 );
140 }
141
142 #[test]
143 fn env_store_delete() {
144 let mut config = make_config();
145 let store = make_env_store("es-del");
146 store.apply(&mut config).expect("apply");
147 assert!(EnvStoreResource::delete_from(&mut config, "es-del"));
148 assert!(EnvStoreResource::get_from(&config, "es-del").is_none());
149 }
150
151 #[test]
152 fn env_store_validate_rejects_empty_name() {
153 let store = make_env_store("");
154 assert!(store.validate().is_err());
155 }
156
157 #[test]
158 fn env_store_to_yaml() {
159 let store = make_env_store("yaml-es");
160 let yaml = store.to_yaml().expect("should serialize");
161 assert!(yaml.contains("EnvStore"));
162 assert!(yaml.contains("yaml-es"));
163 }
164
165 #[test]
166 fn env_store_get_from_returns_none_for_missing() {
167 let config = make_config();
168 assert!(EnvStoreResource::get_from(&config, "no-such").is_none());
169 }
170}