11use crate :: api:: ApiClient ;
22use serde:: { Deserialize , Serialize } ;
33
4+ #[ derive( Deserialize , Serialize ) ]
5+ struct HealthResponse {
6+ #[ allow( dead_code) ]
7+ connection_id : String ,
8+ healthy : bool ,
9+ #[ serde( default , skip_serializing_if = "Option::is_none" ) ]
10+ latency_ms : Option < u64 > ,
11+ #[ serde( default , skip_serializing_if = "Option::is_none" ) ]
12+ error : Option < String > ,
13+ }
14+
15+ fn fetch_health ( api : & ApiClient , connection_id : & str , show_spinner : bool ) -> HealthResponse {
16+ let spinner = show_spinner. then ( || crate :: util:: spinner ( "Checking connection health..." ) ) ;
17+ let health: HealthResponse = api. get ( & format ! ( "/connections/{connection_id}/health" ) ) ;
18+ if let Some ( s) = spinner { s. finish_and_clear ( ) ; }
19+ health
20+ }
21+
22+ fn format_health ( health : & HealthResponse ) -> String {
23+ use crossterm:: style:: Stylize ;
24+ if health. healthy {
25+ match health. latency_ms {
26+ Some ( ms) => format ! ( "{} {}" , "healthy" . green( ) , format!( "({ms}ms)" ) . dark_grey( ) ) ,
27+ None => "healthy" . green ( ) . to_string ( ) ,
28+ }
29+ } else {
30+ let err = health. error . as_deref ( ) . unwrap_or ( "unknown error" ) ;
31+ format ! ( "{} — {}" , "unhealthy" . red( ) , err)
32+ }
33+ }
34+
435#[ derive( Deserialize , Serialize ) ]
536struct ConnectionType {
637 name : String ,
@@ -88,23 +119,60 @@ struct ListResponse {
88119
89120pub fn get ( workspace_id : & str , connection_id : & str , format : & str ) {
90121 let api = ApiClient :: new ( Some ( workspace_id) ) ;
122+ let is_table = format == "table" ;
123+
124+ let spinner = is_table. then ( || crate :: util:: spinner ( "Fetching connection..." ) ) ;
91125 let detail: ConnectionDetail = api. get ( & format ! ( "/connections/{connection_id}" ) ) ;
126+ if let Some ( s) = spinner { s. finish_and_clear ( ) ; }
127+
128+ let health = fetch_health ( & api, connection_id, is_table) ;
92129
93130 match format {
94- "json" => println ! ( "{}" , serde_json:: to_string_pretty( & detail) . unwrap( ) ) ,
95- "yaml" => print ! ( "{}" , serde_yaml:: to_string( & detail) . unwrap( ) ) ,
131+ "json" => {
132+ let combined = serde_json:: json!( {
133+ "id" : detail. id,
134+ "name" : detail. name,
135+ "source_type" : detail. source_type,
136+ "table_count" : detail. table_count,
137+ "synced_table_count" : detail. synced_table_count,
138+ "health" : & health,
139+ } ) ;
140+ println ! ( "{}" , serde_json:: to_string_pretty( & combined) . unwrap( ) ) ;
141+ }
142+ "yaml" => {
143+ let combined = serde_json:: json!( {
144+ "id" : detail. id,
145+ "name" : detail. name,
146+ "source_type" : detail. source_type,
147+ "table_count" : detail. table_count,
148+ "synced_table_count" : detail. synced_table_count,
149+ "health" : & health,
150+ } ) ;
151+ print ! ( "{}" , serde_yaml:: to_string( & combined) . unwrap( ) ) ;
152+ }
96153 "table" => {
97154 use crossterm:: style:: Stylize ;
98155 let label = |l : & str | format ! ( "{:<16}" , l) . dark_grey ( ) . to_string ( ) ;
99156 println ! ( "{}{}" , label( "id:" ) , detail. id. dark_cyan( ) ) ;
100157 println ! ( "{}{}" , label( "name:" ) , detail. name. white( ) ) ;
101158 println ! ( "{}{}" , label( "source_type:" ) , detail. source_type. green( ) ) ;
102159 println ! ( "{}{}" , label( "tables:" ) , format!( "{} synced / {} total" , detail. synced_table_count. to_string( ) . cyan( ) , detail. table_count. to_string( ) . cyan( ) ) ) ;
160+ println ! ( "{}{}" , label( "health:" ) , format_health( & health) ) ;
103161 }
104162 _ => unreachable ! ( ) ,
105163 }
106164}
107165
166+ #[ derive( Deserialize , Serialize ) ]
167+ struct CreateResponse {
168+ id : String ,
169+ name : String ,
170+ source_type : String ,
171+ tables_discovered : u64 ,
172+ discovery_status : String ,
173+ discovery_error : Option < String > ,
174+ }
175+
108176pub fn create (
109177 workspace_id : & str ,
110178 name : & str ,
@@ -127,22 +195,53 @@ pub fn create(
127195 } ) ;
128196
129197 let api = ApiClient :: new ( Some ( workspace_id) ) ;
198+ let is_table = format == "table" ;
130199
131- # [ derive ( Deserialize , Serialize ) ]
132- struct CreateResponse {
133- id : String ,
134- name : String ,
135- source_type : String ,
136- tables_discovered : u64 ,
137- discovery_status : String ,
138- discovery_error : Option < String > ,
200+ let spinner = is_table . then ( || crate :: util :: spinner ( "Creating connection..." ) ) ;
201+ let ( status , resp_body ) = api . post_raw ( "/connections" , & body ) ;
202+ if let Some ( s ) = & spinner { s . finish_and_clear ( ) ; }
203+
204+ if !status . is_success ( ) {
205+ use crossterm :: style :: Stylize ;
206+ eprintln ! ( "{}" , crate :: util :: api_error ( resp_body ) . red ( ) ) ;
207+ std :: process :: exit ( 1 ) ;
139208 }
140209
141- let result: CreateResponse = api. post ( "/connections" , & body) ;
210+ let result: CreateResponse = match serde_json:: from_str ( & resp_body) {
211+ Ok ( v) => v,
212+ Err ( e) => {
213+ eprintln ! ( "error parsing response: {e}" ) ;
214+ std:: process:: exit ( 1 ) ;
215+ }
216+ } ;
217+
218+ let health = fetch_health ( & api, & result. id , is_table) ;
142219
143220 match format {
144- "json" => println ! ( "{}" , serde_json:: to_string_pretty( & result) . unwrap( ) ) ,
145- "yaml" => print ! ( "{}" , serde_yaml:: to_string( & result) . unwrap( ) ) ,
221+ "json" => {
222+ let combined = serde_json:: json!( {
223+ "id" : result. id,
224+ "name" : result. name,
225+ "source_type" : result. source_type,
226+ "tables_discovered" : result. tables_discovered,
227+ "discovery_status" : result. discovery_status,
228+ "discovery_error" : result. discovery_error,
229+ "health" : & health,
230+ } ) ;
231+ println ! ( "{}" , serde_json:: to_string_pretty( & combined) . unwrap( ) ) ;
232+ }
233+ "yaml" => {
234+ let combined = serde_json:: json!( {
235+ "id" : result. id,
236+ "name" : result. name,
237+ "source_type" : result. source_type,
238+ "tables_discovered" : result. tables_discovered,
239+ "discovery_status" : result. discovery_status,
240+ "discovery_error" : result. discovery_error,
241+ "health" : & health,
242+ } ) ;
243+ print ! ( "{}" , serde_yaml:: to_string( & combined) . unwrap( ) ) ;
244+ }
146245 "table" => {
147246 use crossterm:: style:: Stylize ;
148247 println ! ( "{}" , "Connection created" . green( ) ) ;
@@ -156,9 +255,14 @@ pub fn create(
156255 _ => result. discovery_status . yellow ( ) . to_string ( ) ,
157256 } ;
158257 println ! ( "discovery_status: {status_colored}" ) ;
258+ println ! ( "health: {}" , format_health( & health) ) ;
159259 }
160260 _ => unreachable ! ( ) ,
161261 }
262+
263+ if !health. healthy {
264+ std:: process:: exit ( 1 ) ;
265+ }
162266}
163267
164268pub fn list ( workspace_id : & str , format : & str ) {
0 commit comments