Skip to content

Commit

Permalink
adapters: Add WebUI adapter (#61)
Browse files Browse the repository at this point in the history
* adapters: Add WebUI adapter

* update sql store to handle no workflow specified

* allow path specification

* move js out to seperate file

* use sql paramatised value
  • Loading branch information
andrewwormald authored Oct 30, 2024
1 parent c1bbed5 commit cc11a65
Show file tree
Hide file tree
Showing 20 changed files with 1,670 additions and 51 deletions.
31 changes: 31 additions & 0 deletions _examples/webui/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
module github.com/luno/workflow/_examples/webui

go 1.23.2

replace github.com/luno/workflow => ../..

replace github.com/luno/workflow/adapters/webui => ../../adapters/webui

require (
github.com/luno/workflow v0.0.0-20241017150231-e09bd48815f5
github.com/luno/workflow/adapters/webui v0.0.0-00010101000000-000000000000
)

require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.20.4 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.55.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/stretchr/testify v1.9.0 // indirect
golang.org/x/sys v0.22.0 // indirect
google.golang.org/protobuf v1.35.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/utils v0.0.0-20240921022957-49e7df575cb6 // indirect
)
47 changes: 47 additions & 0 deletions _examples/webui/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI=
github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
k8s.io/utils v0.0.0-20240921022957-49e7df575cb6 h1:MDF6h2H/h4tbzmtIKTuctcwZmY0tY9mD9fNT47QO6HI=
k8s.io/utils v0.0.0-20240921022957-49e7df575cb6/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
167 changes: 167 additions & 0 deletions _examples/webui/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
package main

import (
"context"
"fmt"
"net/http"
"os"
"strconv"

"github.com/luno/workflow"
"github.com/luno/workflow/adapters/memrecordstore"
"github.com/luno/workflow/adapters/memrolescheduler"
"github.com/luno/workflow/adapters/memstreamer"
"github.com/luno/workflow/adapters/webui"
)

func main() {
recordStore := memrecordstore.New()
w := Example(recordStore)
ctx := context.Background()
w.Run(ctx)
defer w.Stop()

seed := []string{
"Customer 1",
"Customer 2",
"Customer 3",
}
for _, foreignID := range seed {
_, err := w.Trigger(ctx, foreignID, StatusStart)
if err != nil {
panic(err)
}
}

paths := webui.Paths{
List: "/api/v1/list",
Update: "/api/v1/record/update",
ObjectData: "/api/v1/record/objectdata",
}
http.HandleFunc("/", webui.HomeHandlerFunc(paths))
http.HandleFunc(paths.List, webui.ListHandlerFunc(
recordStore,
func(workflowName string, enumValue int) string {
if workflowName == ExampleWorkflowName {
return Status(enumValue).String()
}

return strconv.Itoa(enumValue)
},
))
http.HandleFunc(paths.ObjectData, webui.ObjectDataHandlerFunc(recordStore))
http.HandleFunc(paths.Update, webui.UpdateHandlerFunc(recordStore))

err := http.ListenAndServe("localhost:9492", nil)
if err != nil {
panic(err)
}

os.Exit(0)
}

type ExampleData struct {
Name string
Age int
City string
Languages []string
Details Details
}

type Details struct {
Hobby string
Profession string
Experience Experience
}

type Experience struct {
Years int
Projects []string
}

type Status int

const (
StatusUnknown Status = 0
StatusStart Status = 1
StatusMiddle Status = 2
StatusEnd Status = 3
)

func (s Status) String() string {
switch s {
case StatusStart:
return "Start"
case StatusMiddle:
return "Middle"
case StatusEnd:
return "End"
default:
return "Unknown"
}
}

const ExampleWorkflowName = "example"

func Example(rs workflow.RecordStore) *workflow.Workflow[ExampleData, Status] {
b := workflow.NewBuilder[ExampleData, Status](ExampleWorkflowName)
b.AddStep(
StatusStart,
func(ctx context.Context, r *workflow.Run[ExampleData, Status]) (Status, error) {
return StatusMiddle, nil
},
StatusMiddle,
)
b.AddStep(
StatusMiddle,
func(ctx context.Context, r *workflow.Run[ExampleData, Status]) (Status, error) {
if r.ForeignID == "Customer 2" {
return r.Pause(ctx)
}

if r.ForeignID == "Customer 1" {
return r.Cancel(ctx)
}

*r.Object = ExampleData{
Name: "Andrew",
City: "New York",
Languages: []string{"Go", "C++", "Typescript"},
Details: Details{
Hobby: "Coding",
Profession: "Software Crafting",
Experience: Experience{
Years: 1_000_000,
Projects: []string{
"Workflow",
},
},
},
}

return StatusEnd, nil
},
StatusEnd,
)

b.OnComplete(func(ctx context.Context, record *workflow.TypedRecord[ExampleData, Status]) error {
fmt.Println("Completed: ", record.ForeignID)
return nil
})

b.OnCancel(func(ctx context.Context, record *workflow.TypedRecord[ExampleData, Status]) error {
fmt.Println("Cancelled: ", record.ForeignID)
return nil
})

b.OnPause(func(ctx context.Context, record *workflow.TypedRecord[ExampleData, Status]) error {
fmt.Println("Paused: ", record.ForeignID)
return nil
})

return b.Build(
memstreamer.New(),
rs,
memrolescheduler.New(),
)
}
Loading

0 comments on commit cc11a65

Please sign in to comment.