cf_system_sdk_directory/
api.rs1use anyhow::Result;
6use async_trait::async_trait;
7
8#[derive(Clone, Debug, PartialEq, Eq, Hash)]
10pub struct ServiceEndpoint {
11 pub uri: String,
12}
13
14impl ServiceEndpoint {
15 pub fn new(uri: impl Into<String>) -> Self {
16 Self { uri: uri.into() }
17 }
18
19 #[must_use]
20 pub fn http(host: &str, port: u16) -> Self {
21 Self {
22 uri: format!("{}://{}:{}", "http", host, port),
23 }
24 }
25
26 #[must_use]
27 pub fn https(host: &str, port: u16) -> Self {
28 Self {
29 uri: format!("https://{host}:{port}"),
30 }
31 }
32
33 pub fn uds(path: impl AsRef<std::path::Path>) -> Self {
34 Self {
35 uri: format!("unix://{}", path.as_ref().display()),
36 }
37 }
38}
39
40#[derive(Debug, Clone)]
42pub struct ServiceInstanceInfo {
43 pub gear: String,
45 pub instance_id: String,
47 pub endpoint: ServiceEndpoint,
49 pub version: Option<String>,
51 pub rest_endpoint: Option<ServiceEndpoint>,
54}
55
56#[derive(Debug, Clone)]
58pub struct RegisterInstanceInfo {
59 pub gear: String,
61 pub instance_id: String,
63 pub grpc_services: Vec<(String, ServiceEndpoint)>,
65 pub version: Option<String>,
67 pub rest_endpoint: Option<ServiceEndpoint>,
69 pub openapi_spec: Option<String>,
71}
72
73#[async_trait]
80pub trait DirectoryClient: Send + Sync {
81 async fn resolve_grpc_service(&self, service_name: &str) -> Result<ServiceEndpoint>;
83
84 async fn resolve_rest_service(&self, gear_name: &str) -> Result<ServiceEndpoint>;
89
90 async fn get_openapi_spec(&self, gear_name: &str) -> Result<String>;
92
93 async fn list_instances(&self, gear: &str) -> Result<Vec<ServiceInstanceInfo>>;
95
96 async fn register_instance(&self, info: RegisterInstanceInfo) -> Result<()>;
98
99 async fn deregister_instance(&self, gear: &str, instance_id: &str) -> Result<()>;
101
102 async fn send_heartbeat(&self, gear: &str, instance_id: &str) -> Result<()>;
104}
105
106#[cfg(test)]
107#[cfg_attr(coverage_nightly, coverage(off))]
108mod tests {
109 use super::*;
110
111 #[test]
112 fn test_service_endpoint_creation() {
113 let http_ep = ServiceEndpoint::http("localhost", 8080);
114 assert_eq!(http_ep.uri, concat!("http", "://localhost:8080"));
115
116 let https_endpoint = ServiceEndpoint::https("localhost", 8443);
117 assert_eq!(https_endpoint.uri, "https://localhost:8443");
118
119 let uds_ep = ServiceEndpoint::uds("/tmp/socket.sock");
120 assert!(uds_ep.uri.starts_with("unix://"));
121 assert!(uds_ep.uri.contains("socket.sock"));
122
123 let custom_ep = ServiceEndpoint::new(concat!("http", "://example.com"));
124 assert_eq!(custom_ep.uri, concat!("http", "://example.com"));
125 }
126
127 #[test]
128 fn test_register_instance_info() {
129 let info = RegisterInstanceInfo {
130 gear: "test_gear".to_owned(),
131 instance_id: "instance1".to_owned(),
132 grpc_services: vec![(
133 "test.Service".to_owned(),
134 ServiceEndpoint::http("127.0.0.1", 8001),
135 )],
136 version: Some("1.0.0".to_owned()),
137 rest_endpoint: None,
138 openapi_spec: None,
139 };
140
141 assert_eq!(info.gear, "test_gear");
142 assert_eq!(info.instance_id, "instance1");
143 assert_eq!(info.grpc_services.len(), 1);
144 assert!(info.rest_endpoint.is_none());
145 assert!(info.openapi_spec.is_none());
146 }
147
148 #[test]
149 fn test_register_instance_info_with_rest() {
150 let info = RegisterInstanceInfo {
151 gear: "billing".to_owned(),
152 instance_id: "instance1".to_owned(),
153 grpc_services: vec![],
154 version: Some("2.0.0".to_owned()),
155 rest_endpoint: Some(ServiceEndpoint::http("billing", 8080)),
156 openapi_spec: Some("{\"openapi\":\"3.1.0\"}".to_owned()),
157 };
158
159 assert_eq!(info.gear, "billing");
160 assert_eq!(
161 info.rest_endpoint.as_ref().unwrap().uri,
162 concat!("http", "://billing:8080")
163 );
164 assert!(info.openapi_spec.is_some());
165 }
166}