add script triggers.

This commit is contained in:
2023-08-29 21:00:29 +02:00
parent 8776ed6df3
commit e8b77ae69c
16 changed files with 434 additions and 27 deletions
@@ -0,0 +1,18 @@
-- +goose Up
-- +goose StatementBegin
CREATE TABLE script_trigger (
id CHAR(36) NOT NULL PRIMARY KEY,
event VARCHAR(255) NOT NULL,
device_match TEXT NOT NULL,
parameter TEXT NOT NULL,
script_target TEXT NOT NULL,
script_name VARCHAR(255) NOT NULL,
script_pre JSON NOT NULL,
script_post JSON NOT NULL
)
-- +goose StatementEnd
-- +goose Down
-- +goose StatementBegin
DROP TABLE IF EXISTS script_trigger;
-- +goose StatementEnd
+30
View File
@@ -48,6 +48,9 @@ func Prepare(ctx context.Context, db DBTX) (*Queries, error) {
if q.deleteDeviceInfoLikeStmt, err = db.PrepareContext(ctx, deleteDeviceInfoLike); err != nil {
return nil, fmt.Errorf("error preparing query DeleteDeviceInfoLike: %w", err)
}
if q.deleteScriptTriggerStmt, err = db.PrepareContext(ctx, deleteScriptTrigger); err != nil {
return nil, fmt.Errorf("error preparing query DeleteScriptTrigger: %w", err)
}
if q.insertDeviceAliasStmt, err = db.PrepareContext(ctx, insertDeviceAlias); err != nil {
return nil, fmt.Errorf("error preparing query InsertDeviceAlias: %w", err)
}
@@ -63,6 +66,9 @@ func Prepare(ctx context.Context, db DBTX) (*Queries, error) {
if q.listDeviceInfosStmt, err = db.PrepareContext(ctx, listDeviceInfos); err != nil {
return nil, fmt.Errorf("error preparing query ListDeviceInfos: %w", err)
}
if q.listScriptTriggersStmt, err = db.PrepareContext(ctx, listScriptTriggers); err != nil {
return nil, fmt.Errorf("error preparing query ListScriptTriggers: %w", err)
}
if q.listScriptVariablesStmt, err = db.PrepareContext(ctx, listScriptVariables); err != nil {
return nil, fmt.Errorf("error preparing query ListScriptVariables: %w", err)
}
@@ -78,6 +84,9 @@ func Prepare(ctx context.Context, db DBTX) (*Queries, error) {
if q.replaceDeviceInfoStmt, err = db.PrepareContext(ctx, replaceDeviceInfo); err != nil {
return nil, fmt.Errorf("error preparing query ReplaceDeviceInfo: %w", err)
}
if q.replaceScriptTriggerStmt, err = db.PrepareContext(ctx, replaceScriptTrigger); err != nil {
return nil, fmt.Errorf("error preparing query ReplaceScriptTrigger: %w", err)
}
if q.saveScriptStmt, err = db.PrepareContext(ctx, saveScript); err != nil {
return nil, fmt.Errorf("error preparing query SaveScript: %w", err)
}
@@ -129,6 +138,11 @@ func (q *Queries) Close() error {
err = fmt.Errorf("error closing deleteDeviceInfoLikeStmt: %w", cerr)
}
}
if q.deleteScriptTriggerStmt != nil {
if cerr := q.deleteScriptTriggerStmt.Close(); cerr != nil {
err = fmt.Errorf("error closing deleteScriptTriggerStmt: %w", cerr)
}
}
if q.insertDeviceAliasStmt != nil {
if cerr := q.insertDeviceAliasStmt.Close(); cerr != nil {
err = fmt.Errorf("error closing insertDeviceAliasStmt: %w", cerr)
@@ -154,6 +168,11 @@ func (q *Queries) Close() error {
err = fmt.Errorf("error closing listDeviceInfosStmt: %w", cerr)
}
}
if q.listScriptTriggersStmt != nil {
if cerr := q.listScriptTriggersStmt.Close(); cerr != nil {
err = fmt.Errorf("error closing listScriptTriggersStmt: %w", cerr)
}
}
if q.listScriptVariablesStmt != nil {
if cerr := q.listScriptVariablesStmt.Close(); cerr != nil {
err = fmt.Errorf("error closing listScriptVariablesStmt: %w", cerr)
@@ -179,6 +198,11 @@ func (q *Queries) Close() error {
err = fmt.Errorf("error closing replaceDeviceInfoStmt: %w", cerr)
}
}
if q.replaceScriptTriggerStmt != nil {
if cerr := q.replaceScriptTriggerStmt.Close(); cerr != nil {
err = fmt.Errorf("error closing replaceScriptTriggerStmt: %w", cerr)
}
}
if q.saveScriptStmt != nil {
if cerr := q.saveScriptStmt.Close(); cerr != nil {
err = fmt.Errorf("error closing saveScriptStmt: %w", cerr)
@@ -236,16 +260,19 @@ type Queries struct {
deleteDeviceInfoStmt *sql.Stmt
deleteDeviceInfoByIDStmt *sql.Stmt
deleteDeviceInfoLikeStmt *sql.Stmt
deleteScriptTriggerStmt *sql.Stmt
insertDeviceAliasStmt *sql.Stmt
listDeviceAliasesStmt *sql.Stmt
listDeviceAssignmentsStmt *sql.Stmt
listDeviceAuthStmt *sql.Stmt
listDeviceInfosStmt *sql.Stmt
listScriptTriggersStmt *sql.Stmt
listScriptVariablesStmt *sql.Stmt
listScriptsStmt *sql.Stmt
replaceDeviceAssignmentStmt *sql.Stmt
replaceDeviceAuthStmt *sql.Stmt
replaceDeviceInfoStmt *sql.Stmt
replaceScriptTriggerStmt *sql.Stmt
saveScriptStmt *sql.Stmt
updateScriptVariablesStmt *sql.Stmt
}
@@ -262,16 +289,19 @@ func (q *Queries) WithTx(tx *sql.Tx) *Queries {
deleteDeviceInfoStmt: q.deleteDeviceInfoStmt,
deleteDeviceInfoByIDStmt: q.deleteDeviceInfoByIDStmt,
deleteDeviceInfoLikeStmt: q.deleteDeviceInfoLikeStmt,
deleteScriptTriggerStmt: q.deleteScriptTriggerStmt,
insertDeviceAliasStmt: q.insertDeviceAliasStmt,
listDeviceAliasesStmt: q.listDeviceAliasesStmt,
listDeviceAssignmentsStmt: q.listDeviceAssignmentsStmt,
listDeviceAuthStmt: q.listDeviceAuthStmt,
listDeviceInfosStmt: q.listDeviceInfosStmt,
listScriptTriggersStmt: q.listScriptTriggersStmt,
listScriptVariablesStmt: q.listScriptVariablesStmt,
listScriptsStmt: q.listScriptsStmt,
replaceDeviceAssignmentStmt: q.replaceDeviceAssignmentStmt,
replaceDeviceAuthStmt: q.replaceDeviceAuthStmt,
replaceDeviceInfoStmt: q.replaceDeviceInfoStmt,
replaceScriptTriggerStmt: q.replaceScriptTriggerStmt,
saveScriptStmt: q.saveScriptStmt,
updateScriptVariablesStmt: q.updateScriptVariablesStmt,
}
+11
View File
@@ -40,6 +40,17 @@ type Script struct {
Data json.RawMessage
}
type ScriptTrigger struct {
ID uuid.UUID
Event string
DeviceMatch string
Parameter string
ScriptTarget string
ScriptName string
ScriptPre json.RawMessage
ScriptPost json.RawMessage
}
type ScriptVariable struct {
Scope string
Name string
+77
View File
@@ -8,8 +8,55 @@ package mysqlgen
import (
"context"
"encoding/json"
"github.com/google/uuid"
)
const deleteScriptTrigger = `-- name: DeleteScriptTrigger :exec
DELETE FROM script_trigger WHERE id = ?
`
func (q *Queries) DeleteScriptTrigger(ctx context.Context, id uuid.UUID) error {
_, err := q.exec(ctx, q.deleteScriptTriggerStmt, deleteScriptTrigger, id)
return err
}
const listScriptTriggers = `-- name: ListScriptTriggers :many
SELECT id, event, device_match, parameter, script_target, script_name, script_pre, script_post FROM script_trigger
`
func (q *Queries) ListScriptTriggers(ctx context.Context) ([]ScriptTrigger, error) {
rows, err := q.query(ctx, q.listScriptTriggersStmt, listScriptTriggers)
if err != nil {
return nil, err
}
defer rows.Close()
items := []ScriptTrigger{}
for rows.Next() {
var i ScriptTrigger
if err := rows.Scan(
&i.ID,
&i.Event,
&i.DeviceMatch,
&i.Parameter,
&i.ScriptTarget,
&i.ScriptName,
&i.ScriptPre,
&i.ScriptPost,
); err != nil {
return nil, err
}
items = append(items, i)
}
if err := rows.Close(); err != nil {
return nil, err
}
if err := rows.Err(); err != nil {
return nil, err
}
return items, nil
}
const listScriptVariables = `-- name: ListScriptVariables :many
SELECT scope, name, value FROM script_variable
`
@@ -64,6 +111,36 @@ func (q *Queries) ListScripts(ctx context.Context) ([]Script, error) {
return items, nil
}
const replaceScriptTrigger = `-- name: ReplaceScriptTrigger :exec
REPLACE INTO script_trigger (id, event, device_match, parameter, script_target, script_name, script_pre, script_post)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
`
type ReplaceScriptTriggerParams struct {
ID uuid.UUID
Event string
DeviceMatch string
Parameter string
ScriptTarget string
ScriptName string
ScriptPre json.RawMessage
ScriptPost json.RawMessage
}
func (q *Queries) ReplaceScriptTrigger(ctx context.Context, arg ReplaceScriptTriggerParams) error {
_, err := q.exec(ctx, q.replaceScriptTriggerStmt, replaceScriptTrigger,
arg.ID,
arg.Event,
arg.DeviceMatch,
arg.Parameter,
arg.ScriptTarget,
arg.ScriptName,
arg.ScriptPre,
arg.ScriptPost,
)
return err
}
const saveScript = `-- name: SaveScript :exec
REPLACE INTO script (name, data) VALUES (?, ?)
`
+11 -1
View File
@@ -8,4 +8,14 @@ REPLACE INTO script (name, data) VALUES (?, ?);
SELECT * FROM script_variable;
-- name: UpdateScriptVariables :exec
REPLACE INTO script_variable (scope, name, value) VALUES (?, ?, ?);
REPLACE INTO script_variable (scope, name, value) VALUES (?, ?, ?);
-- name: ListScriptTriggers :many
SELECT * FROM script_trigger;
-- name: ReplaceScriptTrigger :exec
REPLACE INTO script_trigger (id, event, device_match, parameter, script_target, script_name, script_pre, script_post)
VALUES (?, ?, ?, ?, ?, ?, ?, ?);
-- name: DeleteScriptTrigger :exec
DELETE FROM script_trigger WHERE id = ?;
+69 -3
View File
@@ -141,7 +141,7 @@ func (d *database) HandleEvent(bus *lucifer3.EventBus, event lucifer3.Event) {
})
}
bus.RunCommand(script.UpdateScript{
bus.RunCommand(script.Update{
Name: scr.Name,
Lines: lines,
})
@@ -174,6 +174,26 @@ func (d *database) HandleEvent(bus *lucifer3.EventBus, event lucifer3.Event) {
bus.RunCommand(cmd)
}
scriptTriggers, err := q.ListScriptTriggers(timeout)
if err != nil {
bus.RunEvent(events.Log{
Level: "error",
Code: "database_could_not_list_script_triggers",
Message: "Database could not list script triggers: " + err.Error(),
})
}
for _, trig := range scriptTriggers {
bus.RunCommand(script.UpdateTrigger{
ID: trig.ID,
Event: script.TriggerEvent(trig.Event),
DeviceMatch: trig.DeviceMatch,
Parameter: trig.Parameter,
ScriptTarget: trig.ScriptTarget,
ScriptName: trig.ScriptName,
ScriptPre: fromJSON[[]script.Line](trig.ScriptPre),
ScriptPost: fromJSON[[]script.Line](trig.ScriptPost),
})
}
case events.DeviceAccepted:
if event.Extras == nil {
event.Extras = map[string]string{}
@@ -359,6 +379,33 @@ func (d *database) HandleCommand(bus *lucifer3.EventBus, command lucifer3.Comman
defer cancel()
switch command := command.(type) {
case script.UpdateTrigger:
err := q.ReplaceScriptTrigger(timeout, mysqlgen.ReplaceScriptTriggerParams{
ID: command.ID,
Event: string(command.Event),
DeviceMatch: command.DeviceMatch,
Parameter: command.Parameter,
ScriptTarget: command.ScriptTarget,
ScriptName: command.ScriptName,
ScriptPre: toJSON(command.ScriptPre),
ScriptPost: toJSON(command.ScriptPost),
})
if err != nil {
bus.RunEvent(events.Log{
Level: "error",
Code: "database_could_not_save_trigger",
Message: "Failed to save trigger: " + err.Error(),
})
}
case script.DeleteTrigger:
err := q.DeleteScriptTrigger(timeout, command.ID)
if err != nil {
bus.RunEvent(events.Log{
Level: "error",
Code: "database_could_not_delete_trigger",
Message: "Failed to delete trigger: " + err.Error(),
})
}
case script.SetVariable:
scopeName := "global"
if command.Match != nil {
@@ -368,13 +415,21 @@ func (d *database) HandleCommand(bus *lucifer3.EventBus, command lucifer3.Comman
}
if d.scriptVariables[scopeName+"::"+command.Key] != command.Value {
_ = q.UpdateScriptVariables(timeout, mysqlgen.UpdateScriptVariablesParams{
d.scriptVariables[scopeName+"::"+command.Key] = command.Value
err := q.UpdateScriptVariables(timeout, mysqlgen.UpdateScriptVariablesParams{
Scope: scopeName,
Name: command.Key,
Value: command.Value,
})
if err != nil {
bus.RunEvent(events.Log{
Level: "error",
Code: "database_could_not_save_variables",
Message: "Failed to save variable: " + err.Error(),
})
}
}
case script.UpdateScript:
case script.Update:
j, _ := json.Marshal(command.Lines)
err := q.SaveScript(timeout, mysqlgen.SaveScriptParams{
@@ -391,6 +446,17 @@ func (d *database) HandleCommand(bus *lucifer3.EventBus, command lucifer3.Comman
}
}
func fromJSON[T any](j []byte) T {
var t T
_ = json.Unmarshal(j, &t)
return t
}
func toJSON[T any](v T) json.RawMessage {
j, _ := json.Marshal(v)
return j
}
func Connect(host string, port int, username, password, dbname string) (lucifer3.ActiveService, error) {
db, err := sql.Open("mysql", fmt.Sprintf(
"%s:%s@(%s:%d)/%s?parseTime=true", username, password, host, port, dbname,