Skip to content

Commit

Permalink
Added store.insert plugin API
Browse files Browse the repository at this point in the history
  • Loading branch information
akclace committed Jan 18, 2024
1 parent 2e9f1ea commit 27f8d3d
Show file tree
Hide file tree
Showing 22 changed files with 618 additions and 218 deletions.
2 changes: 1 addition & 1 deletion internal/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,8 @@ func NewApp(sourceFS *util.SourceFs, workFS *util.WorkFs, logger *utils.Logger,
AppEntry: appEntry,
systemConfig: systemConfig,
starlarkCache: map[string]*starlarkCacheEntry{},
plugins: NewAppPlugins(plugins, appEntry.Metadata.Accounts),
}
newApp.plugins = NewAppPlugins(newApp, plugins, appEntry.Metadata.Accounts)

if appEntry.IsDev {
newApp.appDev = dev.NewAppDev(logger, &util.WritableSourceFs{SourceFs: sourceFS}, workFS, systemConfig)
Expand Down
14 changes: 11 additions & 3 deletions internal/app/app_plugins.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,21 @@ import (

type AppPlugins struct {
sync.Mutex
plugins map[string]any
plugins map[string]any

app *App
pluginConfig map[string]utils.PluginSettings // pluginName -> accountName -> PluginSettings
accountMap map[string]string // pluginName -> accountName
}

func NewAppPlugins(pluginConfig map[string]utils.PluginSettings, appAccounts []utils.AccountLink) *AppPlugins {
func NewAppPlugins(app *App, pluginConfig map[string]utils.PluginSettings, appAccounts []utils.AccountLink) *AppPlugins {
accountMap := make(map[string]string)
for _, entry := range appAccounts {
accountMap[entry.Plugin] = entry.AccountName
}

return &AppPlugins{
app: app,
plugins: make(map[string]any),
pluginConfig: pluginConfig,
accountMap: accountMap,
Expand Down Expand Up @@ -63,7 +66,12 @@ func (p *AppPlugins) GetPlugin(pluginInfo *PluginInfo, accountName string) (any,
appConfig = p.pluginConfig[pluginAccount]
}

pluginContext := &PluginContext{Config: appConfig}
pluginContext := &PluginContext{
Logger: p.app.Logger,
AppId: p.app.AppEntry.Id,
StoreInfo: p.app.storeInfo,
Config: appConfig,
}
plugin, err := pluginInfo.builder(pluginContext)
if err != nil {
return nil, fmt.Errorf("error creating plugin %s: %w", pluginInfo.funcName, err)
Expand Down
7 changes: 6 additions & 1 deletion internal/app/app_plugins_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@ func TestGetPlugin(t *testing.T) {
{Plugin: "plugin2.in", AccountName: "account2"},
{Plugin: "plugin2.in#account2", AccountName: "plugin2.in#account3"},
}
appPlugins := NewAppPlugins(pluginConfig, appAccounts)

app := &App{
Logger: utils.NewLogger(&utils.LogConfig{}),
AppEntry: &utils.AppEntry{Id: "testApp", Path: "/test", Domain: "", SourceUrl: ".", IsDev: false},
}
appPlugins := NewAppPlugins(app, pluginConfig, appAccounts)

// Define the pluginInfo and accountName for testing
pluginInfo := &PluginInfo{
Expand Down
5 changes: 5 additions & 0 deletions internal/app/audit.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@ func (a *App) Audit() (*utils.ApproveResult, error) {
Load: auditLoader,
}

err = a.loadSchemaInfo(a.sourceFS)
if err != nil {
return nil, err
}

builtin, err := a.createBuiltin()
if err != nil {
return nil, err
Expand Down
5 changes: 4 additions & 1 deletion internal/app/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ import (
)

type PluginContext struct {
Config utils.PluginSettings
Logger *utils.Logger
AppId utils.AppId
StoreInfo *utils.StoreInfo
Config utils.PluginSettings
}

type NewPluginFunc func(pluginContext *PluginContext) (any, error)
Expand Down
16 changes: 15 additions & 1 deletion internal/app/setup.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ func (a *App) addSchemaTypes(builtin starlark.StringDict) (starlark.StringDict,
newBuiltins[k] = v
}

// Add type module for referencing type names
typeDict := starlark.StringDict{}
for _, t := range a.storeInfo.Types {
tb := utils.TypeBuilder{Name: t.Name, Fields: t.Fields}
Expand All @@ -146,8 +147,21 @@ func (a *App) addSchemaTypes(builtin starlark.StringDict) (starlark.StringDict,
Name: util.TYPE_MODULE,
Members: typeDict,
}

newBuiltins[util.TYPE_MODULE] = &typeModule

// Add table module for referencing table names
tableDict := starlark.StringDict{}
for _, t := range a.storeInfo.Types {
tableDict[t.Name] = starlark.String(t.Name)
}

tableModule := starlarkstruct.Module{
Name: util.TABLE_MODULE,
Members: tableDict,
}
newBuiltins[util.TABLE_MODULE] = &tableModule
a.Trace().Msgf("********Added %d types to builtins", len(a.storeInfo.Types))

return newBuiltins, nil
}

Expand Down
31 changes: 0 additions & 31 deletions internal/app/store/db_plugin.go

This file was deleted.

209 changes: 209 additions & 0 deletions internal/app/store/sql_store.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,209 @@
// Copyright (c) ClaceIO, LLC
// SPDX-License-Identifier: Apache-2.0

package store

import (
"database/sql"
"encoding/json"
"fmt"
"os"
"strings"
"sync"

"github.com/claceio/clace/internal/app"
"github.com/claceio/clace/internal/utils"
)

const (
DB_CONNECTION_CONFIG = "db_connection"
)

type SqlStore struct {
*utils.Logger
sync.Mutex
isInitialized bool
pluginContext *app.PluginContext
db *sql.DB
prefix string
isSqlite bool // false means postgres, no other options
}

var _ Store = (*SqlStore)(nil)

func NewSqlStore(pluginContext *app.PluginContext) (*SqlStore, error) {
return &SqlStore{
Logger: pluginContext.Logger,
pluginContext: pluginContext,
}, nil
}

func validateTableName(name string) error {
// TODO: validate table name
return nil
}

func (s *SqlStore) genTableName(collection string) (string, error) {
err := validateTableName(collection)
if err != nil {
return "", err
}
return fmt.Sprintf("'%s_%s'", s.prefix, collection), nil
}

func (s *SqlStore) initialize() error {
s.Lock()
defer s.Unlock()

if s.isInitialized {
// Already initialized
return nil
}

if err := s.initStore(); err != nil {
return err
}
s.isInitialized = true
return nil
}

func checkConnectString(connStr string) (string, error) {
parts := strings.SplitN(connStr, ":", 2)
if len(parts) != 2 {
return "", fmt.Errorf("invalid connection string: %s", connStr)
}
if !strings.HasPrefix(parts[0], "sqlite") { // only sqlite for now
return "", fmt.Errorf("invalid connection string: %s, only sqlite supported", connStr)
}
return os.ExpandEnv(parts[1]), nil
}

func (s *SqlStore) initStore() error {
if s.pluginContext.StoreInfo == nil {
return fmt.Errorf("store info not found")
}

connectStringConfig, ok := s.pluginContext.Config[DB_CONNECTION_CONFIG]
if !ok {
return fmt.Errorf("db connection string not found in config")
}
connectString, ok := connectStringConfig.(string)
if !ok {
return fmt.Errorf("db connection string is not a string")
}

var err error
connectString, err = checkConnectString(connectString)
if err != nil {
return err
}

s.db, err = sql.Open("sqlite", connectString)
if err != nil {
return fmt.Errorf("error opening db %s: %w", connectString, err)
}
s.isSqlite = true
s.prefix = "db_" + string(s.pluginContext.AppId)[len(utils.ID_PREFIX_APP_PROD):]

for _, storeType := range s.pluginContext.StoreInfo.Types {
collection, err := s.genTableName(storeType.Name)
if err != nil {
return err
}

createStmt := "CREATE TABLE IF NOT EXISTS " + collection + " (id INTEGER PRIMARY KEY AUTOINCREMENT, version INTEGER, created_by TEXT, updated_by TEXT, created_at INTEGER, updated_at INTEGER, data JSON)"
_, err = s.db.Exec(createStmt)
if err != nil {
return fmt.Errorf("error creating table %s: %w", collection, err)
}
s.Info().Msgf("Created table %s", collection)
}

return nil
}

// Insert a new entry in the store
func (s *SqlStore) Insert(collection string, entry *Entry) (EntryId, error) {
if err := s.initialize(); err != nil {
return -1, err
}

var err error
collection, err = s.genTableName(collection)
if err != nil {
return -1, err
}

dataJson, err := json.Marshal(entry.Data)
if err != nil {
return -1, fmt.Errorf("error marshalling data for collection %s: %w", collection, err)
}

createStmt := "INSERT INTO " + collection + " (version, created_by, updated_by, created_at, updated_at, data) VALUES (?, ?, ?, ?, ?, ?)"
result, err := s.db.Exec(createStmt, entry.Version, entry.CreatedBy, entry.UpdatedBy, entry.CreatedAt, entry.UpdatedAt, dataJson)
if err != nil {
return -1, nil
}

insertId, err := result.LastInsertId()
if err != nil {
return -1, nil
}
return EntryId(insertId), nil
}

// SelectById returns a single item from the store
func (s *SqlStore) SelectById(collection string, key EntryId) (*Entry, error) {
if err := s.initialize(); err != nil {
return nil, err
}

var err error
collection, err = s.genTableName(collection)
if err != nil {
return nil, err
}

query := "SELECT id, version, created_by, updated_by, created_at, updated_at, data FROM " + collection + " WHERE id = ?"
row := s.db.QueryRow(query, key)

var dataStr string
entry := &Entry{}

err = row.Scan(&entry.Id, &entry.Version, &entry.CreatedBy, &entry.UpdatedBy, &entry.CreatedAt, &entry.UpdatedAt, dataStr)
if err != nil {
if err == sql.ErrNoRows {
return nil, fmt.Errorf("entry %d not found in collection %s", key, collection)
}
return nil, err
}

if dataStr != "" {
if err := json.Unmarshal([]byte(dataStr), &entry.Data); err != nil {
return nil, err
}
}

return entry, nil
}

// Select returns the entries matching the filter
func (s *SqlStore) Select(collection string, filter map[string]any, sort []string, offset, limit int64) (EntryIterator, error) {
return nil, nil

}

// Update an existing entry in the store
func (s *SqlStore) Update(collection string, Entry *Entry) error {
return nil
}

// DeleteById an entry from the store by id
func (s *SqlStore) DeleteById(collection string, key EntryId) error {
return nil
}

// Delete entries from the store matching the filter
func (s *SqlStore) Delete(collection string, filter map[string]any) error {
return nil
}
Loading

0 comments on commit 27f8d3d

Please sign in to comment.