From 62e20a90ea7977a63ab5743829c2d183b98c8902 Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 15 Jul 2025 12:25:56 +0800 Subject: [PATCH] Refactor DSL management by introducing IO interfaces for database and file system operations - Removed the db.go and fs.go files, consolidating database and file system interactions into a unified IO interface. - Updated DSL struct to include db and fs fields for improved separation of concerns. - Refactored existing methods across various DSL managers (API, Connector, MCP, Model) to utilize the new IO interfaces for operations like Inspect, Create, Update, Delete, and List. - Enhanced error handling and validation in Create, Update, and Delete methods to ensure robust functionality. - Updated types to include new options for Load, Unload, and Reload operations, improving flexibility and usability. --- dsl/api/api.go | 16 +++-- dsl/connector/connector.go | 16 +++-- dsl/dsl.go | 133 ++++++++++++++++++++++++------------- dsl/{ => io}/db.go | 52 +++++++++------ dsl/{ => io}/fs.go | 66 ++++++++++-------- dsl/mcp/client.go | 21 +++--- dsl/mcp/server.go | 8 +-- dsl/model/model.go | 70 ++++++++++++------- dsl/types/interfaces.go | 37 +++++++---- dsl/types/types.go | 70 ++++++++++++++----- 10 files changed, 315 insertions(+), 174 deletions(-) rename dsl/{ => io}/db.go (80%) rename dsl/{ => io}/fs.go (68%) diff --git a/dsl/api/api.go b/dsl/api/api.go index 32fbcc16..ff6912bd 100644 --- a/dsl/api/api.go +++ b/dsl/api/api.go @@ -8,12 +8,14 @@ import ( // YaoAPI is the MCP client DSL manager type YaoAPI struct { - root string // The relative path of the MCP client DSL + root string // The relative path of the MCP client DSL + fs types.IO // The file system IO interface + db types.IO // The database IO interface } // New returns a new connector DSL manager -func New(root string) types.Manager { - return &YaoAPI{root: root} +func New(root string, fs types.IO, db types.IO) types.Manager { + return &YaoAPI{root: root, fs: fs, db: db} } // Loaded return all loaded DSLs @@ -22,17 +24,17 @@ func (api *YaoAPI) Loaded(ctx context.Context) (map[string]*types.Info, error) { } // Load will unload the DSL first, then load the DSL from DB or file system -func (api *YaoAPI) Load(ctx context.Context, id string, options interface{}) error { +func (api *YaoAPI) Load(ctx context.Context, options *types.LoadOptions) error { return nil } // Reload will unload the DSL first, then reload the DSL from DB or file system -func (api *YaoAPI) Reload(ctx context.Context, id string, options interface{}) error { +func (api *YaoAPI) Reload(ctx context.Context, options *types.ReloadOptions) error { return nil } // Unload will unload the DSL from memory -func (api *YaoAPI) Unload(ctx context.Context, id string, options interface{}) error { +func (api *YaoAPI) Unload(ctx context.Context, options *types.UnloadOptions) error { return nil } @@ -42,6 +44,6 @@ func (api *YaoAPI) Validate(ctx context.Context, source string) (bool, []types.L } // Execute will execute the DSL -func (api *YaoAPI) Execute(ctx context.Context, method string, args ...any) (any, error) { +func (api *YaoAPI) Execute(ctx context.Context, id string, method string, args ...any) (any, error) { return nil, nil } diff --git a/dsl/connector/connector.go b/dsl/connector/connector.go index 94f44217..bd11bfe1 100644 --- a/dsl/connector/connector.go +++ b/dsl/connector/connector.go @@ -8,12 +8,14 @@ import ( // YaoConnector is the connector DSL manager type YaoConnector struct { - root string // The relative path of the connector DSL + root string // The relative path of the connector DSL + fs types.IO // The file system IO interface + db types.IO // The database IO interface } // New returns a new connector DSL manager -func New(root string) types.Manager { - return &YaoConnector{root: root} +func New(root string, fs types.IO, db types.IO) types.Manager { + return &YaoConnector{root: root, fs: fs, db: db} } // Loaded return all loaded DSLs @@ -22,17 +24,17 @@ func (c *YaoConnector) Loaded(ctx context.Context) (map[string]*types.Info, erro } // Load will unload the DSL first, then load the DSL from DB or file system -func (c *YaoConnector) Load(ctx context.Context, id string, options interface{}) error { +func (c *YaoConnector) Load(ctx context.Context, options *types.LoadOptions) error { return nil } // Unload will unload the DSL from memory -func (c *YaoConnector) Unload(ctx context.Context, id string, options interface{}) error { +func (c *YaoConnector) Unload(ctx context.Context, options *types.UnloadOptions) error { return nil } // Reload will unload the DSL first, then reload the DSL from DB or file system -func (c *YaoConnector) Reload(ctx context.Context, id string, options interface{}) error { +func (c *YaoConnector) Reload(ctx context.Context, options *types.ReloadOptions) error { return nil } @@ -42,6 +44,6 @@ func (c *YaoConnector) Validate(ctx context.Context, source string) (bool, []typ } // Execute will execute the DSL -func (c *YaoConnector) Execute(ctx context.Context, method string, args ...any) (any, error) { +func (c *YaoConnector) Execute(ctx context.Context, id string, method string, args ...any) (any, error) { return nil, nil } diff --git a/dsl/dsl.go b/dsl/dsl.go index edc11f12..a1c94197 100644 --- a/dsl/dsl.go +++ b/dsl/dsl.go @@ -6,6 +6,7 @@ import ( "github.com/yaoapp/yao/dsl/api" "github.com/yaoapp/yao/dsl/connector" + "github.com/yaoapp/yao/dsl/io" "github.com/yaoapp/yao/dsl/mcp" "github.com/yaoapp/yao/dsl/model" "github.com/yaoapp/yao/dsl/types" @@ -17,11 +18,15 @@ type DSL struct { exts []string root string manager types.Manager + db types.IO + fs types.IO } // New returns a new DSL manager func New(typ types.Type) (types.DSL, error) { var manager types.Manager + var db types.IO = io.NewDB(typ) + var fs types.IO = io.NewFS(typ) // Get the root path and the extensions of the type root, exts := types.TypeRootAndExts(typ) @@ -30,15 +35,15 @@ func New(typ types.Type) (types.DSL, error) { switch typ { case types.TypeConnector: exts = []string{".conn.yao", ".conn.jsonc", ".conn.json"} - manager = connector.New(root) + manager = connector.New(root, fs, db) case types.TypeModel: exts = []string{".mod.yao", ".mod.jsonc", ".mod.json"} - manager = model.New(root) + manager = model.New(root, fs, db) case types.TypeMCPClient: exts = []string{".mcp.yao", ".mcp.jsonc", ".mcp.json"} - manager = mcp.NewClient(root) + manager = mcp.NewClient(root, fs, db) // case types.TypeMCPServer: // exts = []string{".mcp.yao", ".mcp.jsonc", ".mcp.json"} @@ -46,27 +51,27 @@ func New(typ types.Type) (types.DSL, error) { case types.TypeAPI: exts = []string{".http.yao", ".http.jsonc", ".http.json"} - manager = api.New(root) + manager = api.New(root, fs, db) default: return nil, fmt.Errorf("dsl manager is not initialized, %s not supported", typ) } - return &DSL{Type: typ, manager: manager, root: root, exts: exts}, nil + return &DSL{Type: typ, manager: manager, root: root, exts: exts, db: db, fs: fs}, nil } // Inspect DSL func (dsl *DSL) Inspect(ctx context.Context, id string) (*types.Info, error) { // Get the info from the db - info, exists, err := dsl.dbInspect(id) + info, exists, err := dsl.db.Inspect(id) if err != nil { return nil, err } if !exists { // Get the info from the file - info, exists, err = dsl.fsInspect(types.ToPath(dsl.Type, id)) + info, exists, err = dsl.fs.Inspect(id) if err != nil { return nil, err } @@ -99,14 +104,14 @@ func (dsl *DSL) Path(ctx context.Context, id string) (string, error) { func (dsl *DSL) Source(ctx context.Context, id string) (string, error) { // Get the source from the db - source, exists, err := dsl.dbSource(id) + source, exists, err := dsl.db.Source(id) if err != nil { return "", err } if !exists { // Get the source from the file - source, exists, err = dsl.fsSource(types.ToPath(dsl.Type, id)) + source, exists, err = dsl.fs.Source(id) if err != nil { return "", err } @@ -122,13 +127,13 @@ func (dsl *DSL) Source(ctx context.Context, id string) (string, error) { // List DSLs func (dsl *DSL) List(ctx context.Context, opts *types.ListOptions) ([]*types.Info, error) { // Get the list from the db - dbList, err := dsl.dbList(opts) + dbList, err := dsl.db.List(opts) if err != nil { return nil, err } // Get the list from the file - fileList, err := dsl.fsList(opts) + fileList, err := dsl.fs.List(opts) if err != nil { return nil, err } @@ -167,22 +172,35 @@ func (dsl *DSL) List(ctx context.Context, opts *types.ListOptions) ([]*types.Inf // Create DSL func (dsl *DSL) Create(ctx context.Context, options *types.CreateOptions) error { + + if options == nil { + return fmt.Errorf("create options is required") + } + if options.Store == types.StoreTypeDB { - err := dsl.dbCreate(options) + err := dsl.db.Create(options) if err != nil { return err } } if options.Store == types.StoreTypeFile { - err := dsl.fsCreate(options) + err := dsl.fs.Create(options) if err != nil { return err } } + var loadOptions *types.LoadOptions = &types.LoadOptions{ + ID: options.ID, + Path: types.ToPath(dsl.Type, options.ID), + Source: options.Source, + Store: options.Store, + Options: options.Load, + } + // Load the DSL - err := dsl.Load(ctx, options.ID, options.LoadOptions) + err := dsl.Load(ctx, loadOptions) if err != nil { return err } @@ -193,7 +211,7 @@ func (dsl *DSL) Create(ctx context.Context, options *types.CreateOptions) error // Exists Check if the DSL exists func (dsl *DSL) Exists(ctx context.Context, id string) (bool, error) { // Check if the DSL exists in the db - exists, err := dsl.dbExists(id) + exists, err := dsl.db.Exists(id) if err != nil { return false, err } @@ -203,20 +221,24 @@ func (dsl *DSL) Exists(ctx context.Context, id string) (bool, error) { } // Check if the DSL exists in the file - return dsl.fsExists(id) + return dsl.fs.Exists(id) } // Update DSL func (dsl *DSL) Update(ctx context.Context, options *types.UpdateOptions) error { + if options == nil { + return fmt.Errorf("update options is required") + } + // Exists - info, exists, err := dsl.dbInspect(options.ID) + info, exists, err := dsl.db.Inspect(options.ID) if err != nil { return err } if !exists { - info, exists, err = dsl.fsInspect(types.ToPath(dsl.Type, options.ID)) + info, exists, err = dsl.fs.Inspect(options.ID) if err != nil { return err } @@ -225,92 +247,113 @@ func (dsl *DSL) Update(ctx context.Context, options *types.UpdateOptions) error } } + // Create the reload options + var reloadOptions *types.ReloadOptions = &types.ReloadOptions{ + ID: options.ID, + Path: info.Path, + Source: options.Source, + Store: info.Store, + Options: options.Reload, + } + // Update the DSL in the db if info.Store == types.StoreTypeDB { - err := dsl.dbUpdate(options) + err := dsl.db.Update(options) if err != nil { return err } // Reload the DSL - return dsl.manager.Reload(ctx, options.ID, options.ReloadOptions) + return dsl.manager.Reload(ctx, reloadOptions) } // Update the DSL in the file - err = dsl.fsUpdate(options) + err = dsl.fs.Update(options) if err != nil { return err } // Reload the DSL - return dsl.manager.Reload(ctx, options.ID, options.ReloadOptions) + return dsl.manager.Reload(ctx, reloadOptions) } // Delete DSL -func (dsl *DSL) Delete(ctx context.Context, id string, options ...interface{}) error { +func (dsl *DSL) Delete(ctx context.Context, options *types.DeleteOptions) error { + + if options == nil { + return fmt.Errorf("delete options is required") + } + + if options.ID == "" { + return fmt.Errorf("delete options id is required") + } + // Exists - info, exists, err := dsl.dbInspect(id) + info, exists, err := dsl.db.Inspect(options.ID) if err != nil { return err } if !exists { - info, exists, err = dsl.fsInspect(types.ToPath(dsl.Type, id)) + info, exists, err = dsl.fs.Inspect(options.ID) if err != nil { return err } if !exists { - return fmt.Errorf("%s DSL not found, %s", dsl.Type, id) + return fmt.Errorf("%s DSL not found, %s", dsl.Type, options.ID) } } - var unloadOptions interface{} - if len(options) > 0 { - unloadOptions = options[0] + var opts map[string]interface{} + if options.Options != nil { + opts = options.Options + } + + var unloadOptions *types.UnloadOptions = &types.UnloadOptions{ + ID: options.ID, + Path: info.Path, + Store: info.Store, + Options: opts, } if info.Store == types.StoreTypeDB { - err = dsl.dbDelete(id) + err = dsl.db.Delete(options.ID) if err != nil { return err } // Unload the DSL - return dsl.manager.Unload(ctx, id, unloadOptions) + return dsl.manager.Unload(ctx, unloadOptions) } - err = dsl.fsDelete(id) + err = dsl.fs.Delete(options.ID) if err != nil { return err } // Unload the DSL - return dsl.manager.Unload(ctx, id, unloadOptions) + return dsl.manager.Unload(ctx, unloadOptions) } // Load DSL -func (dsl *DSL) Load(ctx context.Context, id string, options interface{}) error { - return dsl.manager.Load(ctx, id, options) +func (dsl *DSL) Load(ctx context.Context, options *types.LoadOptions) error { + return dsl.manager.Load(ctx, options) } // Unload DSL -func (dsl *DSL) Unload(ctx context.Context, id string, options ...interface{}) error { - var unloadOptions interface{} - if len(options) > 0 { - unloadOptions = options[0] - } - return dsl.manager.Unload(ctx, id, unloadOptions) +func (dsl *DSL) Unload(ctx context.Context, options *types.UnloadOptions) error { + return dsl.manager.Unload(ctx, options) } // Reload DSL -func (dsl *DSL) Reload(ctx context.Context, id string, options interface{}) error { - return dsl.manager.Reload(ctx, id, options) +func (dsl *DSL) Reload(ctx context.Context, options *types.ReloadOptions) error { + return dsl.manager.Reload(ctx, options) } // Execute DSL (Some DSLs can be executed) -func (dsl *DSL) Execute(ctx context.Context, method string, args ...any) (any, error) { - return dsl.manager.Execute(ctx, method, args...) +func (dsl *DSL) Execute(ctx context.Context, id string, method string, args ...any) (any, error) { + return dsl.manager.Execute(ctx, id, method, args...) } // Validate DSL diff --git a/dsl/db.go b/dsl/io/db.go similarity index 80% rename from dsl/db.go rename to dsl/io/db.go index 45cdc6c3..84bb445c 100644 --- a/dsl/db.go +++ b/dsl/io/db.go @@ -1,4 +1,4 @@ -package dsl +package io import ( "fmt" @@ -10,8 +10,18 @@ import ( "github.com/yaoapp/yao/dsl/types" ) -// getInfoFromDB get the info from the db -func (dsl *DSL) dbInspect(id string) (*types.Info, bool, error) { +// DB is the db io +type DB struct { + Type types.Type +} + +// NewDB create a new db io +func NewDB(typ types.Type) types.IO { + return &DB{Type: typ} +} + +// Inspect get the info from the db +func (db *DB) Inspect(id string) (*types.Info, bool, error) { // Get from database m := model.Select("__yao.dsl") @@ -57,8 +67,8 @@ func (dsl *DSL) dbInspect(id string) (*types.Info, bool, error) { return &info, true, nil } -// getSourceFromDB get the source from the db -func (dsl *DSL) dbSource(id string) (string, bool, error) { +// Source get the source from the db +func (db *DB) Source(id string) (string, bool, error) { // Get from database m := model.Select("__yao.dsl") @@ -79,14 +89,14 @@ func (dsl *DSL) dbSource(id string) (string, bool, error) { source, ok := rows[0]["source"].(string) if !ok { - return "", true, fmt.Errorf("%s %s source is not a string", dsl.Type, id) + return "", true, fmt.Errorf("%s %s source is not a string", db.Type, id) } return source, true, nil } -// getListFromDB get the list from the db -func (dsl *DSL) dbList(options *types.ListOptions) ([]*types.Info, error) { +// List get the list from the db +func (db *DB) List(options *types.ListOptions) ([]*types.Info, error) { // Get from database m := model.Select("__yao.dsl") @@ -96,7 +106,7 @@ func (dsl *DSL) dbList(options *types.ListOptions) ([]*types.Info, error) { orders = []model.QueryOrder{{Column: "sort", Option: "asc"}} } - var wheres []model.QueryWhere = []model.QueryWhere{{Column: "type", Value: dsl.Type}} + var wheres []model.QueryWhere = []model.QueryWhere{{Column: "type", Value: db.Type}} // Filter by tags if len(options.Tags) > 0 { @@ -110,7 +120,7 @@ func (dsl *DSL) dbList(options *types.ListOptions) ([]*types.Info, error) { // Get the list rows, err := m.Get(model.QueryParam{ - Wheres: []model.QueryWhere{{Column: "type", Value: dsl.Type}}, + Wheres: []model.QueryWhere{{Column: "type", Value: db.Type}}, Select: []interface{}{"dsl_id", "label", "path", "sort", "tags", "description", "status", "store", "mtime", "ctime"}, Orders: orders, }) @@ -136,10 +146,11 @@ func (dsl *DSL) dbList(options *types.ListOptions) ([]*types.Info, error) { return infos, nil } -func (dsl *DSL) dbCreate(options *types.CreateOptions) error { +// Create create the dsl +func (db *DB) Create(options *types.CreateOptions) error { if options.Source == "" { - return fmt.Errorf("%s %s source is required", dsl.Type, options.ID) + return fmt.Errorf("%s %s source is required", db.Type, options.ID) } // Get info from source @@ -154,7 +165,7 @@ func (dsl *DSL) dbCreate(options *types.CreateOptions) error { data := map[string]interface{}{ "source": options.Source, "dsl_id": options.ID, - "type": dsl.Type, + "type": db.Type, "label": info.Label, "path": info.Path, "sort": info.Sort, @@ -174,9 +185,10 @@ func (dsl *DSL) dbCreate(options *types.CreateOptions) error { return nil } -func (dsl *DSL) dbUpdate(options *types.UpdateOptions) error { +// Update update the dsl +func (db *DB) Update(options *types.UpdateOptions) error { if options.Source == "" && options.Info == nil { - return fmt.Errorf("%s %s one of source or info is required", dsl.Type, options.ID) + return fmt.Errorf("%s %s one of source or info is required", db.Type, options.ID) } m := model.Select("__yao.dsl") @@ -192,7 +204,7 @@ func (dsl *DSL) dbUpdate(options *types.UpdateOptions) error { } if len(rows) == 0 { - return fmt.Errorf("%s %s not found", dsl.Type, options.ID) + return fmt.Errorf("%s %s not found", db.Type, options.ID) } row := rows[0] @@ -234,7 +246,8 @@ func (dsl *DSL) dbUpdate(options *types.UpdateOptions) error { return nil } -func (dsl *DSL) dbDelete(id string) error { +// Delete delete the dsl +func (db *DB) Delete(id string) error { // Get from database m := model.Select("__yao.dsl") @@ -250,7 +263,7 @@ func (dsl *DSL) dbDelete(id string) error { } if len(rows) == 0 { - return fmt.Errorf("%s %s not found", dsl.Type, id) + return fmt.Errorf("%s %s not found", db.Type, id) } // Delete the dsl @@ -258,7 +271,8 @@ func (dsl *DSL) dbDelete(id string) error { return m.Delete(row["id"]) } -func (dsl *DSL) dbExists(id string) (bool, error) { +// Exists check if the dsl exists +func (db *DB) Exists(id string) (bool, error) { // Get from database m := model.Select("__yao.dsl") diff --git a/dsl/fs.go b/dsl/io/fs.go similarity index 68% rename from dsl/fs.go rename to dsl/io/fs.go index 742db5b1..22bdd4ae 100644 --- a/dsl/fs.go +++ b/dsl/io/fs.go @@ -1,4 +1,4 @@ -package dsl +package io import ( "fmt" @@ -8,15 +8,19 @@ import ( "github.com/yaoapp/yao/dsl/types" ) -// getInfoFromFile get the info from the file -func (dsl *DSL) fsInspect(id string, path ...string) (*types.Info, bool, error) { - file := "" - if len(path) > 0 { - file = path[0] - } else { - file = types.ToPath(dsl.Type, id) - } +// FS is the fs io +type FS struct { + Type types.Type +} +// NewFS create a new fs io +func NewFS(typ types.Type) types.IO { + return &FS{Type: typ} +} + +// Inspect get the info from the file +func (fs *FS) Inspect(id string) (*types.Info, bool, error) { + file := types.ToPath(fs.Type, id) var info types.Info = types.Info{ID: id, Path: file} exists, err := application.App.Exists(file) if err != nil { @@ -49,9 +53,9 @@ func (dsl *DSL) fsInspect(id string, path ...string) (*types.Info, bool, error) return &info, true, nil } -// getSourceFromFile get the source from the file -func (dsl *DSL) fsSource(id string) (string, bool, error) { - path := types.ToPath(dsl.Type, id) +// Source get the source from the file +func (fs *FS) Source(id string) (string, bool, error) { + path := types.ToPath(fs.Type, id) exists, err := application.App.Exists(path) if err != nil { return "", false, err @@ -68,9 +72,9 @@ func (dsl *DSL) fsSource(id string) (string, bool, error) { return string(data), true, nil } -// getListFromPath get the list from the path -func (dsl *DSL) fsList(options *types.ListOptions) ([]*types.Info, error) { - root, exts := types.TypeRootAndExts(dsl.Type) +// List get the list from the path +func (fs *FS) List(options *types.ListOptions) ([]*types.Info, error) { + root, exts := types.TypeRootAndExts(fs.Type) var infos []*types.Info = []*types.Info{} patterns := []string{} for _, ext := range exts { @@ -81,8 +85,8 @@ func (dsl *DSL) fsList(options *types.ListOptions) ([]*types.Info, error) { if isdir { return nil } - id := types.WithTypeToID(dsl.Type, file) - info, _, err := dsl.fsInspect(id, file) + id := types.WithTypeToID(fs.Type, file) + info, _, err := fs.Inspect(id) if err != nil { errs = append(errs, err) return nil @@ -112,9 +116,10 @@ func (dsl *DSL) fsList(options *types.ListOptions) ([]*types.Info, error) { return infos, err } -func (dsl *DSL) fsCreate(options *types.CreateOptions) error { +// Create create the file +func (fs *FS) Create(options *types.CreateOptions) error { - path := types.ToPath(dsl.Type, options.ID) + path := types.ToPath(fs.Type, options.ID) // Check if the file is a directory exists, err := application.App.Exists(path) @@ -123,21 +128,22 @@ func (dsl *DSL) fsCreate(options *types.CreateOptions) error { } if exists { - return fmt.Errorf("%v %s already exists", dsl.Type, options.ID) + return fmt.Errorf("%v %s already exists", fs.Type, options.ID) } // Create the file return application.App.Write(path, []byte(options.Source)) } -func (dsl *DSL) fsUpdate(options *types.UpdateOptions) error { +// Update update the file +func (fs *FS) Update(options *types.UpdateOptions) error { // Validate the options if options.Source == "" && options.Info == nil { - return fmt.Errorf("%v %s one of source or info is required", dsl.Type, options.ID) + return fmt.Errorf("%v %s one of source or info is required", fs.Type, options.ID) } - path := types.ToPath(dsl.Type, options.ID) + path := types.ToPath(fs.Type, options.ID) // Check if the file exists exists, err := application.App.Exists(path) @@ -146,7 +152,7 @@ func (dsl *DSL) fsUpdate(options *types.UpdateOptions) error { } if !exists { - return fmt.Errorf("%v %s not found", dsl.Type, options.ID) + return fmt.Errorf("%v %s not found", fs.Type, options.ID) } // Update source @@ -179,9 +185,10 @@ func (dsl *DSL) fsUpdate(options *types.UpdateOptions) error { return application.App.Write(path, []byte(new)) } -func (dsl *DSL) fsDelete(id string) error { +// Delete delete the file +func (fs *FS) Delete(id string) error { - path := types.ToPath(dsl.Type, id) + path := types.ToPath(fs.Type, id) // Check if the file is a directory exists, err := application.App.Exists(path) @@ -190,14 +197,15 @@ func (dsl *DSL) fsDelete(id string) error { } if !exists { - return fmt.Errorf("%v %s not found", dsl.Type, id) + return fmt.Errorf("%v %s not found", fs.Type, id) } // Delete the file return application.App.Remove(path) } -func (dsl *DSL) fsExists(id string) (bool, error) { - path := types.ToPath(dsl.Type, id) +// Exists check if the file exists +func (fs *FS) Exists(id string) (bool, error) { + path := types.ToPath(fs.Type, id) return application.App.Exists(path) } diff --git a/dsl/mcp/client.go b/dsl/mcp/client.go index 5f55bdb7..9e6e0183 100644 --- a/dsl/mcp/client.go +++ b/dsl/mcp/client.go @@ -8,17 +8,14 @@ import ( // YaoMCPClient is the MCP client DSL manager type YaoMCPClient struct { - root string // The relative path of the MCP client DSL + root string // The relative path of the MCP client DSL + fs types.IO // The file system IO interface + db types.IO // The database IO interface } // NewClient returns a new MCP client DSL manager -func NewClient(root string) types.Manager { - return New(root) -} - -// New returns a new connector DSL manager -func New(root string) types.Manager { - return &YaoMCPClient{root: root} +func NewClient(root string, fs types.IO, db types.IO) types.Manager { + return &YaoMCPClient{root: root, fs: fs, db: db} } // Loaded return all loaded DSLs @@ -27,17 +24,17 @@ func (client *YaoMCPClient) Loaded(ctx context.Context) (map[string]*types.Info, } // Load will unload the DSL first, then load the DSL from DB or file system -func (client *YaoMCPClient) Load(ctx context.Context, id string, options interface{}) error { +func (client *YaoMCPClient) Load(ctx context.Context, options *types.LoadOptions) error { return nil } // Unload will unload the DSL from memory -func (client *YaoMCPClient) Unload(ctx context.Context, id string, options interface{}) error { +func (client *YaoMCPClient) Unload(ctx context.Context, options *types.UnloadOptions) error { return nil } // Reload will unload the DSL first, then reload the DSL from DB or file system -func (client *YaoMCPClient) Reload(ctx context.Context, id string, options interface{}) error { +func (client *YaoMCPClient) Reload(ctx context.Context, options *types.ReloadOptions) error { return nil } @@ -47,6 +44,6 @@ func (client *YaoMCPClient) Validate(ctx context.Context, source string) (bool, } // Execute will execute the DSL -func (client *YaoMCPClient) Execute(ctx context.Context, method string, args ...any) (any, error) { +func (client *YaoMCPClient) Execute(ctx context.Context, id string, method string, args ...any) (any, error) { return nil, nil } diff --git a/dsl/mcp/server.go b/dsl/mcp/server.go index e6819775..fad58f44 100644 --- a/dsl/mcp/server.go +++ b/dsl/mcp/server.go @@ -22,17 +22,17 @@ func (server *YaoMCPServer) Loaded(ctx context.Context) (map[string]*types.Info, } // Load will unload the DSL first, then load the DSL from DB or file system -func (server *YaoMCPServer) Load(ctx context.Context, id string, options interface{}) error { +func (server *YaoMCPServer) Load(ctx context.Context, options *types.LoadOptions) error { return nil } // Unload will unload the DSL from memory -func (server *YaoMCPServer) Unload(ctx context.Context, id string, options interface{}) error { +func (server *YaoMCPServer) Unload(ctx context.Context, options *types.UnloadOptions) error { return nil } // Reload will unload the DSL first, then reload the DSL from DB or file system -func (server *YaoMCPServer) Reload(ctx context.Context, id string, options interface{}) error { +func (server *YaoMCPServer) Reload(ctx context.Context, options *types.ReloadOptions) error { return nil } @@ -42,6 +42,6 @@ func (server *YaoMCPServer) Validate(ctx context.Context, source string) (bool, } // Execute will execute the DSL -func (server *YaoMCPServer) Execute(ctx context.Context, method string, args ...any) (any, error) { +func (server *YaoMCPServer) Execute(ctx context.Context, id string, method string, args ...any) (any, error) { return nil, nil } diff --git a/dsl/model/model.go b/dsl/model/model.go index fbd4f9bb..026660b2 100644 --- a/dsl/model/model.go +++ b/dsl/model/model.go @@ -10,17 +10,14 @@ import ( // YaoModel is the MCP client DSL manager type YaoModel struct { - root string // The relative path of the MCP client DSL -} - -// NewClient returns a new MCP client DSL manager -func NewClient(root string) types.Manager { - return New(root) + root string // The relative path of the model DSL + fs types.IO // The file system IO interface + db types.IO // The database IO interface } // New returns a new connector DSL manager -func New(root string) types.Manager { - return &YaoModel{root: root} +func New(root string, fs types.IO, db types.IO) types.Manager { + return &YaoModel{root: root, fs: fs, db: db} } // Loaded return all loaded DSLs @@ -43,10 +40,19 @@ func (m *YaoModel) Loaded(ctx context.Context) (map[string]*types.Info, error) { } // Load will unload the DSL first, then load the DSL from DB or file system -func (m *YaoModel) Load(ctx context.Context, id string, options interface{}) error { +func (m *YaoModel) Load(ctx context.Context, options *types.LoadOptions) error { + + if options == nil { + return fmt.Errorf("load options is required") + } + + if options.ID == "" { + return fmt.Errorf("load options id is required") + } + var opts map[string]interface{} - if v, ok := options.(map[string]interface{}); ok { - opts = v + if options.Options != nil { + opts = options.Options } var migration bool = false @@ -59,8 +65,8 @@ func (m *YaoModel) Load(ctx context.Context, id string, options interface{}) err reset = v.(bool) } - path := types.ToPath(types.TypeModel, id) - mod, err := model.LoadSync(path, id) + path := types.ToPath(types.TypeModel, options.ID) + mod, err := model.LoadSync(path, options.ID) if err != nil { return err } @@ -73,11 +79,19 @@ func (m *YaoModel) Load(ctx context.Context, id string, options interface{}) err } // Unload will unload the DSL from memory -func (m *YaoModel) Unload(ctx context.Context, id string, options interface{}) error { +func (m *YaoModel) Unload(ctx context.Context, options *types.UnloadOptions) error { + + if options == nil { + return fmt.Errorf("unload options is required") + } + + if options.ID == "" { + return fmt.Errorf("unload options id is required") + } var opts map[string]interface{} - if v, ok := options.(map[string]interface{}); ok { - opts = v + if options.Options != nil { + opts = options.Options } var dropTable bool = false @@ -85,9 +99,9 @@ func (m *YaoModel) Unload(ctx context.Context, id string, options interface{}) e dropTable = v.(bool) } - mod := model.Select(id) + mod := model.Select(options.ID) if mod == nil { - return fmt.Errorf("model %s not found", id) + return fmt.Errorf("model %s not found", options.ID) } if dropTable { @@ -98,11 +112,19 @@ func (m *YaoModel) Unload(ctx context.Context, id string, options interface{}) e } // Reload will unload the DSL first, then reload the DSL from DB or file system -func (m *YaoModel) Reload(ctx context.Context, id string, options interface{}) error { +func (m *YaoModel) Reload(ctx context.Context, options *types.ReloadOptions) error { + + if options == nil { + return fmt.Errorf("reload options is required") + } + + if options.ID == "" { + return fmt.Errorf("reload options id is required") + } var opts map[string]interface{} - if v, ok := options.(map[string]interface{}); ok { - opts = v + if options.Options != nil { + opts = options.Options } var migration bool = false @@ -116,8 +138,8 @@ func (m *YaoModel) Reload(ctx context.Context, id string, options interface{}) e } // Reload the model - path := types.ToPath(types.TypeModel, id) - mod, err := model.LoadSync(path, id) + path := types.ToPath(types.TypeModel, options.ID) + mod, err := model.LoadSync(path, options.ID) if err != nil { return err } @@ -134,6 +156,6 @@ func (m *YaoModel) Validate(ctx context.Context, source string) (bool, []types.L } // Execute will execute the DSL -func (m *YaoModel) Execute(ctx context.Context, method string, args ...any) (any, error) { +func (m *YaoModel) Execute(ctx context.Context, id string, method string, args ...any) (any, error) { return nil, fmt.Errorf("Not implemented") } diff --git a/dsl/types/interfaces.go b/dsl/types/interfaces.go index 87258205..09d3a254 100644 --- a/dsl/types/interfaces.go +++ b/dsl/types/interfaces.go @@ -1,6 +1,8 @@ package types -import "context" +import ( + "context" +) // DSL interface type DSL interface { @@ -11,17 +13,17 @@ type DSL interface { Exists(ctx context.Context, id string) (bool, error) // Check if the DSL exists // DSL Operations - Create(ctx context.Context, options *CreateOptions) error // Create DSL, Create will unload the DSL first, then create the DSL to DB - Update(ctx context.Context, options *UpdateOptions) error // Update DSL, Update will unload the DSL first, then update the DSL, if update info only, will not unload the DSL - Delete(ctx context.Context, id string, unloadOptions ...interface{}) error // Delete DSL, Delete will unload the DSL first, then delete the DSL file + Create(ctx context.Context, options *CreateOptions) error // Create DSL, Create will unload the DSL first, then create the DSL to DB + Update(ctx context.Context, options *UpdateOptions) error // Update DSL, Update will unload the DSL first, then update the DSL, if update info only, will not unload the DSL + Delete(ctx context.Context, options *DeleteOptions) error // Delete DSL, Delete will unload the DSL first, then delete the DSL file // Load manager - Load(ctx context.Context, id string, options interface{}) error // Load DSL, Load will unload the DSL first, then load the DSL from DB or file system - Reload(ctx context.Context, id string, options interface{}) error // Reload DSL, Reload will unload the DSL first, then reload the DSL from DB or file system - Unload(ctx context.Context, id string, options ...interface{}) error // Unload DSL, Unload will unload the DSL from memory + Load(ctx context.Context, options *LoadOptions) error // Load DSL, Load will unload the DSL first, then load the DSL from DB or file system + Reload(ctx context.Context, options *ReloadOptions) error // Reload DSL, Reload will unload the DSL first, then reload the DSL from DB or file system + Unload(ctx context.Context, options *UnloadOptions) error // Unload DSL, Unload will unload the DSL from memory // Execute - Execute(ctx context.Context, method string, args ...any) (any, error) // Execute DSL (Some DSLs can be executed) + Execute(ctx context.Context, id string, method string, args ...any) (any, error) // Execute DSL (Some DSLs can be executed) // Validate Validate(ctx context.Context, source string) (bool, []LintMessage) // Validate DSL, Validate will validate the DSL from source @@ -33,17 +35,28 @@ type Manager interface { Loaded(ctx context.Context) (map[string]*Info, error) // Get all loaded DSLs // Load DSL, Load will unload the DSL first, then load the DSL from DB or file system - Load(ctx context.Context, id string, options interface{}) error + Load(ctx context.Context, options *LoadOptions) error // Unload DSL, Unload will unload the DSL from memory - Unload(ctx context.Context, id string, options interface{}) error + Unload(ctx context.Context, options *UnloadOptions) error // Reload DSL, Reload will unload the DSL first, then reload the DSL from DB or file system - Reload(ctx context.Context, id string, options interface{}) error + Reload(ctx context.Context, options *ReloadOptions) error // Validate DSL, Validate will validate the DSL from source Validate(ctx context.Context, source string) (bool, []LintMessage) // Execute DSL (Some DSLs can be executed) - Execute(ctx context.Context, method string, args ...any) (any, error) + Execute(ctx context.Context, id string, method string, args ...any) (any, error) +} + +// IO interface +type IO interface { + Inspect(id string) (*Info, bool, error) + Source(id string) (string, bool, error) + List(options *ListOptions) ([]*Info, error) + Create(options *CreateOptions) error + Update(options *UpdateOptions) error + Delete(id string) error + Exists(id string) (bool, error) } diff --git a/dsl/types/types.go b/dsl/types/types.go index 1b1da3eb..ec4bbdb2 100644 --- a/dsl/types/types.go +++ b/dsl/types/types.go @@ -78,17 +78,23 @@ const ( // Info for DSL type Info struct { - ID string + ID string + Type Type - Sort int - Path string Label string Description string Tags []string - Status Status - Store StoreType - Mtime time.Time - Ctime time.Time + + Sort int + Path string + Store StoreType + + Readable bool + Builtin bool + + Status Status + Mtime time.Time + Ctime time.Time } // ListOptions for DSL list @@ -100,18 +106,52 @@ type ListOptions struct { // CreateOptions for DSL upsert type CreateOptions struct { - ID string // ID is the id of the DSL, if not provided, a new id will be generated, required - Source string // Source is the source of the DSL, if not provided, the DSL will be loaded from the file system - Store StoreType // Store is the store type of the DSL, if not provided, the DSL will be loaded from the file system - LoadOptions interface{} // LoadOptions is the options for the DSL, if not provided, the DSL will be loaded from the file system + ID string // ID is the id of the DSL, if not provided, a new id will be generated, required + Source string // Source is the source of the DSL, if not provided, the DSL will be loaded from the file system + Store StoreType // Store is the store type of the DSL, if not provided, the DSL will be loaded from the file system + Load map[string]interface{} // LoadOptions is the options for the DSL, if not provided, the DSL will be loaded from the file system } // UpdateOptions for DSL upsert type UpdateOptions struct { - ID string // ID is the id of the DSL, if not provided, a new id will be generated, required - Info *Info // Info is the info of the DSL, if not provided, the DSL will be loaded from the file system, one of info or source must be provided - Source string // Source is the source of the DSL, if not provided, the DSL will be loaded from the file system, one of info or source must be provided - ReloadOptions interface{} // ReloadOptions is the options for the DSL, if not provided, the DSL will be loaded from the file system + ID string // ID is the id of the DSL, if not provided, a new id will be generated, required + Info *Info // Info is the info of the DSL, if not provided, the DSL will be loaded from the file system, one of info or source must be provided + Source string // Source is the source of the DSL, if not provided, the DSL will be loaded from the file system, one of info or source must be provided + Reload map[string]interface{} // ReloadOptions is the options for the DSL, if not provided, the DSL will be loaded from the file system +} + +// DeleteOptions for DSL delete options +type DeleteOptions struct { + ID string // ID is the id of the DSL, if not provided, a new id will be generated, required + Path string // Path is the path of the DSL, if not provided, the DSL will be loaded from the file system + Options map[string]interface{} // Options is the options for the DSL, if not provided, the DSL will be loaded from the file system +} + +// LoadOptions for DSL load options +type LoadOptions struct { + ID string + Path string + Source string + Store StoreType + Options map[string]interface{} +} + +// UnloadOptions for DSL unload options +type UnloadOptions struct { + ID string + Path string + Source string + Store StoreType + Options map[string]interface{} +} + +// ReloadOptions for DSL reload options +type ReloadOptions struct { + ID string + Path string + Source string + Store StoreType + Options map[string]interface{} } // LintMessage for DSL linter