working on history command, remote:showall

This commit is contained in:
sawka 2022-08-28 14:24:05 -07:00
parent 9d6cc1f67a
commit f2a5985349
7 changed files with 182 additions and 26 deletions

View File

@ -114,7 +114,11 @@ CREATE TABLE history (
screenid varchar(36) NOT NULL, screenid varchar(36) NOT NULL,
windowid varchar(36) NOT NULL, windowid varchar(36) NOT NULL,
lineid int NOT NULL, lineid int NOT NULL,
remoteownerid varchar(36) NOT NULL,
remoteid varchar(36) NOT NULL,
remotename varchar(50) NOT NULL,
haderror boolean NOT NULL, haderror boolean NOT NULL,
cmdid varchar(36) NOT NULL, cmdid varchar(36) NOT NULL,
cmdstr text NOT NULL cmdstr text NOT NULL,
ismetacmd boolean
); );

View File

@ -107,7 +107,11 @@ CREATE TABLE history (
screenid varchar(36) NOT NULL, screenid varchar(36) NOT NULL,
windowid varchar(36) NOT NULL, windowid varchar(36) NOT NULL,
lineid int NOT NULL, lineid int NOT NULL,
remoteownerid varchar(36) NOT NULL,
remoteid varchar(36) NOT NULL,
remotename varchar(50) NOT NULL,
haderror boolean NOT NULL, haderror boolean NOT NULL,
cmdid varchar(36) NOT NULL, cmdid varchar(36) NOT NULL,
cmdstr text NOT NULL cmdstr text NOT NULL,
ismetacmd boolean
); );

View File

@ -63,6 +63,9 @@ func init() {
registerCmdAlias("remote", RemoteCommand) registerCmdAlias("remote", RemoteCommand)
registerCmdFn("remote:show", RemoteShowCommand) registerCmdFn("remote:show", RemoteShowCommand)
registerCmdFn("remote:showall", RemoteShowAllCommand)
registerCmdFn("history", HistoryCommand)
} }
func getValidCommands() []string { func getValidCommands() []string {
@ -126,6 +129,17 @@ func resolveBool(arg string, def bool) bool {
return true return true
} }
func resolveInt(arg string, def int) (int, error) {
if arg == "" {
return def, nil
}
ival, err := strconv.Atoi(arg)
if err != nil {
return 0, err
}
return ival, nil
}
func RunCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) { func RunCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
ids, err := resolveIds(ctx, pk, R_Session|R_Window|R_Remote) ids, err := resolveIds(ctx, pk, R_Session|R_Window|R_Remote)
if err != nil { if err != nil {
@ -159,13 +173,13 @@ func RunCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.U
return sstore.ModelUpdate{Line: rtnLine, Cmd: cmd}, nil return sstore.ModelUpdate{Line: rtnLine, Cmd: cmd}, nil
} }
func addToHistory(ctx context.Context, pk *scpacket.FeCommandPacketType, update sstore.UpdatePacket, hadError bool) error { func addToHistory(ctx context.Context, pk *scpacket.FeCommandPacketType, update sstore.UpdatePacket, isMetaCmd bool, hadError bool) error {
cmdStr := firstArg(pk) cmdStr := firstArg(pk)
ids, err := resolveIds(ctx, pk, R_Session|R_Screen|R_Window) ids, err := resolveIds(ctx, pk, R_Session|R_Screen|R_Window)
if err != nil { if err != nil {
return err return err
} }
lineId, cmdId := sstore.ReadLineCmdIdFromUpdate(update) lineId, cmdId, rptr := sstore.ReadHistoryDataFromUpdate(update)
hitem := &sstore.HistoryItemType{ hitem := &sstore.HistoryItemType{
HistoryId: uuid.New().String(), HistoryId: uuid.New().String(),
Ts: time.Now().UnixMilli(), Ts: time.Now().UnixMilli(),
@ -177,6 +191,10 @@ func addToHistory(ctx context.Context, pk *scpacket.FeCommandPacketType, update
HadError: hadError, HadError: hadError,
CmdId: cmdId, CmdId: cmdId,
CmdStr: cmdStr, CmdStr: cmdStr,
IsMetaCmd: isMetaCmd,
}
if !isMetaCmd && rptr != nil {
hitem.Remote = *rptr
} }
err = sstore.InsertHistoryItem(ctx, hitem) err = sstore.InsertHistoryItem(ctx, hitem)
if err != nil { if err != nil {
@ -195,7 +213,7 @@ func EvalCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.
} }
update, err := HandleCommand(ctx, newPk) update, err := HandleCommand(ctx, newPk)
if !resolveBool(pk.Kwargs["nohist"], false) { if !resolveBool(pk.Kwargs["nohist"], false) {
err := addToHistory(ctx, pk, update, (err != nil)) err := addToHistory(ctx, pk, update, (newPk.MetaCmd != "run"), (err != nil))
if err != nil { if err != nil {
fmt.Printf("[error] adding to history: %v\n", err) fmt.Printf("[error] adding to history: %v\n", err)
// continue... // continue...
@ -369,11 +387,30 @@ func RemoteShowCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (s
if ids.RemoteState != nil { if ids.RemoteState != nil {
buf.WriteString(fmt.Sprintf(" %-15s %s\n", "cwd", ids.RemoteState.Cwd)) buf.WriteString(fmt.Sprintf(" %-15s %s\n", "cwd", ids.RemoteState.Cwd))
} }
output := buf.String()
return sstore.ModelUpdate{ return sstore.ModelUpdate{
Info: &sstore.InfoMsgType{ Info: &sstore.InfoMsgType{
InfoTitle: fmt.Sprintf("show remote '%s' info", ids.RemoteDisplayName), InfoTitle: fmt.Sprintf("show remote '%s' info", ids.RemoteDisplayName),
InfoLines: splitLinesForInfo(output), InfoLines: splitLinesForInfo(buf.String()),
},
}, nil
}
func RemoteShowAllCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
stateArr := remote.GetAllRemoteState()
var buf bytes.Buffer
for _, rstate := range stateArr {
var name string
if rstate.RemoteAlias == "" {
name = rstate.RemoteCanonicalName
} else {
name = fmt.Sprintf("%s (%s)", rstate.RemoteCanonicalName, rstate.RemoteAlias)
}
buf.WriteString(fmt.Sprintf("%-12s %-5s %8s %s\n", rstate.Status, rstate.RemoteType, rstate.RemoteId[0:8], name))
}
return sstore.ModelUpdate{
Info: &sstore.InfoMsgType{
InfoTitle: fmt.Sprintf("show all remote info"),
InfoLines: splitLinesForInfo(buf.String()),
}, },
}, nil }, nil
} }
@ -903,6 +940,55 @@ func ClearCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore
return update, nil return update, nil
} }
const DefaultMaxHistoryItems = 10000
func HistoryCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
ids, err := resolveIds(ctx, pk, R_Session|R_Screen|R_Window|R_Remote)
if err != nil {
return nil, err
}
maxItems, err := resolveInt(pk.Kwargs["maxitems"], DefaultMaxHistoryItems)
if err != nil {
return nil, fmt.Errorf("invalid maxitems value '%s' (must be a number): %v", pk.Kwargs["maxitems"], err)
}
if maxItems < 0 {
return nil, fmt.Errorf("invalid maxitems value '%s' (cannot be negative)", maxItems)
}
if maxItems == 0 {
maxItems = DefaultMaxHistoryItems
}
hitems, err := sstore.GetSessionHistoryItems(ctx, ids.SessionId, maxItems)
if err != nil {
return nil, err
}
var filteredItems []*sstore.HistoryItemType
for _, hitem := range hitems {
if hitem.ScreenId == ids.ScreenId && hitem.WindowId == ids.WindowId && (hitem.Remote == ids.RemotePtr || hitem.IsMetaCmd) {
filteredItems = append(filteredItems, hitem)
}
}
var buf bytes.Buffer
if len(filteredItems) == 0 {
buf.WriteString("(no history)")
} else {
for idx := len(filteredItems) - 1; idx >= 0; idx-- {
hitem := filteredItems[idx]
hnumStr := hitem.HistoryNum
if hitem.IsMetaCmd {
hnumStr = "*" + hnumStr
}
hstr := fmt.Sprintf("%6s %s\n", hnumStr, hitem.CmdStr)
buf.WriteString(hstr)
}
}
update := &sstore.ModelUpdate{}
update.Info = &sstore.InfoMsgType{
InfoMsg: fmt.Sprintf("history, limited to current session, screen, window, and remote (maxitems=%d)", maxItems),
InfoLines: splitLinesForInfo(buf.String()),
}
return update, nil
}
func splitLinesForInfo(str string) []string { func splitLinesForInfo(str string) []string {
rtn := strings.Split(str, "\n") rtn := strings.Split(str, "\n")
if rtn[len(rtn)-1] == "" { if rtn[len(rtn)-1] == "" {

View File

@ -155,6 +155,9 @@ func EvalMetaCommand(ctx context.Context, origPk *scpacket.FeCommandPacketType)
if len(origPk.Args) == 0 { if len(origPk.Args) == 0 {
return nil, fmt.Errorf("empty command (no fields)") return nil, fmt.Errorf("empty command (no fields)")
} }
if strings.TrimSpace(origPk.Args[0]) == "" {
return nil, fmt.Errorf("empty command")
}
metaCmd, metaSubCmd, commandArgs := parseMetaCmd(origPk.Args[0]) metaCmd, metaSubCmd, commandArgs := parseMetaCmd(origPk.Args[0])
rtnPk := scpacket.MakeFeCommandPacket() rtnPk := scpacket.MakeFeCommandPacket()
rtnPk.MetaCmd = metaCmd rtnPk.MetaCmd = metaCmd

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"database/sql" "database/sql"
"fmt" "fmt"
"strconv"
"strings" "strings"
"github.com/google/uuid" "github.com/google/uuid"
@ -107,9 +108,9 @@ func InsertHistoryItem(ctx context.Context, hitem *HistoryItemType) error {
if err != nil { if err != nil {
return err return err
} }
query := `INSERT INTO history ( historyid, ts, userid, sessionid, screenid, windowid, lineid, cmdid, haderror, cmdstr) VALUES query := `INSERT INTO history ( historyid, ts, userid, sessionid, screenid, windowid, lineid, cmdid, haderror, cmdstr, remoteownerid, remoteid, remotename, ismetacmd) VALUES
(:historyid,:ts,:userid,:sessionid,:screenid,:windowid,:lineid,:cmdid,:haderror,:cmdstr)` (:historyid,:ts,:userid,:sessionid,:screenid,:windowid,:lineid,:cmdid,:haderror,:cmdstr,:remoteownerid,:remoteid,:remotename,:ismetacmd)`
_, err = db.NamedExec(query, hitem) _, err = db.NamedExec(query, hitem.ToMap())
if err != nil { if err != nil {
return err return err
} }
@ -119,8 +120,16 @@ func InsertHistoryItem(ctx context.Context, hitem *HistoryItemType) error {
func GetSessionHistoryItems(ctx context.Context, sessionId string, maxItems int) ([]*HistoryItemType, error) { func GetSessionHistoryItems(ctx context.Context, sessionId string, maxItems int) ([]*HistoryItemType, error) {
var rtn []*HistoryItemType var rtn []*HistoryItemType
err := WithTx(ctx, func(tx *TxWrap) error { err := WithTx(ctx, func(tx *TxWrap) error {
query := `SELECT * FROM history WHERE sessionid = ? ORDER BY ts DESC LIMIT ?` query := `SELECT count(*) FROM history WHERE sessionid = ?`
tx.SelectWrap(&rtn, query, sessionId, maxItems) totalNum := tx.GetInt(query, sessionId)
query = `SELECT * FROM history WHERE sessionid = ? ORDER BY ts DESC, historyid LIMIT ?`
marr := tx.SelectMaps(query, sessionId, maxItems)
for idx, m := range marr {
hnum := totalNum - idx
hitem := HistoryItemFromMap(m)
hitem.HistoryNum = strconv.Itoa(hnum)
rtn = append(rtn, hitem)
}
return nil return nil
}) })
if err != nil { if err != nil {

View File

@ -206,6 +206,47 @@ func WindowFromMap(m map[string]interface{}) *WindowType {
return &w return &w
} }
func (h *HistoryItemType) ToMap() map[string]interface{} {
rtn := make(map[string]interface{})
rtn["historyid"] = h.HistoryId
rtn["ts"] = h.Ts
rtn["userid"] = h.UserId
rtn["sessionid"] = h.SessionId
rtn["screenid"] = h.ScreenId
rtn["windowid"] = h.WindowId
rtn["lineid"] = h.LineId
rtn["haderror"] = h.HadError
rtn["cmdid"] = h.CmdId
rtn["cmdstr"] = h.CmdStr
rtn["remoteownerid"] = h.Remote.OwnerId
rtn["remoteid"] = h.Remote.RemoteId
rtn["remotename"] = h.Remote.Name
rtn["ismetacmd"] = h.IsMetaCmd
return rtn
}
func HistoryItemFromMap(m map[string]interface{}) *HistoryItemType {
if len(m) == 0 {
return nil
}
var h HistoryItemType
quickSetStr(&h.HistoryId, m, "historyid")
quickSetInt64(&h.Ts, m, "ts")
quickSetStr(&h.UserId, m, "userid")
quickSetStr(&h.SessionId, m, "sessionid")
quickSetStr(&h.ScreenId, m, "screenid")
quickSetStr(&h.WindowId, m, "windowid")
quickSetStr(&h.LineId, m, "lineid")
quickSetBool(&h.HadError, m, "haderror")
quickSetStr(&h.CmdId, m, "cmdid")
quickSetStr(&h.CmdStr, m, "cmdstr")
quickSetStr(&h.Remote.OwnerId, m, "remoteownerid")
quickSetStr(&h.Remote.RemoteId, m, "remoteid")
quickSetStr(&h.Remote.Name, m, "remotename")
quickSetBool(&h.IsMetaCmd, m, "ismetacmd")
return &h
}
type ScreenOptsType struct { type ScreenOptsType struct {
TabColor string `json:"tabcolor,omitempty"` TabColor string `json:"tabcolor,omitempty"`
} }
@ -281,9 +322,14 @@ type HistoryItemType struct {
HadError bool `json:"haderror"` HadError bool `json:"haderror"`
CmdId string `json:"cmdid"` CmdId string `json:"cmdid"`
CmdStr string `json:"cmdstr"` CmdStr string `json:"cmdstr"`
Remote RemotePtrType `json:"remote"`
IsMetaCmd bool `json:"ismetacmd"`
// only for updates // only for updates
Remove bool `json:"remove"` Remove bool `json:"remove"`
// transient (string because of different history orderings)
HistoryNum string `json:"historynum"`
} }
type RemoteState struct { type RemoteState struct {

View File

@ -49,15 +49,19 @@ func MakeSingleSessionUpdate(sessionId string) (ModelUpdate, *SessionType) {
return update, session return update, session
} }
func ReadLineCmdIdFromUpdate(update UpdatePacket) (string, string) { func ReadHistoryDataFromUpdate(update UpdatePacket) (string, string, *RemotePtrType) {
modelUpdate, ok := update.(ModelUpdate) modelUpdate, ok := update.(ModelUpdate)
if !ok { if !ok {
return "", "" return "", "", nil
} }
if modelUpdate.Line == nil { if modelUpdate.Line == nil {
return "", "" return "", "", nil
} }
return modelUpdate.Line.LineId, modelUpdate.Line.CmdId var rptr *RemotePtrType
if modelUpdate.Cmd != nil {
rptr = &modelUpdate.Cmd.Remote
}
return modelUpdate.Line.LineId, modelUpdate.Line.CmdId, rptr
} }
type InfoMsgType struct { type InfoMsgType struct {