working on playbooks and history view

This commit is contained in:
sawka
2023-03-02 00:31:19 -08:00
parent fa393e2eec
commit 014f1132a7
6 changed files with 249 additions and 7 deletions
+3
View File
@@ -0,0 +1,3 @@
DROP TABLE playbook;
DROP TABLE playbook_entry;
+16
View File
@@ -0,0 +1,16 @@
CREATE TABLE playbook (
playbookid varchar(36) PRIMARY KEY,
playbookname varchar(100) NOT NULL,
description text NOT NULL,
entryids json NOT NULL
);
CREATE TABLE playbook_entry (
entryid varchar(36) PRIMARY KEY,
playbookid varchar(36) NOT NULL,
description text NOT NULL,
alias varchar(50) NOT NULL,
cmdstr text NOT NULL,
createdts bigint NOT NULL,
updatedts bigint NOT NULL
);
+32 -2
View File
@@ -176,6 +176,7 @@ func init() {
registerCmdFn("telemetry:show", TelemetryShowCommand)
registerCmdFn("history", HistoryCommand)
registerCmdFn("history:viewall", HistoryViewAllCommand)
registerCmdFn("bookmarks:show", BookmarksShowCommand)
@@ -1809,6 +1810,35 @@ func ClearCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore
}
func HistoryViewAllCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
_, err := resolveUiIds(ctx, pk, 0)
if err != nil {
return nil, err
}
offset, err := resolveNonNegInt(pk.Kwargs["offset"], 0)
if err != nil {
return nil, err
}
opts := sstore.HistoryQueryOpts{MaxItems: 51, Offset: offset}
if pk.Kwargs["text"] != "" {
opts.SearchText = pk.Kwargs["text"]
}
hitems, err := sstore.GetHistoryItems(ctx, "", "", opts)
if err != nil {
return nil, err
}
hvdata := &sstore.HistoryViewData{
TotalCount: 0,
Offset: offset,
Items: hitems,
}
update := sstore.ModelUpdate{
HistoryViewData: hvdata,
MainView: sstore.MainViewHistory,
}
return update, nil
}
const DefaultMaxHistoryItems = 10000
func HistoryCommand(ctx context.Context, pk *scpacket.FeCommandPacketType) (sstore.UpdatePacket, error) {
@@ -1971,8 +2001,8 @@ func BookmarksShowCommand(ctx context.Context, pk *scpacket.FeCommandPacketType)
log.Printf("error updating current activity (bookmarks): %v\n", err)
}
update := sstore.ModelUpdate{
BookmarksView: true,
Bookmarks: bms,
MainView: sstore.MainViewBookmarks,
Bookmarks: bms,
}
return update, nil
}
+104 -2
View File
@@ -223,6 +223,7 @@ func runHistoryQuery(tx *TxWrap, sessionId string, windowId string, opts History
}
hnumStr := ""
whereClause := ""
var queryArgs []interface{}
if sessionId != "" && windowId != "" {
whereClause = fmt.Sprintf("WHERE sessionid = '%s' AND windowid = '%s'", sessionId, windowId)
hnumStr = "w"
@@ -232,15 +233,26 @@ func runHistoryQuery(tx *TxWrap, sessionId string, windowId string, opts History
} else {
hnumStr = "g"
}
if opts.SearchText != "" {
if whereClause == "" {
whereClause = "WHERE cmdstr LIKE ? ESCAPE '\\'"
} else {
whereClause = whereClause + " AND cmdstr LIKE ? ESCAPE '\\'"
}
likeArg := opts.SearchText
likeArg = strings.ReplaceAll(likeArg, "%", "\\%")
likeArg = strings.ReplaceAll(likeArg, "_", "\\_")
queryArgs = append(queryArgs, "%"+likeArg+"%")
}
maxItems := opts.MaxItems
if maxItems == 0 {
maxItems = DefaultMaxHistoryItems
}
query := fmt.Sprintf("SELECT %s, '%s' || row_number() OVER win AS historynum FROM history %s WINDOW win AS (ORDER BY ts, historyid) ORDER BY ts DESC, historyid DESC LIMIT %d", HistoryCols, hnumStr, whereClause, maxItems)
query := fmt.Sprintf("SELECT %s, '%s' || row_number() OVER win AS historynum FROM history %s WINDOW win AS (ORDER BY ts, historyid) ORDER BY ts DESC, historyid DESC LIMIT %d OFFSET %d", HistoryCols, hnumStr, whereClause, maxItems, opts.Offset)
if opts.FromTs > 0 {
query = fmt.Sprintf("SELECT * FROM (%s) WHERE ts >= %d", query, opts.FromTs)
}
marr := tx.SelectMaps(query)
marr := tx.SelectMaps(query, queryArgs...)
rtn := make([]*HistoryItemType, len(marr))
for idx, m := range marr {
hitem := HistoryItemFromMap(m)
@@ -2241,3 +2253,93 @@ func DeleteBookmark(ctx context.Context, bookmarkId string) error {
})
return txErr
}
func CreatePlaybook(ctx context.Context, name string) (*PlaybookType, error) {
var rtn *PlaybookType
txErr := WithTx(ctx, func(tx *TxWrap) error {
query := `SELECT playbookid FROM playbook WHERE name = ?`
if tx.Exists(query, name) {
return fmt.Errorf("playbook %q already exists", name)
}
rtn = &PlaybookType{}
rtn.PlaybookId = uuid.New().String()
rtn.PlaybookName = name
query = `INSERT INTO playbook ( playbookid, playbookname, description, entryids)
VALUES (:playbookid,:playbookname,:description,:entryids)`
tx.Exec(query, rtn.ToMap())
return nil
})
if txErr != nil {
return nil, txErr
}
return rtn, nil
}
func selectPlaybook(tx *TxWrap, playbookId string) *PlaybookType {
query := `SELECT * FROM playbook where playbookid = ?`
m := tx.GetMap(query, playbookId)
playbook := PlaybookFromMap(m)
return playbook
}
func AddPlaybookEntry(ctx context.Context, entry *PlaybookEntry) error {
if entry.EntryId == "" {
return fmt.Errorf("invalid entryid")
}
txErr := WithTx(ctx, func(tx *TxWrap) error {
playbook := selectPlaybook(tx, entry.PlaybookId)
if playbook == nil {
return fmt.Errorf("cannot add entry, playbook does not exist")
}
query := `SELECT entryid FROM playbook_entry WHERE entryid = ?`
if tx.Exists(query, entry.EntryId) {
return fmt.Errorf("cannot add entry, entryid already exists")
}
query = `INSERT INTO playbook_entry ( entryid, playbookid, description, alias, cmdstr, createdts, updatedts)
VALUES (:entryid,:playbookid,:description,:alias,:cmdstr,:createdts,:updatedts)`
tx.Exec(query, entry)
playbook.EntryIds = append(playbook.EntryIds, entry.EntryId)
query = `UPDATE playbook SET entryids = ? WHERE playbookid = ?`
tx.Exec(query, quickJsonArr(playbook.EntryIds), entry.PlaybookId)
return nil
})
return txErr
}
func RemovePlaybookEntry(ctx context.Context, playbookId string, entryId string) error {
txErr := WithTx(ctx, func(tx *TxWrap) error {
playbook := selectPlaybook(tx, playbookId)
if playbook == nil {
return fmt.Errorf("cannot remove playbook entry, playbook does not exist")
}
query := `SELECT entryid FROM playbook_entry WHERE entryid = ?`
if !tx.Exists(query, entryId) {
return fmt.Errorf("cannot remove playbook entry, entry does not exist")
}
query = `DELETE FROM playbook_entry WHERE entryid = ?`
tx.Exec(query, entryId)
playbook.RemoveEntry(entryId)
query = `UPDATE playbook SET entryids = ? WHERE playbookid = ?`
tx.Exec(query, quickJsonArr(playbook.EntryIds), playbookId)
return nil
})
return txErr
}
func GetPlaybookById(ctx context.Context, playbookId string) (*PlaybookType, error) {
var rtn *PlaybookType
txErr := WithTx(ctx, func(tx *TxWrap) error {
rtn = selectPlaybook(tx, playbookId)
if rtn == nil {
return nil
}
query := `SELECT * FROM playbook_entry WHERE playbookid = ?`
tx.Select(&rtn.Entries, query, playbookId)
rtn.OrderEntries()
return nil
})
if txErr != nil {
return nil, txErr
}
return rtn, nil
}
+86 -2
View File
@@ -39,6 +39,12 @@ const DefaultScreenWindowName = "w1"
const DefaultCwd = "~"
const (
MainViewSession = "session"
MainViewBookmarks = "bookmarks"
MainViewHistory = "history"
)
const (
CmdStatusRunning = "running"
CmdStatusDetached = "detached"
@@ -525,8 +531,10 @@ type HistoryItemType struct {
}
type HistoryQueryOpts struct {
MaxItems int
FromTs int64
Offset int
MaxItems int
FromTs int64
SearchText string
}
type TermOpts struct {
@@ -674,6 +682,82 @@ type LineType struct {
Remove bool `json:"remove,omitempty"`
}
type PlaybookType struct {
PlaybookId string `json:"playbookid"`
PlaybookName string `json:"playbookname"`
Description string `json:"description"`
EntryIds []string `json:"entryids"`
// this is not persisted to DB, just for transport to FE
Entries []*PlaybookEntry `json:"entries"`
}
func (p *PlaybookType) ToMap() map[string]interface{} {
rtn := make(map[string]interface{})
rtn["playbookid"] = p.PlaybookId
rtn["playbookname"] = p.PlaybookName
rtn["description"] = p.Description
rtn["entryids"] = quickJsonArr(p.EntryIds)
return rtn
}
func PlaybookFromMap(m map[string]interface{}) *PlaybookType {
if len(m) == 0 {
return nil
}
var p PlaybookType
quickSetStr(&p.PlaybookId, m, "playbookid")
quickSetStr(&p.PlaybookName, m, "playbookname")
quickSetStr(&p.Description, m, "description")
quickSetJsonArr(&p.Entries, m, "entries")
return &p
}
// reorders p.Entries to match p.EntryIds
func (p *PlaybookType) OrderEntries() {
if len(p.Entries) == 0 {
return
}
m := make(map[string]*PlaybookEntry)
for _, entry := range p.Entries {
m[entry.EntryId] = entry
}
newList := make([]*PlaybookEntry, 0, len(p.EntryIds))
for _, entryId := range p.EntryIds {
entry := m[entryId]
if entry != nil {
newList = append(newList, entry)
}
}
p.Entries = newList
}
// removes from p.EntryIds (not from p.Entries)
func (p *PlaybookType) RemoveEntry(entryIdToRemove string) {
if len(p.EntryIds) == 0 {
return
}
newList := make([]string, 0, len(p.EntryIds)-1)
for _, entryId := range p.EntryIds {
if entryId == entryIdToRemove {
continue
}
newList = append(newList, entryId)
}
p.EntryIds = newList
}
type PlaybookEntry struct {
PlaybookId string `json:"playbookid"`
EntryId string `json:"entryid"`
Alias string `json:"alias"`
CmdStr string `json:"cmdstr"`
UpdatedTs int64 `json:"updatedts"`
CreatedTs int64 `json:"createdts"`
Description string `json:"description"`
Remove bool `json:"remove,omitempty"`
}
type BookmarkType struct {
BookmarkId string `json:"bookmarkid"`
CreatedTs int64 `json:"createdts"`
+8 -1
View File
@@ -43,8 +43,9 @@ type ModelUpdate struct {
History *HistoryInfoType `json:"history,omitempty"`
Interactive bool `json:"interactive"`
Connect bool `json:"connect,omitempty"`
BookmarksView bool `json:"bookmarksview,omitempty"`
MainView string `json:"mainview,omitempty"`
Bookmarks []*BookmarkType `json:"bookmarks,omitempty"`
HistoryViewData *HistoryViewData `json:"historyviewdata,omitempty"`
ClientData *ClientData `json:"clientdata,omitempty"`
}
@@ -74,6 +75,12 @@ func InfoMsgUpdate(infoMsgFmt string, args ...interface{}) *ModelUpdate {
}
}
type HistoryViewData struct {
TotalCount int `json:"totalcount"`
Offset int `json:"offset"`
Items []*HistoryItemType `json:"items"`
}
type RemoteEditType struct {
RemoteEdit bool `json:"remoteedit"`
RemoteId string `json:"remoteid,omitempty"`