@@ -158,6 +158,113 @@ struct ListResponse {
158158 connections : Vec < Connection > ,
159159}
160160
161+ pub fn create (
162+ workspace_id : & str ,
163+ name : & str ,
164+ source_type : & str ,
165+ config : & str ,
166+ secret_id : Option < & str > ,
167+ secret_name : Option < & str > ,
168+ format : & str ,
169+ ) {
170+ let profile_config = match crate :: config:: load ( "default" ) {
171+ Ok ( c) => c,
172+ Err ( e) => {
173+ eprintln ! ( "{e}" ) ;
174+ std:: process:: exit ( 1 ) ;
175+ }
176+ } ;
177+
178+ let api_key = match & profile_config. api_key {
179+ Some ( key) if key != "PLACEHOLDER" => key. clone ( ) ,
180+ _ => {
181+ eprintln ! ( "error: not authenticated. Run 'hotdata auth login' to log in." ) ;
182+ std:: process:: exit ( 1 ) ;
183+ }
184+ } ;
185+
186+ let config_value: serde_json:: Value = match serde_json:: from_str ( config) {
187+ Ok ( v) => v,
188+ Err ( e) => {
189+ eprintln ! ( "error: --config must be a valid JSON object: {e}" ) ;
190+ std:: process:: exit ( 1 ) ;
191+ }
192+ } ;
193+
194+ let mut body = serde_json:: json!( {
195+ "name" : name,
196+ "source_type" : source_type,
197+ "config" : config_value,
198+ } ) ;
199+ if let Some ( id) = secret_id {
200+ body[ "secret_id" ] = serde_json:: json!( id) ;
201+ }
202+ if let Some ( sn) = secret_name {
203+ body[ "secret_name" ] = serde_json:: json!( sn) ;
204+ }
205+
206+ let url = format ! ( "{}/connections" , profile_config. api_url) ;
207+ let client = reqwest:: blocking:: Client :: new ( ) ;
208+
209+ let resp = match client
210+ . post ( & url)
211+ . header ( "Authorization" , format ! ( "Bearer {api_key}" ) )
212+ . header ( "X-Workspace-Id" , workspace_id)
213+ . json ( & body)
214+ . send ( )
215+ {
216+ Ok ( r) => r,
217+ Err ( e) => {
218+ eprintln ! ( "error connecting to API: {e}" ) ;
219+ std:: process:: exit ( 1 ) ;
220+ }
221+ } ;
222+
223+ if !resp. status ( ) . is_success ( ) {
224+ use crossterm:: style:: Stylize ;
225+ eprintln ! ( "{}" , crate :: util:: api_error( resp. text( ) . unwrap_or_default( ) ) . red( ) ) ;
226+ std:: process:: exit ( 1 ) ;
227+ }
228+
229+ #[ derive( Deserialize , Serialize ) ]
230+ struct CreateResponse {
231+ id : String ,
232+ name : String ,
233+ source_type : String ,
234+ tables_discovered : u64 ,
235+ discovery_status : String ,
236+ discovery_error : Option < String > ,
237+ }
238+
239+ let result: CreateResponse = match resp. json ( ) {
240+ Ok ( v) => v,
241+ Err ( e) => {
242+ eprintln ! ( "error parsing response: {e}" ) ;
243+ std:: process:: exit ( 1 ) ;
244+ }
245+ } ;
246+
247+ match format {
248+ "json" => println ! ( "{}" , serde_json:: to_string_pretty( & result) . unwrap( ) ) ,
249+ "yaml" => print ! ( "{}" , serde_yaml:: to_string( & result) . unwrap( ) ) ,
250+ "table" => {
251+ use crossterm:: style:: Stylize ;
252+ println ! ( "{}" , "Connection created" . green( ) ) ;
253+ println ! ( "id: {}" , result. id) ;
254+ println ! ( "name: {}" , result. name) ;
255+ println ! ( "source_type: {}" , result. source_type) ;
256+ println ! ( "tables_discovered: {}" , result. tables_discovered) ;
257+ let status_colored = match result. discovery_status . as_str ( ) {
258+ "success" => result. discovery_status . green ( ) . to_string ( ) ,
259+ "failed" => result. discovery_error . as_deref ( ) . unwrap_or ( "failed" ) . red ( ) . to_string ( ) ,
260+ _ => result. discovery_status . yellow ( ) . to_string ( ) ,
261+ } ;
262+ println ! ( "discovery_status: {status_colored}" ) ;
263+ }
264+ _ => unreachable ! ( ) ,
265+ }
266+ }
267+
161268pub fn list ( workspace_id : & str , format : & str ) {
162269 let profile_config = match config:: load ( "default" ) {
163270 Ok ( c) => c,
0 commit comments