Skip to content

Commit

Permalink
Variables in flows (#25)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
merlinfuchs authored Oct 3, 2024
1 parent 9101d8a commit 780c855
Show file tree
Hide file tree
Showing 34 changed files with 521 additions and 466 deletions.
1 change: 1 addition & 0 deletions kite-service/cmd/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ func serverStartCMD(c *cli.Context) error {
pg,
pg,
pg,
pg,
&http.Client{}, // TODO: think about proxying http requests
)
engine.Run(ctx)
Expand Down
10 changes: 4 additions & 6 deletions kite-service/internal/api/handler/variable/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,7 @@ func (h *VariableHandler) HandleVariableCreate(c *handler.Context, req wire.Vari
variable, err := h.variableStore.CreateVariable(c.Context(), &model.Variable{
ID: util.UniqueID(),
Name: req.Name,
Type: req.Type,
Scope: model.VariableScope(req.Scope),
Scoped: req.Scoped,
AppID: c.App.ID,
CreatedAt: time.Now().UTC(),
UpdatedAt: time.Now().UTC(),
Expand All @@ -73,8 +72,8 @@ func (h *VariableHandler) HandleVariableCreate(c *handler.Context, req wire.Vari
}

func (h *VariableHandler) HandleVariableUpdate(c *handler.Context, req wire.VariableUpdateRequest) (*wire.VariableUpdateResponse, error) {
if req.Scope != string(c.Variable.Scope) || req.Type != c.Variable.Type {
// If the scope or type changes, we have to delete all variable values
if req.Scoped != c.Variable.Scoped {
// If the scoped flag changes, delete all variable values.
err := h.variableValueStore.DeleteAllVariableValues(c.Context(), c.Variable.ID)
if err != nil {
return nil, fmt.Errorf("failed to delete variable values: %w", err)
Expand All @@ -84,8 +83,7 @@ func (h *VariableHandler) HandleVariableUpdate(c *handler.Context, req wire.Vari
variable, err := h.variableStore.UpdateVariable(c.Context(), &model.Variable{
ID: c.Variable.ID,
Name: req.Name,
Type: req.Type,
Scope: model.VariableScope(req.Scope),
Scoped: req.Scoped,
AppID: c.App.ID,
UpdatedAt: time.Now().UTC(),
})
Expand Down
22 changes: 6 additions & 16 deletions kite-service/internal/api/wire/variable.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,12 @@ import (
"gopkg.in/guregu/null.v4"
)

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

type Variable struct {
ID string `json:"id"`
Scope string `json:"scope"`
Name string `json:"name"`
Type string `json:"type"`
Scoped bool `json:"scoped"`
AppID string `json:"app_id"`
ModuleID null.String `json:"module_id"`
TotalValues null.Int `json:"total_values"`
Expand All @@ -30,32 +27,26 @@ type VariableGetResponse = Variable
type VariableListResponse = []*Variable

type VariableCreateRequest struct {
Scope string `json:"scope"`
Name string `json:"name"`
Type string `json:"type"`
Name string `json:"name"`
Scoped bool `json:"scoped"`
}

func (req VariableCreateRequest) Validate() error {
return validation.ValidateStruct(&req,
validation.Field(&req.Scope, validation.Required, validation.In(variableScopes...)),
validation.Field(&req.Name, validation.Required, validation.Length(1, 100), validation.Match(variableNameRegex)),
validation.Field(&req.Type, validation.Required, validation.In(variableTypes...)),
)
}

type VariableCreateResponse = Variable

type VariableUpdateRequest struct {
Scope string `json:"scope"`
Name string `json:"name"`
Type string `json:"type"`
Name string `json:"name"`
Scoped bool `json:"scoped"`
}

func (req VariableUpdateRequest) Validate() error {
return validation.ValidateStruct(&req,
validation.Field(&req.Scope, validation.Required, validation.In(variableScopes...)),
validation.Field(&req.Name, validation.Required, validation.Length(1, 100), validation.Match(variableNameRegex)),
validation.Field(&req.Type, validation.Required, validation.In(variableTypes...)),
)
}

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

return &Variable{
ID: variable.ID,
Scope: string(variable.Scope),
Name: variable.Name,
Type: variable.Type,
Scoped: variable.Scoped,
AppID: variable.AppID,
ModuleID: variable.ModuleID,
CreatedAt: variable.CreatedAt,
Expand Down
5 changes: 5 additions & 0 deletions kite-service/internal/core/engine/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ type App struct {
messageStore store.MessageStore
messageInstanceStore store.MessageInstanceStore
commandStore store.CommandStore
variableValueStore store.VariableValueStore
httpClient *http.Client

hasUndeployedChanges bool
Expand All @@ -43,6 +44,7 @@ func NewApp(
messageStore store.MessageStore,
messageInstanceStore store.MessageInstanceStore,
commandStore store.CommandStore,
variableValueStore store.VariableValueStore,
httpClient *http.Client,
) *App {
return &App{
Expand All @@ -53,6 +55,7 @@ func NewApp(
messageStore: messageStore,
messageInstanceStore: messageInstanceStore,
commandStore: commandStore,
variableValueStore: variableValueStore,
httpClient: httpClient,
commands: make(map[string]*Command),
events: make(map[string]interface{}),
Expand All @@ -70,6 +73,7 @@ func (a *App) AddCommand(cmd *model.Command) {
a.logStore,
a.messageStore,
a.messageInstanceStore,
a.variableValueStore,
a.httpClient,
)
if err != nil {
Expand Down Expand Up @@ -148,6 +152,7 @@ func (a *App) HandleEvent(appID string, session *state.State, event gateway.Even
a.logStore,
a.messageStore,
a.messageInstanceStore,
a.variableValueStore,
a.httpClient,
)
if err != nil {
Expand Down
5 changes: 4 additions & 1 deletion kite-service/internal/core/engine/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ type Command struct {
logStore store.LogStore
messageStore store.MessageStore
messageInstanceStore store.MessageInstanceStore
variableValueStore store.VariableValueStore
httpClient *http.Client
}

Expand All @@ -37,6 +38,7 @@ func NewCommand(
logStore store.LogStore,
messageStore store.MessageStore,
messageInstanceStore store.MessageInstanceStore,
variableValueStore store.VariableValueStore,
httpClient *http.Client,
) (*Command, error) {
flow, err := flow.CompileCommand(cmd.FlowSource)
Expand All @@ -52,6 +54,7 @@ func NewCommand(
logStore: logStore,
messageStore: messageStore,
messageInstanceStore: messageInstanceStore,
variableValueStore: variableValueStore,
httpClient: httpClient,
}, nil
}
Expand All @@ -69,7 +72,7 @@ func (c *Command) HandleEvent(appID string, session *state.State, event gateway.
Log: NewLogProvider(appID, c.logStore),
HTTP: NewHTTPProvider(c.httpClient),
MessageTemplate: NewMessageTemplateProvider(c.messageStore, c.messageInstanceStore),
// TODO: Variable provider
Variable: NewVariableProvider(c.variableValueStore),
}

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
Expand Down
4 changes: 4 additions & 0 deletions kite-service/internal/core/engine/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type Engine struct {
messageStore store.MessageStore
messageInstanceStore store.MessageInstanceStore
commandStore store.CommandStore
variableValueStore store.VariableValueStore
httpClient *http.Client

lastUpdate time.Time
Expand All @@ -35,6 +36,7 @@ func NewEngine(
messageStore store.MessageStore,
messageInstanceStore store.MessageInstanceStore,
commandStore store.CommandStore,
variableValueStore store.VariableValueStore,
httpClient *http.Client,
) *Engine {
return &Engine{
Expand All @@ -45,6 +47,7 @@ func NewEngine(
messageInstanceStore: messageInstanceStore,
httpClient: httpClient,
commandStore: commandStore,
variableValueStore: variableValueStore,
apps: make(map[string]*App),
}
}
Expand Down Expand Up @@ -101,6 +104,7 @@ func (m *Engine) populateCommands(ctx context.Context) error {
m.messageStore,
m.messageInstanceStore,
m.commandStore,
m.variableValueStore,
m.httpClient,
)
m.apps[command.AppID] = app
Expand Down
26 changes: 15 additions & 11 deletions kite-service/internal/core/engine/flow_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package engine
import (
"context"
"encoding/json"
"errors"
"fmt"
"log/slog"
"net/http"
Expand All @@ -16,6 +17,7 @@ import (
"github.com/kitecloud/kite/kite-service/internal/store"
"github.com/kitecloud/kite/kite-service/pkg/flow"
"github.com/kitecloud/kite/kite-service/pkg/message"
"gopkg.in/guregu/null.v4"
)

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

type VariableProvider struct {
scope model.VariableValueScope
variableValueStore store.VariableValueStore
}

func NewVariableProvider(scope model.VariableValueScope, variableValueStore store.VariableValueStore) *VariableProvider {
func NewVariableProvider(variableValueStore store.VariableValueStore) *VariableProvider {
return &VariableProvider{
scope: scope,
variableValueStore: variableValueStore,
}
}

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

err = p.variableValueStore.SetVariableValue(ctx, model.VariableValue{
VariableID: id,
Scope: scope,
Value: rawValue,
CreatedAt: time.Now().UTC(),
UpdatedAt: time.Now().UTC(),
}, p.scope)
})
if err != nil {
return fmt.Errorf("failed to set variable value: %w", err)
}

return nil
}

func (p *VariableProvider) Variable(ctx context.Context, id string) (flow.FlowValue, error) {
row, err := p.variableValueStore.VariableValue(ctx, id, p.scope)
func (p *VariableProvider) Variable(ctx context.Context, id string, scope null.String) (flow.FlowValue, error) {
row, err := p.variableValueStore.VariableValue(ctx, id, scope)
if err != nil {
if errors.Is(err, store.ErrNotFound) {
return flow.FlowValueNull, flow.ErrNotFound
}
return flow.FlowValue{}, fmt.Errorf("failed to get variable value: %w", err)
}

var value flow.FlowValue
err = json.Unmarshal(row.Value, &value.Value)
err = json.Unmarshal(row.Value, &value)
if err != nil {
return flow.FlowValue{}, fmt.Errorf("failed to unmarshal variable value: %w", err)
}

return value, nil
}

func (p *VariableProvider) DeleteVariable(ctx context.Context, id string) error {
err := p.variableValueStore.DeleteVariableValue(ctx, id, p.scope)
func (p *VariableProvider) DeleteVariable(ctx context.Context, id string, scope null.String) error {
err := p.variableValueStore.DeleteVariableValue(ctx, id, scope)
if err != nil {
return fmt.Errorf("failed to delete variable value: %w", err)
}
Expand Down
5 changes: 4 additions & 1 deletion kite-service/internal/core/engine/message.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ type MessageInstance struct {
logStore store.LogStore
messageStore store.MessageStore
messageInstanceStore store.MessageInstanceStore
variableValueStore store.VariableValueStore
httpClient *http.Client
}

Expand All @@ -34,6 +35,7 @@ func NewMessageInstance(
logStore store.LogStore,
messageStore store.MessageStore,
messageInstanceStore store.MessageInstanceStore,
variableValueStore store.VariableValueStore,
httpClient *http.Client,
) (*MessageInstance, error) {
flows := make(map[string]*flow.CompiledFlowNode, len(msg.FlowSources))
Expand All @@ -56,6 +58,7 @@ func NewMessageInstance(
logStore: logStore,
messageStore: messageStore,
messageInstanceStore: messageInstanceStore,
variableValueStore: variableValueStore,
httpClient: httpClient,
}, nil
}
Expand Down Expand Up @@ -83,7 +86,7 @@ func (c *MessageInstance) HandleEvent(appID string, session *state.State, event
Log: NewLogProvider(appID, c.logStore),
HTTP: NewHTTPProvider(c.httpClient),
MessageTemplate: NewMessageTemplateProvider(c.messageStore, c.messageInstanceStore),
// TODO: Variable provider
Variable: NewVariableProvider(c.variableValueStore),
}

ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
CREATE TABLE IF NOT EXISTS variables (
id TEXT PRIMARY KEY,
scope TEXT NOT NULL, -- global, guild, user, member, channel, custom
name TEXT NOT NULL,
type TEXT NOT NULL, -- string, number, boolean, array, object, ...
scoped BOOLEAN NOT NULL DEFAULT FALSE,

app_id TEXT NOT NULL REFERENCES apps(id) ON DELETE CASCADE,
module_id TEXT REFERENCES modules(id) ON DELETE SET NULL,
Expand Down
3 changes: 1 addition & 2 deletions kite-service/internal/db/postgres/pgmodel/models.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 780c855

Please sign in to comment.