|
|
|
@ -27,6 +27,7 @@ import (
|
|
|
|
|
"github.com/asciimoo/wuzz/config"
|
|
|
|
|
"github.com/asciimoo/wuzz/formatter"
|
|
|
|
|
|
|
|
|
|
"github.com/alessio/shellescape"
|
|
|
|
|
"github.com/jroimartin/gocui"
|
|
|
|
|
"github.com/mattn/go-runewidth"
|
|
|
|
|
"github.com/nsf/termbox-go"
|
|
|
|
@ -59,6 +60,7 @@ const (
|
|
|
|
|
SAVE_DIALOG_VIEW = "save-dialog"
|
|
|
|
|
SAVE_RESPONSE_DIALOG_VIEW = "save-response-dialog"
|
|
|
|
|
LOAD_REQUEST_DIALOG_VIEW = "load-request-dialog"
|
|
|
|
|
SAVE_REQUEST_FORMAT_DIALOG_VIEW = "save-request-format-dialog"
|
|
|
|
|
SAVE_REQUEST_DIALOG_VIEW = "save-request-dialog"
|
|
|
|
|
SAVE_RESULT_VIEW = "save-result"
|
|
|
|
|
METHOD_LIST_VIEW = "method-list"
|
|
|
|
@ -72,6 +74,7 @@ var VIEW_TITLES = map[string]string{
|
|
|
|
|
SAVE_RESPONSE_DIALOG_VIEW: "Save Response (enter to submit, ctrl+q to cancel)",
|
|
|
|
|
LOAD_REQUEST_DIALOG_VIEW: "Load Request (enter to submit, ctrl+q to cancel)",
|
|
|
|
|
SAVE_REQUEST_DIALOG_VIEW: "Save Request (enter to submit, ctrl+q to cancel)",
|
|
|
|
|
SAVE_REQUEST_FORMAT_DIALOG_VIEW: "Choose export format",
|
|
|
|
|
SAVE_RESULT_VIEW: "Save Result (press enter to close)",
|
|
|
|
|
METHOD_LIST_VIEW: "Methods",
|
|
|
|
|
HELP_VIEW: "Help",
|
|
|
|
@ -268,6 +271,20 @@ var METHODS = []string{
|
|
|
|
|
http.MethodHead,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var EXPORT_FORMATS = []struct {
|
|
|
|
|
name string
|
|
|
|
|
export func(r Request) []byte
|
|
|
|
|
}{
|
|
|
|
|
{
|
|
|
|
|
name: "JSON",
|
|
|
|
|
export: exportJSON,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: "curl",
|
|
|
|
|
export: exportCurl,
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const DEFAULT_METHOD = http.MethodGet
|
|
|
|
|
|
|
|
|
|
var DEFAULT_FORMATTER = &formatter.TextFormatter{}
|
|
|
|
@ -1123,6 +1140,8 @@ func (a *App) SetKeys(g *gocui.Gui) error {
|
|
|
|
|
a.closePopup(g, METHOD_LIST_VIEW)
|
|
|
|
|
return nil
|
|
|
|
|
})
|
|
|
|
|
g.SetKeybinding(SAVE_REQUEST_FORMAT_DIALOG_VIEW, gocui.KeyArrowDown, gocui.ModNone, cursDown)
|
|
|
|
|
g.SetKeybinding(SAVE_REQUEST_FORMAT_DIALOG_VIEW, gocui.KeyArrowUp, gocui.ModNone, cursUp)
|
|
|
|
|
|
|
|
|
|
g.SetKeybinding(SAVE_DIALOG_VIEW, gocui.KeyCtrlQ, gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error {
|
|
|
|
|
a.closePopup(g, SAVE_DIALOG_VIEW)
|
|
|
|
@ -1267,6 +1286,68 @@ func (a *App) ToggleHistory(g *gocui.Gui, _ *gocui.View) (err error) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *App) SaveRequest(g *gocui.Gui, _ *gocui.View) (err error) {
|
|
|
|
|
// Destroy if present
|
|
|
|
|
if a.currentPopup == SAVE_REQUEST_FORMAT_DIALOG_VIEW {
|
|
|
|
|
a.closePopup(g, SAVE_REQUEST_FORMAT_DIALOG_VIEW)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
// Create the view listing the possible formats
|
|
|
|
|
popup, err := a.CreatePopupView(SAVE_REQUEST_FORMAT_DIALOG_VIEW, 30, len(EXPORT_FORMATS), g)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
popup.Title = VIEW_TITLES[SAVE_REQUEST_FORMAT_DIALOG_VIEW]
|
|
|
|
|
|
|
|
|
|
// Populate the popup witht the available formats
|
|
|
|
|
for _, r := range EXPORT_FORMATS {
|
|
|
|
|
fmt.Fprintln(popup, r.name)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g.SetViewOnTop(SAVE_REQUEST_FORMAT_DIALOG_VIEW)
|
|
|
|
|
g.SetCurrentView(SAVE_REQUEST_FORMAT_DIALOG_VIEW)
|
|
|
|
|
popup.SetCursor(0, 0)
|
|
|
|
|
|
|
|
|
|
// Bind the enter key, when the format is chosen, save the choice and open
|
|
|
|
|
// the save popup
|
|
|
|
|
g.SetKeybinding(SAVE_REQUEST_FORMAT_DIALOG_VIEW, gocui.KeyEnter, gocui.ModNone, func(g *gocui.Gui, v *gocui.View) error {
|
|
|
|
|
// Save the format index
|
|
|
|
|
_, format := v.Cursor()
|
|
|
|
|
// Open the Save popup
|
|
|
|
|
return a.OpenSaveDialog(VIEW_TITLES[SAVE_REQUEST_DIALOG_VIEW], g,
|
|
|
|
|
func(g *gocui.Gui, _ *gocui.View) error {
|
|
|
|
|
defer a.closePopup(g, SAVE_DIALOG_VIEW)
|
|
|
|
|
saveLocation := getViewValue(g, SAVE_DIALOG_VIEW)
|
|
|
|
|
|
|
|
|
|
r := Request{
|
|
|
|
|
Url: getViewValue(g, URL_VIEW),
|
|
|
|
|
Method: getViewValue(g, REQUEST_METHOD_VIEW),
|
|
|
|
|
GetParams: getViewValue(g, URL_PARAMS_VIEW),
|
|
|
|
|
Data: getViewValue(g, REQUEST_DATA_VIEW),
|
|
|
|
|
Headers: getViewValue(g, REQUEST_HEADERS_VIEW),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Export the request using the chosent format
|
|
|
|
|
request := EXPORT_FORMATS[format].export(r)
|
|
|
|
|
|
|
|
|
|
// Write the file
|
|
|
|
|
ioerr := ioutil.WriteFile(saveLocation, []byte(request), 0644)
|
|
|
|
|
|
|
|
|
|
saveResult := fmt.Sprintf("Request saved successfully in %s", EXPORT_FORMATS[format].name)
|
|
|
|
|
if ioerr != nil {
|
|
|
|
|
saveResult = "Error saving request: " + ioerr.Error()
|
|
|
|
|
}
|
|
|
|
|
viewErr := a.OpenSaveResultView(saveResult, g)
|
|
|
|
|
|
|
|
|
|
return viewErr
|
|
|
|
|
},
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *App) ToggleMethodList(g *gocui.Gui, _ *gocui.View) (err error) {
|
|
|
|
|
// Destroy if present
|
|
|
|
|
if a.currentPopup == METHOD_LIST_VIEW {
|
|
|
|
@ -1800,3 +1881,33 @@ func main() {
|
|
|
|
|
log.Panicln(err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func exportJSON(r Request) []byte {
|
|
|
|
|
requestMap := map[string]string{
|
|
|
|
|
URL_VIEW: r.Url,
|
|
|
|
|
REQUEST_METHOD_VIEW: r.Method,
|
|
|
|
|
URL_PARAMS_VIEW: r.GetParams,
|
|
|
|
|
REQUEST_DATA_VIEW: r.Data,
|
|
|
|
|
REQUEST_HEADERS_VIEW: r.Headers,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
request, err := json.Marshal(requestMap)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return []byte{}
|
|
|
|
|
}
|
|
|
|
|
return request
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func exportCurl(r Request) []byte {
|
|
|
|
|
var headers, params string
|
|
|
|
|
for _, header := range strings.Split(r.Headers, "\n") {
|
|
|
|
|
if header == "" {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
headers = fmt.Sprintf("%s -H %s", headers, shellescape.Quote(header))
|
|
|
|
|
}
|
|
|
|
|
if r.GetParams != "" {
|
|
|
|
|
params = fmt.Sprintf("?%s", r.GetParams)
|
|
|
|
|
}
|
|
|
|
|
return []byte(fmt.Sprintf("curl %s -X %s -d %s %s\n", headers, r.Method, shellescape.Quote(r.Data), shellescape.Quote(r.Url+params)))
|
|
|
|
|
}
|
|
|
|
|