greentic_deployer/environment/
messaging.rs1use greentic_deploy_spec::{Environment, MessagingEndpoint, MessagingEndpointId};
19use serde::{Deserialize, Serialize};
20
21#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
23pub struct MessagingEndpointIndexEntry {
24 pub endpoint_id: MessagingEndpointId,
25 pub provider_type: String,
26 pub provider_id: String,
27 pub display_name: String,
28}
29
30impl MessagingEndpointIndexEntry {
31 pub fn from(endpoint: &MessagingEndpoint) -> Self {
32 Self {
33 endpoint_id: endpoint.endpoint_id,
34 provider_type: endpoint.provider_type.clone(),
35 provider_id: endpoint.provider_id.clone(),
36 display_name: endpoint.display_name.clone(),
37 }
38 }
39}
40
41pub fn materialize_messaging_index(env: &Environment) -> Vec<MessagingEndpointIndexEntry> {
47 env.messaging_endpoints
48 .iter()
49 .map(MessagingEndpointIndexEntry::from)
50 .collect()
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56 use chrono::Utc;
57 use greentic_deploy_spec::{
58 EnvId, EnvironmentHostConfig, MessagingEndpoint, MessagingEndpointId, SchemaVersion,
59 };
60
61 fn endpoint(provider_type: &str, provider_id: &str) -> MessagingEndpoint {
62 MessagingEndpoint {
63 schema: SchemaVersion::new(SchemaVersion::MESSAGING_ENDPOINT_V1),
64 env_id: EnvId::try_from("local").unwrap(),
65 endpoint_id: MessagingEndpointId::new(),
66 provider_id: provider_id.into(),
67 provider_type: provider_type.into(),
68 display_name: format!("{provider_type}: {provider_id}"),
69 secret_refs: vec![],
70 webhook_secret_ref: None,
71 linked_bundles: vec![],
72 welcome_flow: None,
73 generation: 1,
74 created_at: Utc::now(),
75 updated_at: Utc::now(),
76 updated_by: "operator://test".into(),
77 }
78 }
79
80 fn env(endpoints: Vec<MessagingEndpoint>) -> Environment {
81 let env_id = EnvId::try_from("local").unwrap();
82 Environment {
83 schema: SchemaVersion::new(SchemaVersion::ENVIRONMENT_V1),
84 environment_id: env_id.clone(),
85 name: "local".into(),
86 host_config: EnvironmentHostConfig {
87 env_id,
88 region: None,
89 tenant_org_id: None,
90 listen_addr: None,
91 public_base_url: None,
92 gui_enabled: None,
93 },
94 packs: vec![],
95 credentials_ref: None,
96 bundles: vec![],
97 revisions: vec![],
98 traffic_splits: vec![],
99 messaging_endpoints: endpoints,
100 extensions: Vec::new(),
101 revocation: Default::default(),
102 retention: Default::default(),
103 health: Default::default(),
104 }
105 }
106
107 #[test]
108 fn empty_env_yields_empty_index() {
109 assert!(materialize_messaging_index(&env(vec![])).is_empty());
110 }
111
112 #[test]
113 fn index_preserves_endpoint_order() {
114 let legal = endpoint("teams", "legal-bot");
115 let accounting = endpoint("teams", "accounting-bot");
116 let index = materialize_messaging_index(&env(vec![legal.clone(), accounting.clone()]));
117 assert_eq!(index.len(), 2);
118 assert_eq!(index[0].endpoint_id, legal.endpoint_id);
119 assert_eq!(index[1].endpoint_id, accounting.endpoint_id);
120 }
121
122 #[test]
123 fn index_carries_human_label() {
124 let ep = endpoint("teams", "legal-bot");
125 let index = materialize_messaging_index(&env(vec![ep.clone()]));
126 assert_eq!(index[0].display_name, ep.display_name);
127 assert_eq!(index[0].provider_type, "teams");
128 assert_eq!(index[0].provider_id, "legal-bot");
129 }
130}