Skip to content

Commit 263ea07

Browse files
committed
network: implement asyncblock for polling job results
This implements async blocking if config option is enabled by polling the async job id and showing a cursor. Add vendor dependency: briandowns/spinner Signed-off-by: Rohit Yadav <rohit@apache.org>
1 parent b9b7e17 commit 263ea07

10 files changed

Lines changed: 1237 additions & 8 deletions

File tree

β€Žcli/completer.goβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ func (t *autoCompleter) Do(line []rune, pos int) (options [][]rune, offset int)
212212
autocompleteAPIArgs = append(autocompleteAPIArgs, "templatefilter=all")
213213
}
214214
fmt.Printf("\nFetching options, please wait...")
215-
response, _ := cmd.NewAPIRequest(r, autocompleteAPI.Name, autocompleteAPIArgs)
215+
response, _ := cmd.NewAPIRequest(r, autocompleteAPI.Name, autocompleteAPIArgs, false)
216216
fmt.Printf("\r")
217217

218218
var autocompleteOptions []selectOption

β€Žcmd/api.goβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ func init() {
160160
return nil
161161
}
162162

163-
response, err := NewAPIRequest(r, api.Name, apiArgs)
163+
response, err := NewAPIRequest(r, api.Name, apiArgs, api.Async)
164164
if err != nil {
165165
return err
166166
}

β€Žcmd/network.goβ€Ž

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,25 @@ import (
2626
"encoding/json"
2727
"errors"
2828
"fmt"
29+
"github.com/briandowns/spinner"
2930
"io/ioutil"
3031
"net/http"
3132
"net/http/cookiejar"
3233
"net/url"
34+
"runtime"
3335
"sort"
3436
"strings"
3537
"time"
3638
)
3739

40+
var cursor = []string{"\r⣷ 😸", "\r⣯ 😹", "\r⣟ 😺", "\r⑿ 😻", "\rⒿ 😼", "\r⣻ 😽", "\r⣽ 😾", "\r⣾ 😻"}
41+
42+
func init() {
43+
if runtime.GOOS == "windows" {
44+
cursor = []string{"|", "/", "-", "\\"}
45+
}
46+
}
47+
3848
func encodeRequestParams(params url.Values) string {
3949
if params == nil {
4050
return ""
@@ -98,8 +108,49 @@ func Login(r *Request) (*http.Client, string, error) {
98108
return client, sessionKey, nil
99109
}
100110

111+
func getResponseData(data map[string]interface{}) map[string]interface{} {
112+
for k := range data {
113+
if strings.HasSuffix(k, "response") {
114+
return data[k].(map[string]interface{})
115+
}
116+
}
117+
return nil
118+
}
119+
120+
func pollAsyncJob(r *Request, jobId string) (map[string]interface{}, error) {
121+
for timeout := float64(r.Config.Core.Timeout); timeout > 0.0; {
122+
startTime := time.Now()
123+
s := spinner.New(cursor, 200*time.Millisecond)
124+
s.Color("blue", "bold")
125+
s.Suffix = " polling for async API job result"
126+
s.Start()
127+
queryResult, queryError := NewAPIRequest(r, "queryAsyncJobResult", []string{"jobid=" + jobId}, false)
128+
diff := time.Duration(1*time.Second).Nanoseconds() - time.Now().Sub(startTime).Nanoseconds()
129+
if diff > 0 {
130+
time.Sleep(time.Duration(diff) * time.Nanosecond)
131+
}
132+
s.Stop()
133+
timeout = timeout - time.Now().Sub(startTime).Seconds()
134+
if queryError != nil {
135+
return queryResult, queryError
136+
}
137+
jobStatus := queryResult["jobstatus"].(float64)
138+
if jobStatus == 0 {
139+
continue
140+
}
141+
if jobStatus == 1 {
142+
return queryResult["jobresult"].(map[string]interface{}), nil
143+
144+
}
145+
if jobStatus == 2 {
146+
return queryResult, errors.New("async API job failed")
147+
}
148+
}
149+
return nil, errors.New("async API job query timed out")
150+
}
151+
101152
// NewAPIRequest makes an API request to configured management server
102-
func NewAPIRequest(r *Request, api string, args []string) (map[string]interface{}, error) {
153+
func NewAPIRequest(r *Request, api string, args []string, isAsync bool) (map[string]interface{}, error) {
103154
params := make(url.Values)
104155
params.Add("command", api)
105156
for _, arg := range args {
@@ -156,10 +207,16 @@ func NewAPIRequest(r *Request, api string, args []string) (map[string]interface{
156207
var data map[string]interface{}
157208
_ = json.Unmarshal([]byte(body), &data)
158209

159-
for k := range data {
160-
if strings.HasSuffix(k, "response") {
161-
return data[k].(map[string]interface{}), nil
210+
if isAsync && r.Config.Core.AsyncBlock {
211+
if jobResponse := getResponseData(data); jobResponse != nil && jobResponse["jobid"] != nil {
212+
jobId := jobResponse["jobid"].(string)
213+
return pollAsyncJob(r, jobId)
162214
}
163215
}
216+
217+
if apiResponse := getResponseData(data); apiResponse != nil {
218+
return apiResponse, nil
219+
}
220+
164221
return nil, errors.New("failed to decode response")
165222
}

β€Žcmd/set.goβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ func init() {
2727
Name: "set",
2828
Help: "Configures options for cmk",
2929
SubCommands: map[string][]string{
30-
"prompt": {"🐡", "random"},
30+
"prompt": {"🐡", "🐱", "random"},
3131
"asyncblock": {"true", "false"},
3232
"timeout": {"600", "1800", "3600"},
3333
"output": {"json", "text", "table", "xml"},

β€Žcmd/sync.goβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func init() {
2626
Name: "sync",
2727
Help: "Discovers and updates APIs",
2828
Handle: func(r *Request) error {
29-
response, err := NewAPIRequest(r, "listApis", []string{"listall=true"})
29+
response, err := NewAPIRequest(r, "listApis", []string{"listall=true"}, false)
3030
if err != nil {
3131
return err
3232
}

β€Žvendor/github.com/briandowns/spinner/LICENSEβ€Ž

Lines changed: 174 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

β€Žvendor/github.com/briandowns/spinner/character_sets.goβ€Ž

Lines changed: 59 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
Β (0)