generate json-schema dynamically in Go

This commit is contained in:
Joel Wetzell
2026-03-23 12:11:10 -05:00
parent 0922ece656
commit 842495f010
77 changed files with 1319 additions and 1659 deletions

View File

@@ -7,6 +7,7 @@ import (
"fmt"
"log/slog"
"github.com/google/jsonschema-go/jsonschema"
"github.com/jwetzell/showbridge-go/internal/common"
"github.com/jwetzell/showbridge-go/internal/config"
@@ -24,7 +25,18 @@ type DbSqlite struct {
func init() {
RegisterModule(ModuleRegistration{
Type: "db.sqlite",
Type: "db.sqlite",
Title: "SQLite Database",
ParamsSchema: &jsonschema.Schema{
Type: "object",
Properties: map[string]*jsonschema.Schema{
"dsn": {
Type: "string",
MinLength: jsonschema.Ptr(1),
},
},
Required: []string{"dsn"},
},
New: func(config config.ModuleConfig) (common.Module, error) {
params := config.Params

View File

@@ -9,6 +9,7 @@ import (
"net"
"net/http"
"github.com/google/jsonschema-go/jsonschema"
"github.com/jwetzell/showbridge-go/internal/common"
"github.com/jwetzell/showbridge-go/internal/config"
"github.com/jwetzell/showbridge-go/internal/processor"
@@ -55,7 +56,21 @@ func (hsrw *HTTPServerResponseWriter) Write(data []byte) (int, error) {
func init() {
RegisterModule(ModuleRegistration{
Type: "http.server",
Type: "http.server",
Title: "HTTP Server",
ParamsSchema: &jsonschema.Schema{
Type: "object",
Properties: map[string]*jsonschema.Schema{
"port": {
Title: "Port",
Type: "integer",
Minimum: jsonschema.Ptr[float64](1024),
Maximum: jsonschema.Ptr[float64](65535),
},
},
Required: []string{"port"},
AdditionalProperties: nil,
},
New: func(config config.ModuleConfig) (common.Module, error) {
params := config.Params
portNum, err := params.GetInt("port")

View File

@@ -8,6 +8,7 @@ import (
"fmt"
"log/slog"
"github.com/google/jsonschema-go/jsonschema"
"github.com/jwetzell/showbridge-go/internal/common"
"github.com/jwetzell/showbridge-go/internal/config"
"gitlab.com/gomidi/midi/v2"
@@ -26,7 +27,19 @@ type MIDIInput struct {
func init() {
RegisterModule(ModuleRegistration{
Type: "midi.input",
Type: "midi.input",
Title: "MIDI Input",
ParamsSchema: &jsonschema.Schema{
Type: "object",
Properties: map[string]*jsonschema.Schema{
"port": {
Title: "Port",
Type: "string",
},
},
Required: []string{"port"},
AdditionalProperties: nil,
},
New: func(config config.ModuleConfig) (common.Module, error) {
params := config.Params
portString, err := params.GetString("port")

View File

@@ -8,6 +8,7 @@ import (
"fmt"
"log/slog"
"github.com/google/jsonschema-go/jsonschema"
"github.com/jwetzell/showbridge-go/internal/common"
"github.com/jwetzell/showbridge-go/internal/config"
"gitlab.com/gomidi/midi/v2"
@@ -26,7 +27,19 @@ type MIDIOutput struct {
func init() {
RegisterModule(ModuleRegistration{
Type: "midi.output",
Type: "midi.output",
Title: "MIDI Output",
ParamsSchema: &jsonschema.Schema{
Type: "object",
Properties: map[string]*jsonschema.Schema{
"port": {
Title: "Port",
Type: "string",
},
},
Required: []string{"port"},
AdditionalProperties: nil,
},
New: func(config config.ModuleConfig) (common.Module, error) {
params := config.Params

View File

@@ -5,6 +5,7 @@ import (
"log/slog"
"sync"
"github.com/google/jsonschema-go/jsonschema"
"github.com/jwetzell/showbridge-go/internal/common"
"github.com/jwetzell/showbridge-go/internal/config"
)
@@ -16,8 +17,11 @@ type ModuleError struct {
}
type ModuleRegistration struct {
Type string `json:"type"`
New func(config.ModuleConfig) (common.Module, error)
Type string `json:"type"`
Title string `json:"title,omitempty"`
Description string `json:"description,omitempty"`
ParamsSchema *jsonschema.Schema `json:"paramsSchema,omitempty"`
New func(config.ModuleConfig) (common.Module, error)
}
func RegisterModule(mod ModuleRegistration) {
@@ -46,3 +50,49 @@ var (
func CreateLogger(config config.ModuleConfig) *slog.Logger {
return slog.Default().With("component", "module", "id", config.Id, "type", config.Type)
}
func GetModulesSchema() *jsonschema.Schema {
moduleRegistryMu.RLock()
defer moduleRegistryMu.RUnlock()
schema := &jsonschema.Schema{
Schema: "https://json-schema.org/draft/2020-12/schema",
ID: "https://showbridge.io/modules.schema.json",
Title: "Modules",
Description: "module configurations",
Type: "array",
}
moduleDefinitionSchemas := []*jsonschema.Schema{}
for _, mod := range ModuleRegistry {
moduleSchema := &jsonschema.Schema{
Type: "object",
Properties: map[string]*jsonschema.Schema{
"id": {
Type: "string",
MinLength: jsonschema.Ptr(1),
},
"type": {
Const: jsonschema.Ptr[any](mod.Type),
},
},
Required: []string{"id", "type"},
AdditionalProperties: nil,
}
if mod.Title != "" {
moduleSchema.Title = mod.Title
}
if mod.Description != "" {
moduleSchema.Description = mod.Description
}
if mod.ParamsSchema != nil {
moduleSchema.Properties["params"] = mod.ParamsSchema
moduleSchema.Required = append(moduleSchema.Required, "params")
}
moduleDefinitionSchemas = append(moduleDefinitionSchemas, moduleSchema)
}
schema.Items = &jsonschema.Schema{
OneOf: moduleDefinitionSchemas,
}
return schema
}

View File

@@ -7,6 +7,7 @@ import (
"log/slog"
mqtt "github.com/eclipse/paho.mqtt.golang"
"github.com/google/jsonschema-go/jsonschema"
"github.com/jwetzell/showbridge-go/internal/common"
"github.com/jwetzell/showbridge-go/internal/config"
)
@@ -25,7 +26,27 @@ type MQTTClient struct {
func init() {
RegisterModule(ModuleRegistration{
Type: "mqtt.client",
Type: "mqtt.client",
Title: "MQTT Client",
ParamsSchema: &jsonschema.Schema{
Type: "object",
Properties: map[string]*jsonschema.Schema{
"broker": {
Title: "Broker URL",
Type: "string",
},
"topic": {
Title: "Topic",
Type: "string",
},
"clientId": {
Title: "Client ID",
Type: "string",
},
},
Required: []string{"broker", "topic", "clientId"},
AdditionalProperties: nil,
},
New: func(config config.ModuleConfig) (common.Module, error) {
params := config.Params
brokerString, err := params.GetString("broker")

View File

@@ -5,6 +5,7 @@ import (
"errors"
"log/slog"
"github.com/google/jsonschema-go/jsonschema"
"github.com/jwetzell/showbridge-go/internal/common"
"github.com/jwetzell/showbridge-go/internal/config"
"github.com/jwetzell/showbridge-go/internal/processor"
@@ -24,7 +25,23 @@ type NATSClient struct {
func init() {
RegisterModule(ModuleRegistration{
Type: "nats.client",
Type: "nats.client",
Title: "NATS Client",
ParamsSchema: &jsonschema.Schema{
Type: "object",
Properties: map[string]*jsonschema.Schema{
"url": {
Title: "NATS Server URL",
Type: "string",
},
"subject": {
Title: "Subject",
Type: "string",
},
},
Required: []string{"url", "subject"},
AdditionalProperties: nil,
},
New: func(config config.ModuleConfig) (common.Module, error) {
params := config.Params
urlString, err := params.GetString("url")

View File

@@ -2,12 +2,14 @@ package module
import (
"context"
"encoding/json"
"errors"
"fmt"
"log/slog"
"net"
"time"
"github.com/google/jsonschema-go/jsonschema"
"github.com/jwetzell/showbridge-go/internal/common"
"github.com/jwetzell/showbridge-go/internal/config"
"github.com/nats-io/nats-server/v2/server"
@@ -26,7 +28,28 @@ type NATSServer struct {
func init() {
RegisterModule(ModuleRegistration{
Type: "nats.server",
Type: "nats.server",
Title: "NATS Server",
ParamsSchema: &jsonschema.Schema{
Type: "object",
Properties: map[string]*jsonschema.Schema{
"ip": {
Title: "IP",
Type: "string",
Default: json.RawMessage(`"0.0.0.0"`),
},
"port": {
Title: "Port",
Type: "integer",
Minimum: jsonschema.Ptr[float64](1024),
Maximum: jsonschema.Ptr[float64](65535),
Default: json.RawMessage(`4222`),
},
},
Required: []string{},
AdditionalProperties: nil,
},
New: func(moduleConfig config.ModuleConfig) (common.Module, error) {
params := moduleConfig.Params
portNum, err := params.GetInt("port")

View File

@@ -24,7 +24,8 @@ type PSNClient struct {
func init() {
RegisterModule(ModuleRegistration{
Type: "psn.client",
Type: "psn.client",
Title: "PosiStageNet Client",
New: func(config config.ModuleConfig) (common.Module, error) {
return &PSNClient{config: config, decoder: psn.NewDecoder(), logger: CreateLogger(config)}, nil

View File

@@ -6,6 +6,7 @@ import (
"fmt"
"log/slog"
"github.com/google/jsonschema-go/jsonschema"
"github.com/jwetzell/showbridge-go/internal/common"
"github.com/jwetzell/showbridge-go/internal/config"
"github.com/redis/go-redis/v9"
@@ -24,7 +25,23 @@ type RedisClient struct {
func init() {
RegisterModule(ModuleRegistration{
Type: "redis.client",
Type: "redis.client",
Title: "Redis Client",
ParamsSchema: &jsonschema.Schema{
Type: "object",
Properties: map[string]*jsonschema.Schema{
"host": {
Type: "string",
},
"port": {
Type: "integer",
Minimum: jsonschema.Ptr[float64](1),
Maximum: jsonschema.Ptr[float64](65535),
},
},
Required: []string{"host", "port"},
AdditionalProperties: nil,
},
New: func(config config.ModuleConfig) (common.Module, error) {
params := config.Params
hostString, err := params.GetString("host")

View File

@@ -9,6 +9,7 @@ import (
"log/slog"
"time"
"github.com/google/jsonschema-go/jsonschema"
"github.com/jwetzell/showbridge-go/internal/common"
"github.com/jwetzell/showbridge-go/internal/config"
"github.com/jwetzell/showbridge-go/internal/framer"
@@ -29,7 +30,23 @@ type SerialClient struct {
func init() {
RegisterModule(ModuleRegistration{
Type: "serial.client",
Type: "serial.client",
Title: "Serial Client",
ParamsSchema: &jsonschema.Schema{
Type: "object",
Properties: map[string]*jsonschema.Schema{
"port": {
Title: "Port",
Type: "string",
},
"baudRate": {
Title: "Baud Rate",
Type: "integer",
},
},
Required: []string{"port", "baudRate"},
AdditionalProperties: nil,
},
New: func(config config.ModuleConfig) (common.Module, error) {
params := config.Params
portString, err := params.GetString("port")

View File

@@ -2,6 +2,7 @@ package module
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
@@ -14,6 +15,7 @@ import (
"github.com/emiago/diago/media"
"github.com/emiago/sipgo"
"github.com/emiago/sipgo/sip"
"github.com/google/jsonschema-go/jsonschema"
"github.com/jwetzell/showbridge-go/internal/common"
"github.com/jwetzell/showbridge-go/internal/config"
"github.com/jwetzell/showbridge-go/internal/processor"
@@ -45,7 +47,38 @@ type sipCallContextKey string
func init() {
RegisterModule(ModuleRegistration{
Type: "sip.call.server",
Type: "sip.call.server",
Title: "SIP Call Server",
ParamsSchema: &jsonschema.Schema{
Type: "object",
Properties: map[string]*jsonschema.Schema{
"ip": {
Title: "IP",
Type: "string",
Default: json.RawMessage(`"0.0.0.0"`),
},
"port": {
Title: "Port",
Type: "integer",
Minimum: jsonschema.Ptr[float64](1024),
Maximum: jsonschema.Ptr[float64](65535),
Default: json.RawMessage(`5060`),
},
"transport": {
Title: "Transport",
Type: "string",
Enum: []any{"udp", "tcp", "ws", "udp4", "tcp4"},
Default: json.RawMessage(`"udp"`),
},
"userAgent": {
Title: "User Agent",
Type: "string",
Default: json.RawMessage(`"showbridge"`),
},
},
Required: []string{},
AdditionalProperties: nil,
},
New: func(moduleConfig config.ModuleConfig) (common.Module, error) {
params := moduleConfig.Params
portNum, err := params.GetInt("port")

View File

@@ -2,6 +2,7 @@ package module
import (
"context"
"encoding/json"
"errors"
"fmt"
"io"
@@ -15,6 +16,7 @@ import (
"github.com/emiago/diago/media"
"github.com/emiago/sipgo"
"github.com/emiago/sipgo/sip"
"github.com/google/jsonschema-go/jsonschema"
"github.com/jwetzell/showbridge-go/internal/common"
"github.com/jwetzell/showbridge-go/internal/config"
"github.com/jwetzell/showbridge-go/internal/processor"
@@ -45,7 +47,44 @@ type SIPDTMFCall struct {
func init() {
RegisterModule(ModuleRegistration{
Type: "sip.dtmf.server",
Type: "sip.dtmf.server",
Title: "SIP DTMF Server",
ParamsSchema: &jsonschema.Schema{
Type: "object",
Properties: map[string]*jsonschema.Schema{
"ip": {
Title: "IP",
Type: "string",
Default: json.RawMessage(`"0.0.0.0"`),
},
"port": {
Title: "Port",
Type: "integer",
Minimum: jsonschema.Ptr[float64](1024),
Maximum: jsonschema.Ptr[float64](65535),
Default: json.RawMessage(`5060`),
},
"transport": {
Title: "Transport",
Type: "string",
Enum: []any{"udp", "tcp", "ws", "udp4", "tcp4"},
Default: json.RawMessage(`"udp"`),
},
"userAgent": {
Title: "User Agent",
Type: "string",
Default: json.RawMessage(`"showbridge"`),
},
"separator": {
Title: "DTMF Separator",
Type: "string",
MinLength: jsonschema.Ptr(1),
MaxLength: jsonschema.Ptr(1),
},
},
Required: []string{"separator"},
AdditionalProperties: nil,
},
New: func(moduleConfig config.ModuleConfig) (common.Module, error) {
params := moduleConfig.Params

View File

@@ -8,6 +8,7 @@ import (
"net"
"time"
"github.com/google/jsonschema-go/jsonschema"
"github.com/jwetzell/showbridge-go/internal/common"
"github.com/jwetzell/showbridge-go/internal/config"
"github.com/jwetzell/showbridge-go/internal/framer"
@@ -26,7 +27,30 @@ type TCPClient struct {
func init() {
RegisterModule(ModuleRegistration{
Type: "net.tcp.client",
Type: "net.tcp.client",
Title: "TCP Client",
ParamsSchema: &jsonschema.Schema{
Type: "object",
Properties: map[string]*jsonschema.Schema{
"host": {
Title: "Host",
Type: "string",
},
"port": {
Title: "Port",
Type: "integer",
Minimum: jsonschema.Ptr[float64](1),
Maximum: jsonschema.Ptr[float64](65535),
},
"framing": {
Title: "Framing Method",
Type: "string",
Enum: []any{"LF", "CR", "CRLF", "SLIP", "RAW"},
},
},
Required: []string{"host", "port", "framing"},
AdditionalProperties: nil,
},
New: func(config config.ModuleConfig) (common.Module, error) {
params := config.Params
hostString, err := params.GetString("host")

View File

@@ -2,6 +2,7 @@ package module
import (
"context"
"encoding/json"
"errors"
"fmt"
"log/slog"
@@ -11,6 +12,7 @@ import (
"syscall"
"time"
"github.com/google/jsonschema-go/jsonschema"
"github.com/jwetzell/showbridge-go/internal/common"
"github.com/jwetzell/showbridge-go/internal/config"
"github.com/jwetzell/showbridge-go/internal/framer"
@@ -32,7 +34,31 @@ type TCPServer struct {
func init() {
RegisterModule(ModuleRegistration{
Type: "net.tcp.server",
Type: "net.tcp.server",
Title: "TCP Server",
ParamsSchema: &jsonschema.Schema{
Type: "object",
Properties: map[string]*jsonschema.Schema{
"ip": {
Title: "IP",
Type: "string",
Default: json.RawMessage(`"0.0.0.0"`),
},
"port": {
Title: "Port",
Type: "integer",
Minimum: jsonschema.Ptr[float64](1024),
Maximum: jsonschema.Ptr[float64](65535),
},
"framing": {
Title: "Framing Method",
Type: "string",
Enum: []any{"LF", "CR", "CRLF", "SLIP", "RAW"},
},
},
Required: []string{"port", "framing"},
AdditionalProperties: nil,
},
New: func(moduleConfig config.ModuleConfig) (common.Module, error) {
params := moduleConfig.Params
portNum, err := params.GetInt("port")

View File

@@ -7,6 +7,7 @@ import (
"log/slog"
"time"
"github.com/google/jsonschema-go/jsonschema"
"github.com/jwetzell/showbridge-go/internal/common"
"github.com/jwetzell/showbridge-go/internal/config"
)
@@ -23,7 +24,20 @@ type TimeInterval struct {
func init() {
RegisterModule(ModuleRegistration{
Type: "time.interval",
Type: "time.interval",
Title: "Interval",
ParamsSchema: &jsonschema.Schema{
Type: "object",
Properties: map[string]*jsonschema.Schema{
"duration": {
Title: "Duration",
Type: "integer",
Description: "Interval duration in milliseconds",
},
},
Required: []string{"duration"},
AdditionalProperties: nil,
},
New: func(config config.ModuleConfig) (common.Module, error) {
params := config.Params

View File

@@ -7,6 +7,7 @@ import (
"log/slog"
"time"
"github.com/google/jsonschema-go/jsonschema"
"github.com/jwetzell/showbridge-go/internal/common"
"github.com/jwetzell/showbridge-go/internal/config"
)
@@ -23,7 +24,20 @@ type TimeTimer struct {
func init() {
RegisterModule(ModuleRegistration{
Type: "time.timer",
Type: "time.timer",
Title: "Timer",
ParamsSchema: &jsonschema.Schema{
Type: "object",
Properties: map[string]*jsonschema.Schema{
"duration": {
Title: "Duration",
Type: "integer",
Description: "Interval duration in milliseconds",
},
},
Required: []string{"duration"},
AdditionalProperties: nil,
},
New: func(config config.ModuleConfig) (common.Module, error) {
params := config.Params

View File

@@ -7,6 +7,7 @@ import (
"log/slog"
"net"
"github.com/google/jsonschema-go/jsonschema"
"github.com/jwetzell/showbridge-go/internal/common"
"github.com/jwetzell/showbridge-go/internal/config"
)
@@ -24,7 +25,25 @@ type UDPClient struct {
func init() {
RegisterModule(ModuleRegistration{
Type: "net.udp.client",
Type: "net.udp.client",
Title: "UDP Client",
ParamsSchema: &jsonschema.Schema{
Type: "object",
Properties: map[string]*jsonschema.Schema{
"host": {
Title: "Host",
Type: "string",
},
"port": {
Title: "Port",
Type: "integer",
Minimum: jsonschema.Ptr[float64](1),
Maximum: jsonschema.Ptr[float64](65535),
},
},
Required: []string{"host", "port"},
AdditionalProperties: nil,
},
New: func(config config.ModuleConfig) (common.Module, error) {
params := config.Params
hostString, err := params.GetString("host")

View File

@@ -8,6 +8,7 @@ import (
"net"
"time"
"github.com/google/jsonschema-go/jsonschema"
"github.com/jwetzell/showbridge-go/internal/common"
"github.com/jwetzell/showbridge-go/internal/config"
)
@@ -24,7 +25,25 @@ type UDPMulticast struct {
func init() {
RegisterModule(ModuleRegistration{
Type: "net.udp.multicast",
Type: "net.udp.multicast",
Title: "UDP Multicast",
ParamsSchema: &jsonschema.Schema{
Type: "object",
Properties: map[string]*jsonschema.Schema{
"ip": {
Title: "IP",
Type: "string",
},
"port": {
Title: "Port",
Type: "integer",
Minimum: jsonschema.Ptr[float64](1024),
Maximum: jsonschema.Ptr[float64](65535),
},
},
Required: []string{"ip", "port"},
AdditionalProperties: nil,
},
New: func(moduleConfig config.ModuleConfig) (common.Module, error) {
params := moduleConfig.Params
ipString, err := params.GetString("ip")

View File

@@ -8,6 +8,7 @@ import (
"net"
"time"
"github.com/google/jsonschema-go/jsonschema"
"github.com/jwetzell/showbridge-go/internal/common"
"github.com/jwetzell/showbridge-go/internal/config"
)
@@ -24,7 +25,25 @@ type UDPServer struct {
func init() {
RegisterModule(ModuleRegistration{
Type: "net.udp.server",
Type: "net.udp.server",
Title: "UDP Server",
ParamsSchema: &jsonschema.Schema{
Type: "object",
Properties: map[string]*jsonschema.Schema{
"ip": {
Title: "IP",
Type: "string",
},
"port": {
Title: "Port",
Type: "integer",
Minimum: jsonschema.Ptr[float64](1024),
Maximum: jsonschema.Ptr[float64](65535),
},
},
Required: []string{"ip", "port"},
AdditionalProperties: nil,
},
New: func(moduleConfig config.ModuleConfig) (common.Module, error) {
params := moduleConfig.Params
portNum, err := params.GetInt("port")