2024-04-29 08:36:13 +02:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
// External
|
2024-06-01 09:18:56 +02:00
|
|
|
werr "git.gibonuddevalla.se/go/wrappederror"
|
2024-04-29 08:36:13 +02:00
|
|
|
"github.com/expr-lang/expr"
|
2024-05-01 10:02:33 +02:00
|
|
|
"github.com/lib/pq"
|
2024-04-29 08:36:13 +02:00
|
|
|
|
|
|
|
// Standard
|
2024-05-30 15:06:41 +02:00
|
|
|
"database/sql"
|
2024-04-29 08:36:13 +02:00
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Trigger struct {
|
2024-06-30 22:10:01 +02:00
|
|
|
ID int
|
|
|
|
Name string
|
|
|
|
SectionID int `db:"section_id"`
|
|
|
|
Expression string
|
|
|
|
Datapoints []string
|
|
|
|
DatapointValues map[string]any
|
2024-04-29 08:36:13 +02:00
|
|
|
}
|
|
|
|
|
2024-05-30 15:06:41 +02:00
|
|
|
func TriggerCreate(sectionID int, name string) (t Trigger, err error) { // {{{
|
|
|
|
t.SectionID = sectionID
|
|
|
|
t.Name = name
|
|
|
|
t.Expression = "false"
|
|
|
|
err = t.Update()
|
|
|
|
return
|
|
|
|
} // }}}
|
2024-04-29 08:36:13 +02:00
|
|
|
func TriggersRetrieve() (areas []Area, err error) { // {{{
|
|
|
|
areas = []Area{}
|
|
|
|
|
|
|
|
row := service.Db.Conn.QueryRow(`
|
|
|
|
WITH section_triggers AS (
|
|
|
|
SELECT
|
|
|
|
s.id AS id,
|
|
|
|
s.area_id,
|
|
|
|
s.name AS name,
|
|
|
|
jsonb_agg(
|
|
|
|
to_jsonb(t.*)
|
|
|
|
) AS triggers
|
|
|
|
FROM section s
|
|
|
|
LEFT JOIN "trigger" t ON t.section_id = s.id
|
|
|
|
GROUP BY
|
|
|
|
s.id, s.name)
|
|
|
|
|
|
|
|
SELECT
|
|
|
|
jsonb_agg(jsonsections)
|
|
|
|
FROM (
|
|
|
|
SELECT
|
|
|
|
a.id,
|
|
|
|
a.name,
|
|
|
|
jsonb_agg(
|
|
|
|
to_jsonb(
|
|
|
|
s.*
|
|
|
|
)
|
|
|
|
) AS sections
|
|
|
|
FROM area a
|
|
|
|
LEFT JOIN section_triggers s ON s.area_id = a.id
|
|
|
|
GROUP BY
|
|
|
|
a.id, a.name
|
|
|
|
) jsonsections
|
|
|
|
`,
|
|
|
|
)
|
|
|
|
|
|
|
|
var jsonData []byte
|
|
|
|
err = row.Scan(&jsonData)
|
|
|
|
if err != nil {
|
2024-06-01 09:18:56 +02:00
|
|
|
err = werr.Wrap(err)
|
2024-04-29 08:36:13 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-05-01 21:44:53 +02:00
|
|
|
if jsonData == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-04-29 08:36:13 +02:00
|
|
|
err = json.Unmarshal(jsonData, &areas)
|
|
|
|
if err != nil {
|
2024-06-01 09:18:56 +02:00
|
|
|
err = werr.Wrap(err)
|
2024-04-29 08:36:13 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
} // }}}
|
2024-04-30 20:10:05 +02:00
|
|
|
func TriggersRetrieveByDatapoint(datapointName string) (triggers []Trigger, err error) { // {{{
|
|
|
|
triggers = []Trigger{}
|
2024-04-30 21:23:05 +02:00
|
|
|
row := service.Db.Conn.QueryRow(`
|
|
|
|
SELECT jsonb_agg(t.*)
|
|
|
|
FROM public."trigger" t
|
2024-04-30 20:10:05 +02:00
|
|
|
WHERE
|
|
|
|
datapoints @> $1
|
|
|
|
`,
|
|
|
|
fmt.Sprintf(`["%s"]`, datapointName),
|
|
|
|
)
|
2024-04-30 21:23:05 +02:00
|
|
|
|
|
|
|
var data []byte
|
|
|
|
err = row.Scan(&data)
|
2024-04-30 20:10:05 +02:00
|
|
|
if err != nil {
|
2024-06-01 09:18:56 +02:00
|
|
|
err = werr.Wrap(err).WithData(datapointName)
|
2024-04-30 20:10:05 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-06-27 10:33:26 +02:00
|
|
|
// no triggers found for this datapoint.
|
|
|
|
if data == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-04-30 21:23:05 +02:00
|
|
|
err = json.Unmarshal(data, &triggers)
|
|
|
|
if err != nil {
|
2024-06-01 09:18:56 +02:00
|
|
|
err = werr.Wrap(err).WithData(datapointName)
|
2024-04-30 21:23:05 +02:00
|
|
|
return
|
2024-04-30 20:10:05 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
} // }}}
|
2024-04-29 08:36:13 +02:00
|
|
|
func TriggerRetrieve(id int) (trigger Trigger, err error) { // {{{
|
|
|
|
row := service.Db.Conn.QueryRow(`SELECT to_jsonb(t.*) FROM "trigger" t WHERE id=$1`, id)
|
|
|
|
var jsonData []byte
|
|
|
|
err = row.Scan(&jsonData)
|
|
|
|
if err != nil {
|
2024-06-01 09:18:56 +02:00
|
|
|
err = werr.Wrap(err)
|
2024-04-29 08:36:13 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
err = json.Unmarshal(jsonData, &trigger)
|
|
|
|
return
|
|
|
|
} // }}}
|
2024-04-30 20:10:05 +02:00
|
|
|
func (t *Trigger) Validate() (ok bool, err error) { // {{{
|
2024-04-29 08:36:13 +02:00
|
|
|
if strings.TrimSpace(t.Name) == "" {
|
|
|
|
err = fmt.Errorf("Name can't be empty")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if strings.TrimSpace(t.Expression) == "" {
|
|
|
|
err = fmt.Errorf("Expression can't be empty")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
return true, nil
|
2024-04-30 20:10:05 +02:00
|
|
|
} // }}}
|
|
|
|
func (t *Trigger) Update() (err error) { // {{{
|
2024-04-29 08:36:13 +02:00
|
|
|
var ok bool
|
|
|
|
if ok, err = t.Validate(); !ok {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-05-01 10:02:33 +02:00
|
|
|
if t.Datapoints == nil {
|
|
|
|
t.Datapoints = []string{}
|
|
|
|
}
|
|
|
|
jsonDatapoints, _ := json.Marshal(t.Datapoints)
|
|
|
|
if t.ID == 0 {
|
2024-05-30 15:06:41 +02:00
|
|
|
var row *sql.Row
|
|
|
|
row = service.Db.Conn.QueryRow(`
|
2024-05-01 10:02:33 +02:00
|
|
|
INSERT INTO "trigger"(name, section_id, expression, datapoints)
|
|
|
|
VALUES($1, $2, $3, $4)
|
2024-05-30 15:06:41 +02:00
|
|
|
RETURNING id
|
2024-05-01 10:02:33 +02:00
|
|
|
`,
|
|
|
|
t.Name,
|
|
|
|
t.SectionID,
|
|
|
|
t.Expression,
|
|
|
|
jsonDatapoints,
|
|
|
|
)
|
2024-05-30 15:06:41 +02:00
|
|
|
err = row.Scan(&t.ID)
|
|
|
|
if err != nil {
|
2024-06-01 09:18:56 +02:00
|
|
|
err = werr.Wrap(err).WithData(
|
2024-05-30 15:06:41 +02:00
|
|
|
struct {
|
|
|
|
SectionID int
|
|
|
|
Name string
|
|
|
|
Expression string
|
|
|
|
JsonDataPoints []byte
|
|
|
|
}{
|
|
|
|
t.SectionID,
|
|
|
|
t.Name,
|
|
|
|
t.Expression,
|
|
|
|
jsonDatapoints,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
return
|
|
|
|
}
|
2024-05-01 10:02:33 +02:00
|
|
|
} else {
|
|
|
|
_, err = service.Db.Conn.Exec(`
|
|
|
|
UPDATE "trigger"
|
|
|
|
SET
|
|
|
|
name=$2,
|
|
|
|
expression=$3,
|
|
|
|
datapoints=$4
|
|
|
|
WHERE
|
|
|
|
id=$1
|
|
|
|
`,
|
|
|
|
t.ID,
|
|
|
|
t.Name,
|
|
|
|
t.Expression,
|
|
|
|
jsonDatapoints,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
if pqErr, ok := err.(*pq.Error); ok {
|
2024-06-01 09:18:56 +02:00
|
|
|
err = werr.Wrap(err).WithData(
|
2024-05-01 10:02:33 +02:00
|
|
|
struct {
|
|
|
|
Trigger *Trigger
|
|
|
|
PostgresCode pq.ErrorCode
|
|
|
|
PostgresMsg string
|
|
|
|
}{
|
|
|
|
t,
|
|
|
|
pqErr.Code,
|
|
|
|
pqErr.Code.Name(),
|
|
|
|
})
|
|
|
|
} else if err != nil {
|
2024-06-01 09:18:56 +02:00
|
|
|
err = werr.Wrap(err).WithData(t)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
} // }}}
|
|
|
|
func TriggerDelete(id int) (err error) { // {{{
|
|
|
|
_, err = service.Db.Conn.Exec(`DELETE FROM public.trigger WHERE id=$1`, id)
|
|
|
|
if err != nil {
|
|
|
|
return werr.Wrap(err).WithData(id)
|
2024-04-29 08:36:13 +02:00
|
|
|
}
|
|
|
|
return
|
2024-04-30 20:10:05 +02:00
|
|
|
} // }}}
|
|
|
|
|
|
|
|
func (t *Trigger) Run() (output any, err error) { // {{{
|
|
|
|
datapoints := make(map[string]Datapoint)
|
|
|
|
for _, dpname := range t.Datapoints {
|
|
|
|
var dp Datapoint
|
|
|
|
dp, err = DatapointRetrieve(0, dpname)
|
|
|
|
if err != nil {
|
2024-06-01 09:18:56 +02:00
|
|
|
err = werr.Wrap(err)
|
2024-04-30 20:10:05 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
datapoints[dpname] = dp
|
|
|
|
}
|
2024-04-29 08:36:13 +02:00
|
|
|
|
2024-06-30 22:10:01 +02:00
|
|
|
t.DatapointValues = make(map[string]any)
|
2024-04-29 08:36:13 +02:00
|
|
|
for dpName, dp := range datapoints {
|
2024-06-30 22:10:01 +02:00
|
|
|
t.DatapointValues[dpName] = dp.LastDatapointValue.Value()
|
2024-04-29 08:36:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
program, err := expr.Compile(t.Expression)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-06-30 22:10:01 +02:00
|
|
|
output, err = expr.Run(program, t.DatapointValues)
|
2024-04-29 08:36:13 +02:00
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
return
|
2024-04-30 20:10:05 +02:00
|
|
|
} // }}}
|