Skip to content

Commit 780c855

Browse files
authored
Variables in flows (#25)
* add frontend for variables in flows * add POC backend for variables in flows * simplify variable system * implement basic variable operations * minor * set return value on set variable node
1 parent 9101d8a commit 780c855

34 files changed

+521
-466
lines changed

kite-service/cmd/server.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ func serverStartCMD(c *cli.Context) error {
6868
pg,
6969
pg,
7070
pg,
71+
pg,
7172
&http.Client{}, // TODO: think about proxying http requests
7273
)
7374
engine.Run(ctx)

kite-service/internal/api/handler/variable/handler.go

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,7 @@ func (h *VariableHandler) HandleVariableCreate(c *handler.Context, req wire.Vari
5959
variable, err := h.variableStore.CreateVariable(c.Context(), &model.Variable{
6060
ID: util.UniqueID(),
6161
Name: req.Name,
62-
Type: req.Type,
63-
Scope: model.VariableScope(req.Scope),
62+
Scoped: req.Scoped,
6463
AppID: c.App.ID,
6564
CreatedAt: time.Now().UTC(),
6665
UpdatedAt: time.Now().UTC(),
@@ -73,8 +72,8 @@ func (h *VariableHandler) HandleVariableCreate(c *handler.Context, req wire.Vari
7372
}
7473

7574
func (h *VariableHandler) HandleVariableUpdate(c *handler.Context, req wire.VariableUpdateRequest) (*wire.VariableUpdateResponse, error) {
76-
if req.Scope != string(c.Variable.Scope) || req.Type != c.Variable.Type {
77-
// If the scope or type changes, we have to delete all variable values
75+
if req.Scoped != c.Variable.Scoped {
76+
// If the scoped flag changes, delete all variable values.
7877
err := h.variableValueStore.DeleteAllVariableValues(c.Context(), c.Variable.ID)
7978
if err != nil {
8079
return nil, fmt.Errorf("failed to delete variable values: %w", err)
@@ -84,8 +83,7 @@ func (h *VariableHandler) HandleVariableUpdate(c *handler.Context, req wire.Vari
8483
variable, err := h.variableStore.UpdateVariable(c.Context(), &model.Variable{
8584
ID: c.Variable.ID,
8685
Name: req.Name,
87-
Type: req.Type,
88-
Scope: model.VariableScope(req.Scope),
86+
Scoped: req.Scoped,
8987
AppID: c.App.ID,
9088
UpdatedAt: time.Now().UTC(),
9189
})

kite-service/internal/api/wire/variable.go

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,12 @@ import (
99
"gopkg.in/guregu/null.v4"
1010
)
1111

12-
var variableScopes = []interface{}{"global", "guild", "channel", "user", "member"}
13-
var variableTypes = []interface{}{"string", "integer", "float", "boolean"}
1412
var variableNameRegex = regexp.MustCompile(`^[a-zA-Z0-9_]+$`)
1513

1614
type Variable struct {
1715
ID string `json:"id"`
18-
Scope string `json:"scope"`
1916
Name string `json:"name"`
20-
Type string `json:"type"`
17+
Scoped bool `json:"scoped"`
2118
AppID string `json:"app_id"`
2219
ModuleID null.String `json:"module_id"`
2320
TotalValues null.Int `json:"total_values"`
@@ -30,32 +27,26 @@ type VariableGetResponse = Variable
3027
type VariableListResponse = []*Variable
3128

3229
type VariableCreateRequest struct {
33-
Scope string `json:"scope"`
34-
Name string `json:"name"`
35-
Type string `json:"type"`
30+
Name string `json:"name"`
31+
Scoped bool `json:"scoped"`
3632
}
3733

3834
func (req VariableCreateRequest) Validate() error {
3935
return validation.ValidateStruct(&req,
40-
validation.Field(&req.Scope, validation.Required, validation.In(variableScopes...)),
4136
validation.Field(&req.Name, validation.Required, validation.Length(1, 100), validation.Match(variableNameRegex)),
42-
validation.Field(&req.Type, validation.Required, validation.In(variableTypes...)),
4337
)
4438
}
4539

4640
type VariableCreateResponse = Variable
4741

4842
type VariableUpdateRequest struct {
49-
Scope string `json:"scope"`
50-
Name string `json:"name"`
51-
Type string `json:"type"`
43+
Name string `json:"name"`
44+
Scoped bool `json:"scoped"`
5245
}
5346

5447
func (req VariableUpdateRequest) Validate() error {
5548
return validation.ValidateStruct(&req,
56-
validation.Field(&req.Scope, validation.Required, validation.In(variableScopes...)),
5749
validation.Field(&req.Name, validation.Required, validation.Length(1, 100), validation.Match(variableNameRegex)),
58-
validation.Field(&req.Type, validation.Required, validation.In(variableTypes...)),
5950
)
6051
}
6152

@@ -70,9 +61,8 @@ func VariableToWire(variable *model.Variable) *Variable {
7061

7162
return &Variable{
7263
ID: variable.ID,
73-
Scope: string(variable.Scope),
7464
Name: variable.Name,
75-
Type: variable.Type,
65+
Scoped: variable.Scoped,
7666
AppID: variable.AppID,
7767
ModuleID: variable.ModuleID,
7868
CreatedAt: variable.CreatedAt,

kite-service/internal/core/engine/app.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ type App struct {
2626
messageStore store.MessageStore
2727
messageInstanceStore store.MessageInstanceStore
2828
commandStore store.CommandStore
29+
variableValueStore store.VariableValueStore
2930
httpClient *http.Client
3031

3132
hasUndeployedChanges bool
@@ -43,6 +44,7 @@ func NewApp(
4344
messageStore store.MessageStore,
4445
messageInstanceStore store.MessageInstanceStore,
4546
commandStore store.CommandStore,
47+
variableValueStore store.VariableValueStore,
4648
httpClient *http.Client,
4749
) *App {
4850
return &App{
@@ -53,6 +55,7 @@ func NewApp(
5355
messageStore: messageStore,
5456
messageInstanceStore: messageInstanceStore,
5557
commandStore: commandStore,
58+
variableValueStore: variableValueStore,
5659
httpClient: httpClient,
5760
commands: make(map[string]*Command),
5861
events: make(map[string]interface{}),
@@ -70,6 +73,7 @@ func (a *App) AddCommand(cmd *model.Command) {
7073
a.logStore,
7174
a.messageStore,
7275
a.messageInstanceStore,
76+
a.variableValueStore,
7377
a.httpClient,
7478
)
7579
if err != nil {
@@ -148,6 +152,7 @@ func (a *App) HandleEvent(appID string, session *state.State, event gateway.Even
148152
a.logStore,
149153
a.messageStore,
150154
a.messageInstanceStore,
155+
a.variableValueStore,
151156
a.httpClient,
152157
)
153158
if err != nil {

kite-service/internal/core/engine/command.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ type Command struct {
2727
logStore store.LogStore
2828
messageStore store.MessageStore
2929
messageInstanceStore store.MessageInstanceStore
30+
variableValueStore store.VariableValueStore
3031
httpClient *http.Client
3132
}
3233

@@ -37,6 +38,7 @@ func NewCommand(
3738
logStore store.LogStore,
3839
messageStore store.MessageStore,
3940
messageInstanceStore store.MessageInstanceStore,
41+
variableValueStore store.VariableValueStore,
4042
httpClient *http.Client,
4143
) (*Command, error) {
4244
flow, err := flow.CompileCommand(cmd.FlowSource)
@@ -52,6 +54,7 @@ func NewCommand(
5254
logStore: logStore,
5355
messageStore: messageStore,
5456
messageInstanceStore: messageInstanceStore,
57+
variableValueStore: variableValueStore,
5558
httpClient: httpClient,
5659
}, nil
5760
}
@@ -69,7 +72,7 @@ func (c *Command) HandleEvent(appID string, session *state.State, event gateway.
6972
Log: NewLogProvider(appID, c.logStore),
7073
HTTP: NewHTTPProvider(c.httpClient),
7174
MessageTemplate: NewMessageTemplateProvider(c.messageStore, c.messageInstanceStore),
72-
// TODO: Variable provider
75+
Variable: NewVariableProvider(c.variableValueStore),
7376
}
7477

7578
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)

kite-service/internal/core/engine/engine.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ type Engine struct {
2222
messageStore store.MessageStore
2323
messageInstanceStore store.MessageInstanceStore
2424
commandStore store.CommandStore
25+
variableValueStore store.VariableValueStore
2526
httpClient *http.Client
2627

2728
lastUpdate time.Time
@@ -35,6 +36,7 @@ func NewEngine(
3536
messageStore store.MessageStore,
3637
messageInstanceStore store.MessageInstanceStore,
3738
commandStore store.CommandStore,
39+
variableValueStore store.VariableValueStore,
3840
httpClient *http.Client,
3941
) *Engine {
4042
return &Engine{
@@ -45,6 +47,7 @@ func NewEngine(
4547
messageInstanceStore: messageInstanceStore,
4648
httpClient: httpClient,
4749
commandStore: commandStore,
50+
variableValueStore: variableValueStore,
4851
apps: make(map[string]*App),
4952
}
5053
}
@@ -101,6 +104,7 @@ func (m *Engine) populateCommands(ctx context.Context) error {
101104
m.messageStore,
102105
m.messageInstanceStore,
103106
m.commandStore,
107+
m.variableValueStore,
104108
m.httpClient,
105109
)
106110
m.apps[command.AppID] = app

kite-service/internal/core/engine/flow_provider.go

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package engine
33
import (
44
"context"
55
"encoding/json"
6+
"errors"
67
"fmt"
78
"log/slog"
89
"net/http"
@@ -16,6 +17,7 @@ import (
1617
"github.com/kitecloud/kite/kite-service/internal/store"
1718
"github.com/kitecloud/kite/kite-service/pkg/flow"
1819
"github.com/kitecloud/kite/kite-service/pkg/message"
20+
"gopkg.in/guregu/null.v4"
1921
)
2022

2123
type DiscordProvider struct {
@@ -222,53 +224,55 @@ func (p *HTTPProvider) HTTPRequest(ctx context.Context, req *http.Request) (*htt
222224
}
223225

224226
type VariableProvider struct {
225-
scope model.VariableValueScope
226227
variableValueStore store.VariableValueStore
227228
}
228229

229-
func NewVariableProvider(scope model.VariableValueScope, variableValueStore store.VariableValueStore) *VariableProvider {
230+
func NewVariableProvider(variableValueStore store.VariableValueStore) *VariableProvider {
230231
return &VariableProvider{
231-
scope: scope,
232232
variableValueStore: variableValueStore,
233233
}
234234
}
235235

236-
func (p *VariableProvider) SetVariable(ctx context.Context, id string, value flow.FlowValue) error {
237-
rawValue, err := json.Marshal(value.Value)
236+
func (p *VariableProvider) SetVariable(ctx context.Context, id string, scope null.String, value flow.FlowValue) error {
237+
rawValue, err := json.Marshal(value)
238238
if err != nil {
239239
return fmt.Errorf("failed to marshal variable value: %w", err)
240240
}
241241

242242
err = p.variableValueStore.SetVariableValue(ctx, model.VariableValue{
243243
VariableID: id,
244+
Scope: scope,
244245
Value: rawValue,
245246
CreatedAt: time.Now().UTC(),
246247
UpdatedAt: time.Now().UTC(),
247-
}, p.scope)
248+
})
248249
if err != nil {
249250
return fmt.Errorf("failed to set variable value: %w", err)
250251
}
251252

252253
return nil
253254
}
254255

255-
func (p *VariableProvider) Variable(ctx context.Context, id string) (flow.FlowValue, error) {
256-
row, err := p.variableValueStore.VariableValue(ctx, id, p.scope)
256+
func (p *VariableProvider) Variable(ctx context.Context, id string, scope null.String) (flow.FlowValue, error) {
257+
row, err := p.variableValueStore.VariableValue(ctx, id, scope)
257258
if err != nil {
259+
if errors.Is(err, store.ErrNotFound) {
260+
return flow.FlowValueNull, flow.ErrNotFound
261+
}
258262
return flow.FlowValue{}, fmt.Errorf("failed to get variable value: %w", err)
259263
}
260264

261265
var value flow.FlowValue
262-
err = json.Unmarshal(row.Value, &value.Value)
266+
err = json.Unmarshal(row.Value, &value)
263267
if err != nil {
264268
return flow.FlowValue{}, fmt.Errorf("failed to unmarshal variable value: %w", err)
265269
}
266270

267271
return value, nil
268272
}
269273

270-
func (p *VariableProvider) DeleteVariable(ctx context.Context, id string) error {
271-
err := p.variableValueStore.DeleteVariableValue(ctx, id, p.scope)
274+
func (p *VariableProvider) DeleteVariable(ctx context.Context, id string, scope null.String) error {
275+
err := p.variableValueStore.DeleteVariableValue(ctx, id, scope)
272276
if err != nil {
273277
return fmt.Errorf("failed to delete variable value: %w", err)
274278
}

kite-service/internal/core/engine/message.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ type MessageInstance struct {
2424
logStore store.LogStore
2525
messageStore store.MessageStore
2626
messageInstanceStore store.MessageInstanceStore
27+
variableValueStore store.VariableValueStore
2728
httpClient *http.Client
2829
}
2930

@@ -34,6 +35,7 @@ func NewMessageInstance(
3435
logStore store.LogStore,
3536
messageStore store.MessageStore,
3637
messageInstanceStore store.MessageInstanceStore,
38+
variableValueStore store.VariableValueStore,
3739
httpClient *http.Client,
3840
) (*MessageInstance, error) {
3941
flows := make(map[string]*flow.CompiledFlowNode, len(msg.FlowSources))
@@ -56,6 +58,7 @@ func NewMessageInstance(
5658
logStore: logStore,
5759
messageStore: messageStore,
5860
messageInstanceStore: messageInstanceStore,
61+
variableValueStore: variableValueStore,
5962
httpClient: httpClient,
6063
}, nil
6164
}
@@ -83,7 +86,7 @@ func (c *MessageInstance) HandleEvent(appID string, session *state.State, event
8386
Log: NewLogProvider(appID, c.logStore),
8487
HTTP: NewHTTPProvider(c.httpClient),
8588
MessageTemplate: NewMessageTemplateProvider(c.messageStore, c.messageInstanceStore),
86-
// TODO: Variable provider
89+
Variable: NewVariableProvider(c.variableValueStore),
8790
}
8891

8992
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)

kite-service/internal/db/postgres/migrations/008_create_variables_table.up.sql

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
CREATE TABLE IF NOT EXISTS variables (
22
id TEXT PRIMARY KEY,
3-
scope TEXT NOT NULL, -- global, guild, user, member, channel, custom
43
name TEXT NOT NULL,
5-
type TEXT NOT NULL, -- string, number, boolean, array, object, ...
4+
scoped BOOLEAN NOT NULL DEFAULT FALSE,
65

76
app_id TEXT NOT NULL REFERENCES apps(id) ON DELETE CASCADE,
87
module_id TEXT REFERENCES modules(id) ON DELETE SET NULL,

kite-service/internal/db/postgres/pgmodel/models.go

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)