@@ -140,6 +140,119 @@ func (c *Client) Get(path string, result interface{}) error {
140140 return c .parseResponse (resp , result )
141141}
142142
143+ // GetJob fetches GET /api/jobs/{id}. The API returns either { "data": { job } } or a flat job object.
144+ func (c * Client ) GetJob (jobID string ) (* JobStatus , error ) {
145+ resp , err := c .request ("GET" , fmt .Sprintf ("/api/jobs/%s" , jobID ), nil )
146+ if err != nil {
147+ return nil , err
148+ }
149+ defer resp .Body .Close ()
150+ data , err := io .ReadAll (resp .Body )
151+ if err != nil {
152+ return nil , err
153+ }
154+ if resp .StatusCode >= 400 {
155+ var apiErr APIError
156+ if json .Unmarshal (data , & apiErr ) == nil && apiErr .Message != "" {
157+ return nil , & apiErr
158+ }
159+ return nil , fmt .Errorf ("API error (HTTP %d): %s" , resp .StatusCode , string (data ))
160+ }
161+ var outer map [string ]json.RawMessage
162+ if err := json .Unmarshal (data , & outer ); err != nil {
163+ return nil , fmt .Errorf ("parsing job response: %w" , err )
164+ }
165+ if raw , ok := outer ["data" ]; ok {
166+ var job JobStatus
167+ if err := json .Unmarshal (raw , & job ); err != nil {
168+ return nil , fmt .Errorf ("parsing job data: %w" , err )
169+ }
170+ return & job , nil
171+ }
172+ var job JobStatus
173+ if err := json .Unmarshal (data , & job ); err != nil {
174+ return nil , fmt .Errorf ("parsing job response: %w" , err )
175+ }
176+ return & job , nil
177+ }
178+
179+ // ListDatabases returns GET /api/dbs. The server may answer synchronously (200 + data) or
180+ // asynchronously (202 + job_id); in the latter case the job is polled until completion.
181+ func (c * Client ) ListDatabases () ([]map [string ]interface {}, error ) {
182+ resp , err := c .request ("GET" , "/api/dbs" , nil )
183+ if err != nil {
184+ return nil , err
185+ }
186+ defer resp .Body .Close ()
187+ body , err := io .ReadAll (resp .Body )
188+ if err != nil {
189+ return nil , err
190+ }
191+ if resp .StatusCode >= 400 {
192+ var apiErr APIError
193+ if json .Unmarshal (body , & apiErr ) == nil && apiErr .Message != "" {
194+ return nil , & apiErr
195+ }
196+ return nil , fmt .Errorf ("API error (HTTP %d): %s" , resp .StatusCode , string (body ))
197+ }
198+
199+ switch resp .StatusCode {
200+ case http .StatusOK :
201+ var v struct {
202+ Data []map [string ]interface {} `json:"data"`
203+ }
204+ if err := json .Unmarshal (body , & v ); err != nil {
205+ return nil , fmt .Errorf ("parsing database list: %w" , err )
206+ }
207+ if v .Data == nil {
208+ return []map [string ]interface {}{}, nil
209+ }
210+ return v .Data , nil
211+ case http .StatusAccepted :
212+ var async AsyncResponse
213+ if err := json .Unmarshal (body , & async ); err != nil {
214+ return nil , fmt .Errorf ("parsing async response: %w" , err )
215+ }
216+ if async .JobID == nil || async .JobIDString () == "" {
217+ return nil , fmt .Errorf ("no job_id in database list response" )
218+ }
219+ job , err := c .WaitForJob (async .JobIDString ())
220+ if err != nil {
221+ return nil , err
222+ }
223+ return databasesFromJobResult (job .Result )
224+ default :
225+ return nil , fmt .Errorf ("unexpected HTTP %d listing databases: %s" , resp .StatusCode , string (body ))
226+ }
227+ }
228+
229+ func databasesFromJobResult (result interface {}) ([]map [string ]interface {}, error ) {
230+ if result == nil {
231+ return []map [string ]interface {}{}, nil
232+ }
233+ m , ok := result .(map [string ]interface {})
234+ if ! ok {
235+ return nil , fmt .Errorf ("unexpected job result format for database list" )
236+ }
237+ raw , ok := m ["databases" ]
238+ if ! ok || raw == nil {
239+ return []map [string ]interface {}{}, nil
240+ }
241+ arr , ok := raw .([]interface {})
242+ if ! ok {
243+ return nil , fmt .Errorf ("job result \" databases\" is not an array" )
244+ }
245+ out := make ([]map [string ]interface {}, 0 , len (arr ))
246+ for _ , item := range arr {
247+ row , ok := item .(map [string ]interface {})
248+ if ! ok {
249+ continue
250+ }
251+ out = append (out , row )
252+ }
253+ return out , nil
254+ }
255+
143256func (c * Client ) Post (path string , body interface {}, result interface {}) error {
144257 resp , err := c .request ("POST" , path , body )
145258 if err != nil {
@@ -191,23 +304,23 @@ func (c *Client) WaitForJob(jobID string) (*JobStatus, error) {
191304
192305 maxAttempts := 120
193306 for i := 0 ; i < maxAttempts ; i ++ {
194- var job JobStatus
195- if err := c . Get ( fmt . Sprintf ( "/api/jobs/%s" , jobID ), & job ); err != nil {
307+ job , err := c . GetJob ( jobID )
308+ if err != nil {
196309 time .Sleep (2 * time .Second )
197310 continue
198311 }
199312
200313 switch job .Status {
201314 case "completed" , "success" , "finished" :
202315 s .Stop ()
203- return & job , nil
316+ return job , nil
204317 case "failed" , "error" :
205318 s .Stop ()
206319 errMsg := job .Error
207320 if errMsg == "" {
208321 errMsg = "job failed"
209322 }
210- return & job , fmt .Errorf ("%s" , errMsg )
323+ return job , fmt .Errorf ("%s" , errMsg )
211324 }
212325
213326 interval := 2 * time .Second
0 commit comments