浏览代码

fix: missing endpoint declarations

Yeuoly 9 月之前
父节点
当前提交
ba88f6c01d

+ 17 - 0
internal/core/plugin_packager/decoder/decoder.go

@@ -126,6 +126,23 @@ func (p *PluginDecoderHelper) Manifest(decoder PluginDecoder) (plugin_entities.P
 			return plugin_entities.PluginDeclaration{}, errors.Join(err, fmt.Errorf("failed to unmarshal plugin file: %s", endpoint))
 		}
 
+		// read detailed endpoints
+		endpoints_files := plugin_dec.EndpointFiles
+
+		for _, endpoint_file := range endpoints_files {
+			endpoint_file_content, err := decoder.ReadFile(endpoint_file)
+			if err != nil {
+				return plugin_entities.PluginDeclaration{}, errors.Join(err, fmt.Errorf("failed to read endpoint file: %s", endpoint_file))
+			}
+
+			endpoint_file_dec, err := parser.UnmarshalYamlBytes[plugin_entities.EndpointDeclaration](endpoint_file_content)
+			if err != nil {
+				return plugin_entities.PluginDeclaration{}, errors.Join(err, fmt.Errorf("failed to unmarshal endpoint file: %s", endpoint_file))
+			}
+
+			plugin_dec.Endpoints = append(plugin_dec.Endpoints, endpoint_file_dec)
+		}
+
 		dec.Endpoint = &plugin_dec
 	}
 

+ 68 - 1
internal/types/entities/plugin_entities/endpoint_declaration.go

@@ -1,8 +1,11 @@
 package plugin_entities
 
 import (
+	"encoding/json"
+
 	"github.com/go-playground/validator/v10"
 	"github.com/langgenius/dify-plugin-daemon/internal/types/validators"
+	"gopkg.in/yaml.v3"
 )
 
 type EndpointMethod string
@@ -40,5 +43,69 @@ type EndpointDeclaration struct {
 }
 
 type EndpointProviderDeclaration struct {
-	Settings []ProviderConfig `json:"settings" validate:"omitempty,dive"`
+	Settings      []ProviderConfig      `json:"settings" yaml:"settings" validate:"omitempty,dive"`
+	Endpoints     []EndpointDeclaration `json:"endpoints" yaml:"endpoint_declarations" validate:"omitempty,dive"`
+	EndpointFiles []string              `json:"-" yaml:"-"`
+}
+
+func (e *EndpointProviderDeclaration) UnmarshalYAML(node *yaml.Node) error {
+	type alias EndpointProviderDeclaration
+
+	var temp struct {
+		alias     `yaml:",inline"`
+		Endpoints yaml.Node `yaml:"endpoints"`
+	}
+
+	if err := node.Decode(&temp); err != nil {
+		return err
+	}
+
+	e.Settings = temp.Settings
+
+	if temp.Endpoints.Kind == yaml.SequenceNode {
+		for _, node := range temp.Endpoints.Content {
+			if node.Kind == yaml.ScalarNode {
+				e.EndpointFiles = append(e.EndpointFiles, node.Value)
+			} else {
+				var declaration EndpointDeclaration
+				if err := node.Decode(&declaration); err != nil {
+					return nil
+				}
+				e.Endpoints = append(e.Endpoints, declaration)
+			}
+		}
+	}
+
+	return nil
+}
+
+func (e *EndpointProviderDeclaration) UnmarshalJSON(data []byte) error {
+	type alias EndpointProviderDeclaration
+
+	var temp struct {
+		alias
+		Endpoints json.RawMessage `json:"endpoints"`
+	}
+
+	if err := json.Unmarshal(data, &temp); err != nil {
+		return err
+	}
+
+	*e = EndpointProviderDeclaration(temp.alias)
+
+	var raw_endpoints []json.RawMessage
+	if err := json.Unmarshal(temp.Endpoints, &raw_endpoints); err != nil {
+		return err
+	}
+
+	for _, raw_endpoint := range raw_endpoints {
+		var declaration EndpointDeclaration
+		if err := json.Unmarshal(raw_endpoint, &declaration); err != nil {
+			e.EndpointFiles = append(e.EndpointFiles, string(raw_endpoint))
+		} else {
+			e.Endpoints = append(e.Endpoints, declaration)
+		}
+	}
+
+	return nil
 }

+ 142 - 0
internal/types/entities/plugin_entities/endpoint_declaration_test.go

@@ -0,0 +1,142 @@
+package plugin_entities
+
+import (
+	"testing"
+
+	"github.com/langgenius/dify-plugin-daemon/internal/utils/parser"
+)
+
+func TestUnmarshalEndpointDeclarationFromYaml(t *testing.T) {
+	const data = `settings:
+  - name: api_key
+    type: secret-input
+    required: true
+    label:
+      en_US: API key
+      zh_Hans: API key
+      pt_BR: API key
+    placeholder:
+      en_US: Please input your API key
+      zh_Hans: 请输入你的 API key
+      pt_BR: Please input your API key
+endpoints:
+  - endpoints/duck.yaml
+  - endpoints/neko.yaml
+`
+
+	dec, err := parser.UnmarshalYamlBytes[EndpointProviderDeclaration]([]byte(data))
+	if err != nil {
+		t.Fatalf("Failed to unmarshal EndpointProviderDeclaration: %v", err)
+	}
+
+	if len(dec.EndpointFiles) != 2 {
+		t.Fatalf("Failed to unmarshal EndpointProviderDeclaration: %v", err)
+	}
+}
+
+func TestUnmarshalEndpointDeclarationFromYaml2(t *testing.T) {
+	const data = `settings:
+  - name: api_key
+    type: secret-input
+    required: true
+    label:
+      en_US: API key
+      zh_Hans: API key
+      pt_BR: API key
+    placeholder:
+      en_US: Please input your API key
+      zh_Hans: 请输入你的 API key
+      pt_BR: Please input your API key
+endpoints:
+  - path: "/duck/<app_id>"
+    method: "GET"`
+
+	dec, err := parser.UnmarshalYamlBytes[EndpointProviderDeclaration]([]byte(data))
+	if err != nil {
+		t.Fatalf("Failed to unmarshal EndpointProviderDeclaration: %v", err)
+	}
+
+	if len(dec.Endpoints) != 1 {
+		t.Fatalf("Failed to unmarshal EndpointProviderDeclaration: %v", err)
+	}
+}
+
+func TestUnmarshalEndpointDeclarationFromJSON(t *testing.T) {
+	const data = `{
+		"settings": [
+			{
+				"name": "api_key",
+				"type": "secret-input",
+				"required": true,
+				"label": {
+					"en_US": "API key",
+					"zh_Hans": "API key",
+					"pt_BR": "API key"
+				},
+				"placeholder": {
+					"en_US": "Please input your API key",
+					"zh_Hans": "请输入你的 API key",
+					"pt_BR": "Please input your API key"
+				}
+			}
+		],
+		"endpoints": [
+			"endpoints/duck.yaml",
+			"endpoints/neko.yaml"
+		]
+	}`
+
+	dec, err := parser.UnmarshalJsonBytes[EndpointProviderDeclaration]([]byte(data))
+	if err != nil {
+		t.Fatalf("Failed to unmarshal EndpointProviderDeclaration from JSON: %v", err)
+	}
+
+	if len(dec.EndpointFiles) != 2 {
+		t.Fatalf("Failed to unmarshal EndpointProviderDeclaration from JSON: expected 1 endpoint, got %d", len(dec.Endpoints))
+	}
+
+	if len(dec.Settings) != 1 {
+		t.Fatalf("Failed to unmarshal EndpointProviderDeclaration from JSON: expected 1 setting, got %d", len(dec.Settings))
+	}
+}
+
+func TestUnmarshalEndpointDeclarationFromJSON2(t *testing.T) {
+	const data = `{
+		"settings": [
+			{
+				"name": "api_key",
+				"type": "secret-input",
+				"required": true,
+				"label": {
+					"en_US": "API key",
+					"zh_Hans": "API key",
+					"pt_BR": "API key"
+				},
+				"placeholder": {
+					"en_US": "Please input your API key",
+					"zh_Hans": "请输入你的 API key",
+					"pt_BR": "Please input your API key"
+				}
+			}
+		],
+		"endpoints": [
+			{
+				"path": "/duck/<app_id>",
+				"method": "GET"
+			}
+		]
+	}`
+
+	dec, err := parser.UnmarshalJsonBytes[EndpointProviderDeclaration]([]byte(data))
+	if err != nil {
+		t.Fatalf("Failed to unmarshal EndpointProviderDeclaration from JSON: %v", err)
+	}
+
+	if len(dec.Endpoints) != 1 {
+		t.Fatalf("Failed to unmarshal EndpointProviderDeclaration from JSON: expected 1 endpoint, got %d", len(dec.Endpoints))
+	}
+
+	if len(dec.Settings) != 1 {
+		t.Fatalf("Failed to unmarshal EndpointProviderDeclaration from JSON: expected 1 setting, got %d", len(dec.Settings))
+	}
+}