Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 1 addition & 9 deletions admin/api/plugins.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,7 @@ func (api *API) CreatePlugin(w http.ResponseWriter, r *http.Request) {
return
}

p, err := model.Plugin()
api.assert(err)
model.Config = utils.Must(p.MarshalConfig())
err = api.db.PluginsWS.Insert(r.Context(), &model)
err := api.db.PluginsWS.Insert(r.Context(), &model)
api.assert(err)

api.json(201, w, model)
Expand All @@ -73,11 +70,6 @@ func (api *API) UpdatePlugin(w http.ResponseWriter, r *http.Request) {
return
}

p, err := model.Plugin()
api.assert(err)

model.Config = utils.Must(p.MarshalConfig())

model.ID = id
err = api.db.PluginsWS.Update(r.Context(), model)
api.assert(err)
Expand Down
50 changes: 23 additions & 27 deletions db/entities/plugin.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package entities

import (
"database/sql/driver"
"encoding/json"
"errors"
"fmt"
Expand Down Expand Up @@ -44,11 +45,11 @@ func (m *Plugin) Validate() error {
}

// validate plugin configuration
p, err := m.Plugin()
p, err := m.ToPlugin()
if err != nil {
return err
}
if err = p.ValidateConfig(); err != nil {
if err = p.ValidateConfig(m.Config); err != nil {
if e, ok := err.(*errs.ValidateError); ok {
e.Fields = map[string]interface{}{
"config": e.Fields,
Expand All @@ -57,6 +58,13 @@ func (m *Plugin) Validate() error {
}
return err
}

err = p.Init(m.Config)
if err != nil {
return err
}
m.Config = p.GetConfig()

return nil
}

Expand All @@ -69,44 +77,32 @@ func (m *Plugin) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, (*alias)(m))
}

func (m *Plugin) Plugin() (plugin.Plugin, error) {
r := plugin.GetRegistration(m.Name)
if r == nil {
func (m *Plugin) ToPlugin() (plugin.Plugin, error) {
executor, ok := plugin.New(m.Name)
if !ok {
return nil, fmt.Errorf("unknown plugin name: '%s'", m.Name)
}

executor, err := r.New(m.Config)
if err != nil {
return nil, err
}
return executor, nil
}

type PluginConfiguration json.RawMessage
type PluginConfiguration map[string]interface{}

func (m PluginConfiguration) MarshalYAML() (interface{}, error) {
if len(m) == 0 {
return nil, nil
}
data := make(map[string]interface{})
err := json.Unmarshal(m, &data)
if err != nil {
return nil, err
}
return data, nil
func (m *PluginConfiguration) Scan(src interface{}) error {
return json.Unmarshal(src.([]byte), m)
}

func (m PluginConfiguration) MarshalJSON() ([]byte, error) {
func (m PluginConfiguration) Value() (driver.Value, error) {
if m == nil {
return []byte("null"), nil
return []byte(`{}`), nil
}
return m, nil
return json.Marshal(m)
}

func (m *PluginConfiguration) UnmarshalJSON(data []byte) error {
if m == nil {
return errors.New("json.RawMessage: UnmarshalJSON on nil pointer")
v := make(map[string]interface{})
if err := json.Unmarshal(data, &v); err != nil {
return err
}
*m = append((*m)[0:0], data...)
*m = v
return nil
}
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ require (
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,8 @@ github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mfridman/tparse v0.18.0 h1:wh6dzOKaIwkUGyKgOntDW4liXSo37qg5AXbIhkMV3vE=
github.com/mfridman/tparse v0.18.0/go.mod h1:gEvqZTuCgEhPbYk/2lS3Kcxg1GmTxxU7kTC8DvP0i/A=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
Expand Down
63 changes: 62 additions & 1 deletion openapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -936,7 +936,7 @@ components:
nullable: true
config:
type: object
nullable: true
default: { }
metadata:
$ref: "#/components/schemas/Metadata"
created_at:
Expand Down Expand Up @@ -988,3 +988,64 @@ components:
required:
- quota
- period


WasmPluginConfiguration:
description: "The wasm plugin configuration"
type: object
properties:
file:
description: "The file path to the wasm file."
type: string
envs:
description: "Environments that can be used in wasm."
type: object
additionalProperties:
type: string
default: { }
required:
- file

FunctionPluginConfiguration:
description: "The function plugin configuration"
type: object
properties:
function:
description: "The function content."
type: string
maxLength: 1048576
required:
- function

WebhookxSignaturePluginConfiguration:
description: "The webhookx-signature plugin configuration"
type: object
properties:
signing_secret:
description: "The signature secret."
type: string
minLength: 1

JsonschemaValidatorPluginConfiguration:
description: "The jsonschema-validator plugin configuration"
type: object
properties:
draft:
type: string
enum: ["6"]
default: "6"
default_schema:
description: "The default schema"
type: string
format: jsonschema
maxLength: 1048576
schemas:
description: "Defines event's schema."
type: object
additionalProperties:
type: object
properties:
schema:
type: string
format: jsonschema
maxLength: 1048576
10 changes: 0 additions & 10 deletions pkg/declarative/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,6 @@ func (cfg *Configuration) Validate() error {
if err := model.Validate(); err != nil {
return err
}
p, err := model.Plugin()
if err != nil {
return err
}
model.Config = utils.Must(p.MarshalConfig())
}
}

Expand All @@ -67,11 +62,6 @@ func (cfg *Configuration) Validate() error {
if err := model.Validate(); err != nil {
return err
}
p, err := model.Plugin()
if err != nil {
return err
}
model.Config = utils.Must(p.MarshalConfig())
}
}

Expand Down
26 changes: 20 additions & 6 deletions pkg/openapi/openapi.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,31 @@
package openapi

import (
"context"
"encoding/json"
"errors"
"github.com/getkin/kin-openapi/openapi3"
"github.com/webhookx-io/webhookx/pkg/errs"
"strconv"
)

type FormatValidatorFunc[T any] func(T) error

func (fn FormatValidatorFunc[T]) Validate(value T) error { return fn(value) }

func init() {
openapi3.DefineStringFormatValidator("jsonschema", FormatValidatorFunc[string](func(s string) error {
schema := &openapi3.Schema{}
if err := schema.UnmarshalJSON([]byte(s)); err != nil {
return err
}
if err := schema.Validate(context.TODO(), openapi3.EnableSchemaFormatValidation()); err != nil {
return err
}
return nil
}))
}

func SetDefaults(schema *openapi3.Schema, defaults map[string]interface{}) error {
data := make(map[string]interface{})
_ = schema.VisitJSON(data,
Expand Down Expand Up @@ -122,13 +140,9 @@ func insertError(current map[string]interface{}, i int, paths []string, err *ope
if isIndex {
ensureArray(current, "", index)
arr := current[""].([]interface{})
if err.Origin == nil {
arr[index] = formatError(err)
}
arr[index] = formatError(err)
} else {
if err.Origin == nil {
current[key] = formatError(err)
}
current[key] = formatError(err)
}
return
}
Expand Down
53 changes: 53 additions & 0 deletions pkg/plugin/base.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package plugin

import (
"context"
"github.com/getkin/kin-openapi/openapi3"
"github.com/mitchellh/mapstructure"
"github.com/webhookx-io/webhookx/pkg/openapi"
"github.com/webhookx-io/webhookx/utils"
)

// Configuration plugin configuration
type Configuration interface {
Schema() *openapi3.Schema
}

type BasePlugin[T Configuration] struct {
Config T
}

func (p *BasePlugin[T]) Init(config map[string]interface{}) error {
decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
TagName: "json",
Result: &p.Config,
})
if err != nil {
return err
}
return decoder.Decode(config)
}

func (p *BasePlugin[T]) GetConfig() map[string]interface{} {
m, err := utils.StructToMap(p.Config)
if err != nil {
panic(err)
}
return m
}

func (p *BasePlugin[T]) ValidateConfig(config map[string]interface{}) error {
err := openapi.Validate(p.Config.Schema(), config)
if err != nil {
return err
}
return nil
}

func (p *BasePlugin[T]) ExecuteOutbound(ctx context.Context, outbound *Outbound) error {
panic("not implemented")
}

func (p *BasePlugin[T]) ExecuteInbound(ctx context.Context, inbound *Inbound) (InboundResult, error) {
panic("not implemented")
}
29 changes: 29 additions & 0 deletions pkg/plugin/base_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package plugin

import (
"context"
"github.com/getkin/kin-openapi/openapi3"
"github.com/stretchr/testify/assert"
"testing"
)

type config struct {
}

func (c config) Schema() *openapi3.Schema {
return &openapi3.Schema{}
}

type MyPlugin struct {
BasePlugin[config]
}

func (m MyPlugin) Name() string {
panic("my-plugin")
}

func Test(t *testing.T) {
myPlugin := &MyPlugin{}
assert.PanicsWithValue(t, "not implemented", func() { myPlugin.ExecuteInbound(context.TODO(), nil) })
assert.PanicsWithValue(t, "not implemented", func() { myPlugin.ExecuteOutbound(context.TODO(), nil) })
}
Loading
Loading