浏览代码

refactor: plugin identifier

Yeuoly 10 月之前
父节点
当前提交
026c76ba1d

+ 1 - 1
internal/core/plugin_manager/aws_manager/environment.go

@@ -29,5 +29,5 @@ func (r *AWSPluginRuntime) Identity() (plugin_entities.PluginUniqueIdentifier, e
 	if err != nil {
 		return "", err
 	}
-	return plugin_entities.PluginUniqueIdentifier(fmt.Sprintf("%s@%s", r.Config.Identity(), checksum)), nil
+	return plugin_entities.NewPluginUniqueIdentifier(fmt.Sprintf("%s@%s", r.Config.Identity(), checksum))
 }

+ 1 - 1
internal/core/plugin_manager/local_manager/environment.go

@@ -40,5 +40,5 @@ func (r *LocalPluginRuntime) Identity() (plugin_entities.PluginUniqueIdentifier,
 	if err != nil {
 		return "", err
 	}
-	return plugin_entities.PluginUniqueIdentifier(fmt.Sprintf("%s@%s", r.Config.Identity(), checksum)), nil
+	return plugin_entities.NewPluginUniqueIdentifier(fmt.Sprintf("%s@%s", r.Config.Identity(), checksum))
 }

+ 2 - 2
internal/core/plugin_manager/remote_manager/environment.go

@@ -8,9 +8,9 @@ import (
 )
 
 func (r *RemotePluginRuntime) Identity() (plugin_entities.PluginUniqueIdentifier, error) {
-	identity := strings.Join([]string{r.Configuration().Identity(), r.tenant_id}, ":")
+	identity := strings.Join([]string{r.tenant_id, r.Configuration().Identity()}, "/")
 	checksum, _ := r.Checksum()
-	return plugin_entities.PluginUniqueIdentifier(fmt.Sprintf("%s@%s", identity, checksum)), nil
+	return plugin_entities.NewPluginUniqueIdentifier(fmt.Sprintf("%s@%s", identity, checksum))
 }
 
 func (r *RemotePluginRuntime) Cleanup() {

+ 1 - 1
internal/core/plugin_packager/decoder/decoder.go

@@ -183,5 +183,5 @@ func (p *PluginDecoderHelper) UniqueIdentity(decoder PluginDecoder) (plugin_enti
 		return plugin_entities.PluginUniqueIdentifier(""), err
 	}
 
-	return plugin_entities.PluginUniqueIdentifier(fmt.Sprintf("%s@%s", identity, checksum)), nil
+	return plugin_entities.NewPluginUniqueIdentifier(fmt.Sprintf("%s@%s", identity, checksum))
 }

+ 3 - 2
internal/server/controllers/base.go

@@ -46,12 +46,13 @@ func BindRequestWithPluginUniqueIdentifier[T any](r *gin.Context, success func(
 			return
 		}
 
-		if err := plugin_entities.PluginUniqueIdentifier(plugin_unique_identifier).Validate(); err != nil {
+		identifier, err := plugin_entities.NewPluginUniqueIdentifier(plugin_unique_identifier)
+		if err != nil {
 			resp := entities.NewErrorResponse(-400, err.Error())
 			r.JSON(400, resp)
 			return
 		}
 
-		success(req, plugin_entities.PluginUniqueIdentifier(plugin_unique_identifier))
+		success(req, identifier)
 	})
 }

+ 1 - 1
internal/server/controllers/endpoint.go

@@ -9,7 +9,7 @@ import (
 func SetupEndpoint(ctx *gin.Context) {
 	BindRequest(ctx, func(
 		request struct {
-			PluginUniqueIdentifier plugin_entities.PluginUniqueIdentifier `json:"plugin_unique_identifier" binding:"required,plugin_unique_identifier"`
+			PluginUniqueIdentifier plugin_entities.PluginUniqueIdentifier `json:"plugin_unique_identifier" binding:"required" validate:"plugin_unique_identifier"`
 			TenantID               string                                 `json:"tenant_id" binding:"required"`
 			UserID                 string                                 `json:"user_id" binding:"required"`
 			Settings               map[string]any                         `json:"settings" binding:"omitempty"`

+ 2 - 2
internal/server/controllers/plugins.go

@@ -57,9 +57,9 @@ func InstallPluginFromIdentifier(app *app.Config) gin.HandlerFunc {
 	return func(c *gin.Context) {
 		BindRequest(c, func(request struct {
 			TenantID               string                                 `json:"tenant_id" binding:"required"`
-			PluginUniqueIdentifier plugin_entities.PluginUniqueIdentifier `json:"plugin_unique_identifier" binding:"required,plugin_unique_identifier"`
+			PluginUniqueIdentifier plugin_entities.PluginUniqueIdentifier `json:"plugin_unique_identifier" binding:"required" validate:"plugin_unique_identifier"`
 		}) {
-			service.InstallPluginFromIdentifier(c, request.TenantID, request.PluginUniqueIdentifier)
+			c.JSON(http.StatusOK, service.InstallPluginFromIdentifier(c, request.TenantID, request.PluginUniqueIdentifier))
 		})
 	}
 }

+ 8 - 6
internal/server/endpoint.go

@@ -54,13 +54,15 @@ func (app *App) EndpointHandler(ctx *gin.Context, hook_id string, path string) {
 		return
 	}
 
+	plugin_unique_identifier, err := plugin_entities.NewPluginUniqueIdentifier(plugin_installation.PluginUniqueIdentifier)
+	if err != nil {
+		ctx.JSON(400, gin.H{"error": "invalid plugin unique identifier"})
+		return
+	}
+
 	// check if plugin exists in current node
-	if !app.cluster.IsPluginNoCurrentNode(
-		plugin_entities.PluginUniqueIdentifier(plugin_installation.PluginUniqueIdentifier),
-	) {
-		app.redirectPluginInvokeByPluginIdentifier(ctx, plugin_entities.PluginUniqueIdentifier(
-			plugin_installation.PluginUniqueIdentifier,
-		))
+	if !app.cluster.IsPluginNoCurrentNode(plugin_unique_identifier) {
+		app.redirectPluginInvokeByPluginIdentifier(ctx, plugin_unique_identifier)
 	} else {
 		service.Endpoint(ctx, &endpoint, &plugin_installation, path)
 	}

+ 3 - 3
internal/server/middleware.go

@@ -49,9 +49,9 @@ func (app *App) RedirectPluginInvoke() gin.HandlerFunc {
 			reader: bytes.NewReader(raw),
 		}
 
-		identity := plugin_entities.PluginUniqueIdentifier(ctx.Request.Header.Get(constants.X_PLUGIN_IDENTIFIER))
-		if identity == "" {
-			ctx.AbortWithStatusJSON(400, gin.H{"error": "Invalid request"})
+		identity, err := plugin_entities.NewPluginUniqueIdentifier(ctx.Request.Header.Get(constants.X_PLUGIN_IDENTIFIER))
+		if err != nil {
+			ctx.AbortWithStatusJSON(400, gin.H{"error": "Invalid request, " + err.Error()})
 			return
 		}
 

+ 8 - 4
internal/service/endpoint.go

@@ -38,11 +38,15 @@ func Endpoint(
 		ctx.JSON(500, gin.H{"error": err.Error()})
 	}
 
+	identifier, err := plugin_entities.NewPluginUniqueIdentifier(plugin_installation.PluginUniqueIdentifier)
+	if err != nil {
+		ctx.JSON(400, gin.H{"error": "Invalid plugin identifier, " + err.Error()})
+		return
+	}
+
 	// fetch plugin
 	manager := plugin_manager.Manager()
-	runtime := manager.Get(
-		plugin_entities.PluginUniqueIdentifier(plugin_installation.PluginUniqueIdentifier),
-	)
+	runtime := manager.Get(identifier)
 	if runtime == nil {
 		ctx.JSON(404, gin.H{"error": "plugin not found"})
 		return
@@ -79,7 +83,7 @@ func Endpoint(
 	session := session_manager.NewSession(
 		endpoint.TenantID,
 		"",
-		plugin_entities.PluginUniqueIdentifier(plugin_installation.PluginUniqueIdentifier),
+		identifier,
 		ctx.GetString("cluster_id"),
 		access_types.PLUGIN_ACCESS_TYPE_ENDPOINT,
 		access_types.PLUGIN_ACCESS_ACTION_INVOKE_ENDPOINT,

+ 46 - 4
internal/types/entities/plugin_entities/identity.go

@@ -1,6 +1,7 @@
 package plugin_entities
 
 import (
+	"errors"
 	"regexp"
 	"strings"
 
@@ -12,23 +13,64 @@ type PluginUniqueIdentifier string
 
 var (
 	// pluginUniqueIdentifierRegexp is a regular expression to validate the plugin unique identifier.
-	// It must be in the format of "plugin_id:version@checksum".
+	// It must be in the format of "author/plugin_id:version@checksum".
 	// all lowercase. the length of plugin_id must be less than 128, and for version part, it must be ^\d{1,4}(\.\d{1,4}){1,3}(-\w{1,16})?$
 	// for checksum, it must be a 32-character hexadecimal string.
+	// the author part is optional, if not specified, it will be empty.
 	pluginUniqueIdentifierRegexp = regexp.MustCompile(
-		`^[a-z0-9_-]{1,128}:[0-9]{1,4}(\.[0-9]{1,4}){1,3}(-\w{1,16})?@[a-f0-9]{32}$`,
+		`^(?:([a-z0-9_-]{1,64})\/)?([a-z0-9_-]{1,128}):([0-9]{1,4})(\.[0-9]{1,4}){1,3}(-\w{1,16})?@[a-f0-9]{32,64}$`,
 	)
 )
 
+func NewPluginUniqueIdentifier(identifier string) (PluginUniqueIdentifier, error) {
+	if !pluginUniqueIdentifierRegexp.MatchString(identifier) {
+		return "", errors.New("plugin_unique_identifier is not valid")
+	}
+	return PluginUniqueIdentifier(identifier), nil
+}
+
 func (p PluginUniqueIdentifier) PluginID() string {
-	// try find @
-	split := strings.Split(p.String(), "@")
+	// try find :
+	split := strings.Split(p.String(), ":")
 	if len(split) == 2 {
 		return split[0]
 	}
 	return p.String()
 }
 
+func (p PluginUniqueIdentifier) Version() string {
+	// extract version part from the string
+	split := strings.Split(p.String(), "@")
+	if len(split) == 2 {
+		split = strings.Split(split[0], ":")
+		if len(split) == 2 {
+			return split[1]
+		}
+	}
+	return ""
+}
+
+func (p PluginUniqueIdentifier) Author() string {
+	// extract author part from the string
+	split := strings.Split(p.String(), ":")
+	if len(split) == 2 {
+		split = strings.Split(split[0], "/")
+		if len(split) == 2 {
+			return split[0]
+		}
+	}
+	return ""
+}
+
+func (p PluginUniqueIdentifier) Checksum() string {
+	// extract checksum part from the string
+	split := strings.Split(p.String(), "@")
+	if len(split) == 2 {
+		return split[1]
+	}
+	return ""
+}
+
 func (p PluginUniqueIdentifier) String() string {
 	return string(p)
 }

+ 49 - 0
internal/types/entities/plugin_entities/identity_test.go

@@ -0,0 +1,49 @@
+package plugin_entities
+
+import (
+	"testing"
+)
+
+func TestPluginUniqueIdentifier(t *testing.T) {
+	i, err := NewPluginUniqueIdentifier("langgenius/test:1.0.0@1234567890abcdef1234567890abcdef1234567890abcdef")
+	if err != nil {
+		t.Fatalf("NewPluginUniqueIdentifier() returned an error: %v", err)
+	}
+	if i.Author() != "langgenius" {
+		t.Fatalf("Author() = %s; want langgenius", i.Author())
+	}
+	if i.PluginID() != "langgenius/test" {
+		t.Fatalf("PluginID() = %s; want langgenius/test", i.PluginID())
+	}
+	if i.Version() != "1.0.0" {
+		t.Fatalf("Version() = %s; want 1.0.0", i.Version())
+	}
+	if i.Checksum() != "1234567890abcdef1234567890abcdef1234567890abcdef" {
+		t.Fatalf("Checksum() = %s; want 1234567890abcdef1234567890abcdef1234567890abcdef", i.Checksum())
+	}
+
+	_, err = NewPluginUniqueIdentifier("test:1.0.0@1234567890abcdef1234567890abcdef1234567890abcdef")
+	if err != nil {
+		t.Fatalf("NewPluginUniqueIdentifier() returned an error: %v", err)
+	}
+
+	_, err = NewPluginUniqueIdentifier("1.0.0@1234567890abcdef1234567890abcdef1234567890abcdef")
+	if err == nil {
+		t.Fatalf("NewPluginUniqueIdentifier() returned nil error for invalid identifier")
+	}
+
+	_, err = NewPluginUniqueIdentifier("1234567890abcdef1234567890abcdef1234567890abcdef")
+	if err == nil {
+		t.Fatalf("NewPluginUniqueIdentifier() returned nil error for invalid identifier")
+	}
+
+	_, err = NewPluginUniqueIdentifier("langgenius/test:1.0.0@123456")
+	if err == nil {
+		t.Fatalf("NewPluginUniqueIdentifier() returned nil error for invalid identifier")
+	}
+
+	_, err = NewPluginUniqueIdentifier("langgenius/test:1.0.0")
+	if err == nil {
+		t.Fatalf("NewPluginUniqueIdentifier() returned nil error for invalid identifier")
+	}
+}

+ 1 - 1
internal/types/entities/plugin_entities/plugin_declaration.go

@@ -169,7 +169,7 @@ func isPluginName(fl validator.FieldLevel) bool {
 }
 
 func (p *PluginDeclaration) Identity() string {
-	return parser.MarshalPluginID(p.Name, p.Version)
+	return parser.MarshalPluginID(p.Author, p.Name, p.Version)
 }
 
 func (p *PluginDeclaration) ManifestValidate() error {

+ 5 - 2
internal/utils/parser/identity.go

@@ -2,6 +2,9 @@ package parser
 
 import "fmt"
 
-func MarshalPluginID(name string, version string) string {
-	return fmt.Sprintf("%s:%s", name, version)
+func MarshalPluginID(author string, name string, version string) string {
+	if author == "" {
+		return fmt.Sprintf("%s:%s", name, version)
+	}
+	return fmt.Sprintf("%s/%s:%s", author, name, version)
 }