11package ovc
22
33import (
4+ "bytes"
5+ "encoding/json"
46 "errors"
57 "fmt"
68 "io/ioutil"
79 "net/http"
810 "net/url"
911 "strings"
12+ "time"
1013
1114 "github.com/sirupsen/logrus"
1215)
@@ -116,8 +119,40 @@ func NewClient(c *Config) (*Client, error) {
116119 return client , nil
117120}
118121
122+ // async adds "async=true" flag to all API calls
123+ func async (req * http.Request ) (* http.Request , error ) {
124+ // fetch request body to the string
125+ jsonMap := make (map [string ]interface {})
126+
127+ if req .Body != nil {
128+ reqBody , err := ioutil .ReadAll (req .Body )
129+ if err != nil {
130+ return nil , err
131+ }
132+ err = json .Unmarshal (reqBody , & jsonMap )
133+ if err != nil {
134+ return nil , err
135+ }
136+ }
137+
138+ jsonMap ["_async" ] = true
139+ configJSON , err := json .Marshal (jsonMap )
140+ if err != nil {
141+ return nil , err
142+ }
143+ newReq , err := http .NewRequest (req .Method , req .URL .String (), bytes .NewBuffer (configJSON ))
144+ if err != nil {
145+ return nil , err
146+ }
147+ return newReq , nil
148+ }
149+
119150// Do sends and API Request and returns the body as an array of bytes
120151func (c * Client ) Do (req * http.Request ) ([]byte , error ) {
152+ req , err := async (req ) // make request asynchronous
153+ if err != nil {
154+ return nil , err
155+ }
121156 client := & http.Client {}
122157 tokenString , err := c .JWT .Get ()
123158 if err != nil {
@@ -137,19 +172,95 @@ func (c *Client) Do(req *http.Request) ([]byte, error) {
137172 if err != nil {
138173 return nil , err
139174 }
175+ taskID := string (body )
140176
141177 c .logger .Debug ("OVC call: " + req .URL .Path )
142178 c .logger .Debug ("OVC response status code: " + resp .Status )
143- c .logger .Debug ("OVC response body: " + string (body ))
179+ c .logger .Debug ("OVC response body: " + string (taskID ))
144180
145181 switch {
146182 case resp .StatusCode == 401 :
147183 return nil , ErrAuthentication
148184 case resp .StatusCode > 202 :
149- return body , errors .New (string ( body ) )
185+ return body , errors .New (taskID )
150186 }
151187
152- return body , nil
188+ // remove quotes from taskID if contains any
189+ taskID = strings .Replace (taskID , "\" " , "" , - 1 )
190+
191+ // create request to get result of an async API call by job id
192+ taskJSON , err := json .Marshal (
193+ struct {
194+ TaskID string `json:"taskguid"`
195+ }{
196+ TaskID : taskID ,
197+ },
198+ )
199+ if err != nil {
200+ return nil , err
201+ }
202+
203+ var taskResp * http.Response
204+ result := make ([]interface {}, 0 )
205+ start , timeout := time .Now (), 10 * time .Minute
206+
207+ // wait for result of the async task
208+ for {
209+ taskReq , err := http .NewRequest ("POST" , c .ServerURL + "/system/task/get" , bytes .NewBuffer (taskJSON ))
210+ if err != nil {
211+ return nil , err
212+ }
213+ taskReq .Header .Set ("Authorization" , fmt .Sprintf ("bearer %s" , tokenString ))
214+ taskReq .Header .Set ("Content-Type" , "application/json" )
215+ taskResp , err = client .Do (taskReq )
216+ if taskResp != nil {
217+ defer taskResp .Body .Close ()
218+ }
219+ if err != nil {
220+ return nil , err
221+ }
222+ switch {
223+ case taskResp .StatusCode == 401 :
224+ return nil , ErrAuthentication
225+ case taskResp .StatusCode == 404 :
226+ // task may have not been registered yet
227+ continue
228+ case taskResp .StatusCode > 202 :
229+ return nil , errors .New (taskID )
230+ }
231+ resultBody , err := ioutil .ReadAll (taskResp .Body )
232+ if err != nil {
233+ return nil , err
234+ }
235+ if len (resultBody ) != 0 {
236+ // if body is not empty, parse result
237+ err = json .Unmarshal (resultBody , & result )
238+ if err != nil {
239+ return resultBody , err
240+ }
241+ if len (result ) != 0 {
242+ // result is not empty if can be parsed to a []interface{}
243+ break
244+ }
245+ }
246+ if now := time .Now (); now .Sub (start ) > timeout {
247+ return nil , fmt .Errorf ("job timeout %s" , taskID )
248+ }
249+ time .Sleep (2 * time .Second )
250+ }
251+
252+ success , ok := result [0 ].(bool )
253+ if ! ok {
254+ return nil , fmt .Errorf ("Task response is incorrect taskId %v \n expected response in form [True/False, taskResult], received: \n %v" , string (taskID ), result )
255+ }
256+ if ! success {
257+ return nil , fmt .Errorf ("Task was not successfull taskID: %v:\n %v" , string (taskID ), result [1 ])
258+ }
259+ finalBody , err := json .Marshal (result [1 ])
260+ if err != nil {
261+ return finalBody , err
262+ }
263+ return finalBody , nil
153264}
154265
155266// GetLocation parses the URL to return the location of the API
0 commit comments