Properly add ids for workflow execution and add first visualization
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
package workflow_executions
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
. "git.gorlug.de/code/ersteller"
|
||||
"git.gorlug.de/code/ersteller/schema/ent"
|
||||
"git.gorlug.de/code/ersteller/schema/ent/generalqueue"
|
||||
"git.gorlug.de/code/ersteller/workflow"
|
||||
|
||||
. "maragu.dev/gomponents"
|
||||
. "maragu.dev/gomponents/html"
|
||||
)
|
||||
|
||||
const WorkflowExecutionsPath = "/workflow-executions"
|
||||
const WorkflowExecutionsPathDe = "/workflow-ausfuehrungen"
|
||||
|
||||
var texts *Texts
|
||||
|
||||
type Texts struct {
|
||||
PageTitle I18nText
|
||||
PageDescription I18nText
|
||||
HeroTitle I18nText
|
||||
TriggerPlaceholder I18nText
|
||||
TriggerButton I18nText
|
||||
}
|
||||
|
||||
type Page struct {
|
||||
createPage CreateHtmxPageFunc
|
||||
db *ent.Client
|
||||
wf *workflow.Workflow
|
||||
|
||||
ViewRoute HtmxRoute
|
||||
TriggerRoute HtmxRoute
|
||||
}
|
||||
|
||||
func NewPage(createPage CreateHtmxPageFunc, server HtmxServer, path *ActivePath, db *ent.Client, wf *workflow.Workflow) *Page {
|
||||
if texts == nil {
|
||||
createTexts()
|
||||
}
|
||||
p := &Page{createPage: createPage, db: db, wf: wf}
|
||||
p.ViewRoute = NewHtmxGetRoute(p.View, LanguagePaths{En: WorkflowExecutionsPath, De: WorkflowExecutionsPathDe}).SetActivePath(path)
|
||||
p.ViewRoute.Add(server)
|
||||
|
||||
p.TriggerRoute = NewHtmxPostRoute(p.Trigger, LanguagePaths{En: WorkflowExecutionsPath + "/trigger", De: WorkflowExecutionsPathDe + "/trigger"})
|
||||
p.TriggerRoute.Add(server)
|
||||
return p
|
||||
}
|
||||
|
||||
func createTexts() {
|
||||
texts = &Texts{
|
||||
PageTitle: NewI18nText(map[Language]string{En: "Workflow Executions", De: "Workflow-Ausführungen"}),
|
||||
PageDescription: NewI18nText(map[Language]string{En: "Monitor workflow executions", De: "Überwache Workflow-Ausführungen"}),
|
||||
HeroTitle: NewI18nText(map[Language]string{En: "Workflow Executions", De: "Workflow-Ausführungen"}),
|
||||
TriggerPlaceholder: NewI18nText(map[Language]string{En: "Input for workflow", De: "Input für Workflow"}),
|
||||
TriggerButton: NewI18nText(map[Language]string{En: "Trigger Workflow", De: "Workflow starten"}),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Page) getMetaData() PageWebsiteMetaData {
|
||||
return PageWebsiteMetaData{
|
||||
Title: texts.PageTitle,
|
||||
Lang: En,
|
||||
Description: texts.PageDescription,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Page) View(c HtmxContext) {
|
||||
language := c.GetLanguage()
|
||||
|
||||
// Query all jobs ordered by created_at desc
|
||||
jobs, _ := p.db.GeneralQueue.Query().
|
||||
Order(ent.Desc(generalqueue.FieldCreatedAt)).
|
||||
All(context.Background())
|
||||
|
||||
// Group jobs by workflow_id
|
||||
executions := make(map[string][]*ent.GeneralQueue)
|
||||
var workflowIds []string
|
||||
for _, j := range jobs {
|
||||
if j.WorkflowID == "" {
|
||||
continue
|
||||
}
|
||||
if _, ok := executions[j.WorkflowID]; !ok {
|
||||
workflowIds = append(workflowIds, j.WorkflowID)
|
||||
}
|
||||
executions[j.WorkflowID] = append(executions[j.WorkflowID], j)
|
||||
}
|
||||
|
||||
content := Group{
|
||||
Div(Class("hero-section"),
|
||||
H1(Class("hero-title"), Text(texts.HeroTitle.FromLang(language))),
|
||||
),
|
||||
Div(Class("content-section"),
|
||||
p.triggerForm(c),
|
||||
Div(ID("executions-list"), p.executionsList(c, workflowIds, executions)),
|
||||
),
|
||||
}
|
||||
p.createPage(c, p.getMetaData(), content)
|
||||
}
|
||||
|
||||
func (p *Page) triggerForm(c HtmxContext) Node {
|
||||
lang := c.GetLanguage()
|
||||
return Form(Action(p.TriggerRoute.ToUrlFromContext(c, lang)), Method("post"),
|
||||
Attr("hx-post", p.TriggerRoute.ToUrlFromContext(c, lang)),
|
||||
Attr("hx-target", "#executions-list"),
|
||||
Attr("hx-swap", "outerHTML"),
|
||||
Div(Class("form-row"),
|
||||
Input(Type("text"), Name("input"), Placeholder(texts.TriggerPlaceholder.FromLang(lang))),
|
||||
Button(Type("submit"), Text(texts.TriggerButton.FromLang(lang))),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
func (p *Page) executionsList(c HtmxContext, workflowIds []string, executions map[string][]*ent.GeneralQueue) Node {
|
||||
items := make([]Node, 0, len(workflowIds))
|
||||
for _, id := range workflowIds {
|
||||
items = append(items, p.executionItem(id, executions[id]))
|
||||
}
|
||||
return Div(Class("executions-container"), Group(items))
|
||||
}
|
||||
|
||||
func (p *Page) executionItem(workflowId string, jobs []*ent.GeneralQueue) Node {
|
||||
// Sort jobs by created_at within execution
|
||||
sort.Slice(jobs, func(i, j int) bool {
|
||||
return jobs[i].CreatedAt.Before(jobs[j].CreatedAt)
|
||||
})
|
||||
|
||||
jobNodes := make([]Node, 0, len(jobs))
|
||||
for _, j := range jobs {
|
||||
jobNodes = append(jobNodes, Div(Class("job-step"),
|
||||
H4(Text(fmt.Sprintf("Step: %s", j.Name))),
|
||||
P(Text(fmt.Sprintf("Status: %s", j.Status))),
|
||||
P(Text(fmt.Sprintf("Tries: %d/%d", j.NumberOfTries, j.MaxRetries))),
|
||||
If(j.ErrorMessage != "", P(Class("error-message"), Text(fmt.Sprintf("Error: %s", j.ErrorMessage)))),
|
||||
If(len(j.Payload) > 0, Details(Summary(Text("Payload")), Pre(Class("payload-pre"), Text(fmt.Sprintf("%+v", j.Payload))))),
|
||||
If(len(j.ResultPayload) > 0, Details(Summary(Text("Result")), Pre(Class("result-pre"), Text(fmt.Sprintf("%+v", j.ResultPayload))))),
|
||||
))
|
||||
}
|
||||
|
||||
return Div(Class("execution-card"),
|
||||
H3(Text(fmt.Sprintf("Execution: %s", workflowId))),
|
||||
Div(Class("steps-list"), Group(jobNodes)),
|
||||
Hr(),
|
||||
)
|
||||
}
|
||||
|
||||
func (p *Page) Trigger(c HtmxContext) {
|
||||
input := strings.TrimSpace(c.GetFormValue("input"))
|
||||
|
||||
payload := map[string]interface{}{
|
||||
"workflowId": p.wf.GenerateId(),
|
||||
"input": input,
|
||||
}
|
||||
|
||||
err := p.wf.Execute(context.Background(), payload)
|
||||
if err != nil {
|
||||
// handle error?
|
||||
}
|
||||
|
||||
p.renderList(c)
|
||||
}
|
||||
|
||||
func (p *Page) renderList(c HtmxContext) {
|
||||
jobs, _ := p.db.GeneralQueue.Query().
|
||||
Order(ent.Desc(generalqueue.FieldCreatedAt)).
|
||||
All(context.Background())
|
||||
|
||||
executions := make(map[string][]*ent.GeneralQueue)
|
||||
var workflowIds []string
|
||||
for _, j := range jobs {
|
||||
if j.WorkflowID == "" {
|
||||
continue
|
||||
}
|
||||
if _, ok := executions[j.WorkflowID]; !ok {
|
||||
workflowIds = append(workflowIds, j.WorkflowID)
|
||||
}
|
||||
executions[j.WorkflowID] = append(executions[j.WorkflowID], j)
|
||||
}
|
||||
|
||||
c.Render(Div(ID("executions-list"), p.executionsList(c, workflowIds, executions)))
|
||||
}
|
||||
Reference in New Issue
Block a user