move schema to an internal package

This commit is contained in:
Joel Wetzell
2026-03-23 12:51:26 -05:00
parent 256eeac6a8
commit 9a50ca8cfe
12 changed files with 223 additions and 159 deletions

10
api.go
View File

@@ -10,8 +10,8 @@ import (
"github.com/jwetzell/showbridge-go/internal/config" "github.com/jwetzell/showbridge-go/internal/config"
"github.com/jwetzell/showbridge-go/internal/module" "github.com/jwetzell/showbridge-go/internal/module"
"github.com/jwetzell/showbridge-go/internal/processor"
"github.com/jwetzell/showbridge-go/internal/route" "github.com/jwetzell/showbridge-go/internal/route"
"github.com/jwetzell/showbridge-go/internal/schema"
) )
func (r *Router) startAPIServer(config config.ApiConfig) { func (r *Router) startAPIServer(config config.ApiConfig) {
@@ -135,7 +135,7 @@ func (r *Router) handleConfigHTTP(w http.ResponseWriter, req *http.Request) {
func handleConfigSchema(w http.ResponseWriter, req *http.Request) { func handleConfigSchema(w http.ResponseWriter, req *http.Request) {
switch req.Method { switch req.Method {
case http.MethodGet: case http.MethodGet:
schemaJSON, err := json.Marshal(config.ConfigSchema) schemaJSON, err := json.Marshal(schema.ConfigSchema)
if err != nil { if err != nil {
http.Error(w, "Internal server error", http.StatusInternalServerError) http.Error(w, "Internal server error", http.StatusInternalServerError)
return return
@@ -157,7 +157,7 @@ func handleConfigSchema(w http.ResponseWriter, req *http.Request) {
func handleRoutesSchema(w http.ResponseWriter, req *http.Request) { func handleRoutesSchema(w http.ResponseWriter, req *http.Request) {
switch req.Method { switch req.Method {
case http.MethodGet: case http.MethodGet:
schemaJSON, err := json.Marshal(config.RoutesConfigSchema) schemaJSON, err := json.Marshal(schema.RoutesConfigSchema)
if err != nil { if err != nil {
http.Error(w, "Internal server error", http.StatusInternalServerError) http.Error(w, "Internal server error", http.StatusInternalServerError)
return return
@@ -179,7 +179,7 @@ func handleRoutesSchema(w http.ResponseWriter, req *http.Request) {
func handleModulesSchema(w http.ResponseWriter, req *http.Request) { func handleModulesSchema(w http.ResponseWriter, req *http.Request) {
switch req.Method { switch req.Method {
case http.MethodGet: case http.MethodGet:
schemaJSON, err := json.Marshal(module.GetModulesSchema()) schemaJSON, err := json.Marshal(schema.GetModulesSchema())
if err != nil { if err != nil {
http.Error(w, "Internal server error", http.StatusInternalServerError) http.Error(w, "Internal server error", http.StatusInternalServerError)
return return
@@ -201,7 +201,7 @@ func handleModulesSchema(w http.ResponseWriter, req *http.Request) {
func handleProcessorsSchema(w http.ResponseWriter, req *http.Request) { func handleProcessorsSchema(w http.ResponseWriter, req *http.Request) {
switch req.Method { switch req.Method {
case http.MethodGet: case http.MethodGet:
schemaJSON, err := json.Marshal(processor.GetProcessorsSchema()) schemaJSON, err := json.Marshal(schema.GetProcessorsSchema())
if err != nil { if err != nil {
http.Error(w, "Internal server error", http.StatusInternalServerError) http.Error(w, "Internal server error", http.StatusInternalServerError)
return return

View File

@@ -1,28 +1,6 @@
package config package config
import (
"encoding/json"
"github.com/google/jsonschema-go/jsonschema"
)
type ApiConfig struct { type ApiConfig struct {
Enabled bool `json:"enabled"` Enabled bool `json:"enabled"`
Port int `json:"port"` Port int `json:"port"`
} }
var ApiConfigSchema = jsonschema.Schema{
Type: "object",
Properties: map[string]*jsonschema.Schema{
"enabled": {
Type: "boolean",
Description: "Whether the API server is enabled",
Default: json.RawMessage(`false`),
},
"port": {
Type: "integer",
Description: "Port for the API server to listen on",
},
},
Required: []string{"port"},
}

View File

@@ -1,28 +1,7 @@
package config package config
import (
"github.com/google/jsonschema-go/jsonschema"
)
type Config struct { type Config struct {
Api ApiConfig `json:"api"` Api ApiConfig `json:"api"`
Modules []ModuleConfig `json:"modules"` Modules []ModuleConfig `json:"modules"`
Routes []RouteConfig `json:"routes"` Routes []RouteConfig `json:"routes"`
} }
var ConfigSchema = jsonschema.Schema{
Schema: "https://json-schema.org/draft/2020-12/schema",
ID: "https://showbridge.io/config.schema.json",
Title: "Config",
Description: "showbridge configuration",
Type: "object",
Properties: map[string]*jsonschema.Schema{
"api": &ApiConfigSchema,
"modules": {
Ref: "https://showbridge.io/modules.schema.json",
},
"routes": {
Ref: "https://showbridge.io/routes.schema.json",
},
},
}

View File

@@ -1,29 +1,6 @@
package config package config
import "github.com/google/jsonschema-go/jsonschema"
type RouteConfig struct { type RouteConfig struct {
Input string `json:"input"` Input string `json:"input"`
Processors []ProcessorConfig `json:"processors"` Processors []ProcessorConfig `json:"processors"`
} }
var RoutesConfigSchema = jsonschema.Schema{
Schema: "https://json-schema.org/draft/2020-12/schema",
ID: "https://showbridge.io/routes.schema.json",
Title: "Routes",
Description: "route configurations",
Type: "array",
Items: &jsonschema.Schema{
Type: "object",
Properties: map[string]*jsonschema.Schema{
"input": {
Type: "string",
MinLength: jsonschema.Ptr(1),
},
"processors": {
Ref: "https://showbridge.io/processors.schema.json",
},
},
Required: []string{"input"},
},
}

View File

@@ -50,49 +50,3 @@ var (
func CreateLogger(config config.ModuleConfig) *slog.Logger { func CreateLogger(config config.ModuleConfig) *slog.Logger {
return slog.Default().With("component", "module", "id", config.Id, "type", config.Type) 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

@@ -45,45 +45,3 @@ var (
processorRegistryMu sync.RWMutex processorRegistryMu sync.RWMutex
ProcessorRegistry = make(map[string]ProcessorRegistration) ProcessorRegistry = make(map[string]ProcessorRegistration)
) )
func GetProcessorsSchema() *jsonschema.Schema {
processorRegistryMu.RLock()
defer processorRegistryMu.RUnlock()
schema := &jsonschema.Schema{
Schema: "https://json-schema.org/draft/2020-12/schema",
ID: "https://showbridge.io/processors.schema.json",
Title: "Processors",
Description: "processor configurations",
Type: "array",
}
processorDefinitionSchemas := []*jsonschema.Schema{}
for _, proc := range ProcessorRegistry {
processorSchema := &jsonschema.Schema{
Type: "object",
Properties: map[string]*jsonschema.Schema{
"type": {
Const: jsonschema.Ptr[any](proc.Type),
},
},
Required: []string{"type"},
AdditionalProperties: nil,
}
if proc.Title != "" {
processorSchema.Title = proc.Title
}
if proc.Description != "" {
processorSchema.Description = proc.Description
}
if proc.ParamsSchema != nil {
processorSchema.Properties["params"] = proc.ParamsSchema
processorSchema.Required = append(processorSchema.Required, "params")
}
processorDefinitionSchemas = append(processorDefinitionSchemas, processorSchema)
}
schema.Items = &jsonschema.Schema{
OneOf: processorDefinitionSchemas,
}
return schema
}

26
internal/schema/api.go Normal file
View File

@@ -0,0 +1,26 @@
package schema
import (
"encoding/json"
"github.com/google/jsonschema-go/jsonschema"
)
var ApiConfigSchema = jsonschema.Schema{
Type: "object",
Properties: map[string]*jsonschema.Schema{
"enabled": {
Type: "boolean",
Description: "Whether the API server is enabled",
Default: json.RawMessage(`false`),
},
"port": {
Type: "integer",
Description: "Port for the API server to listen on",
Minimum: jsonschema.Ptr[float64](1024),
Maximum: jsonschema.Ptr[float64](65535),
Default: json.RawMessage(`8080`),
},
},
Required: []string{"port"},
}

45
internal/schema/config.go Normal file
View File

@@ -0,0 +1,45 @@
package schema
import (
"encoding/json"
"github.com/google/jsonschema-go/jsonschema"
"github.com/jwetzell/showbridge-go/internal/config"
)
var ConfigSchema = jsonschema.Schema{
Schema: "https://json-schema.org/draft/2020-12/schema",
ID: "https://showbridge.io/config.schema.json",
Title: "Config",
Description: "showbridge configuration",
Type: "object",
Properties: map[string]*jsonschema.Schema{
"api": &ApiConfigSchema,
"modules": {
Ref: "https://showbridge.io/modules.schema.json",
},
"routes": {
Ref: "https://showbridge.io/routes.schema.json",
},
},
}
func ValidateConfig(config config.Config) error {
resolvedSchema, err := GetResolvedConfigSchema()
if err != nil {
return err
}
jsonBytes, err := json.Marshal(config)
if err != nil {
return err
}
jsonMap := make(map[string]any)
err = json.Unmarshal(jsonBytes, &jsonMap)
if err != nil {
return err
}
return resolvedSchema.Validate(jsonMap)
}

View File

@@ -0,0 +1,50 @@
package schema
import (
"github.com/google/jsonschema-go/jsonschema"
"github.com/jwetzell/showbridge-go/internal/module"
)
func GetModulesSchema() *jsonschema.Schema {
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 module.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

@@ -0,0 +1,46 @@
package schema
import (
"github.com/google/jsonschema-go/jsonschema"
"github.com/jwetzell/showbridge-go/internal/processor"
)
func GetProcessorsSchema() *jsonschema.Schema {
schema := &jsonschema.Schema{
Schema: "https://json-schema.org/draft/2020-12/schema",
ID: "https://showbridge.io/processors.schema.json",
Title: "Processors",
Description: "processor configurations",
Type: "array",
}
processorDefinitionSchemas := []*jsonschema.Schema{}
for _, proc := range processor.ProcessorRegistry {
processorSchema := &jsonschema.Schema{
Type: "object",
Properties: map[string]*jsonschema.Schema{
"type": {
Const: jsonschema.Ptr[any](proc.Type),
},
},
Required: []string{"type"},
AdditionalProperties: nil,
}
if proc.Title != "" {
processorSchema.Title = proc.Title
}
if proc.Description != "" {
processorSchema.Description = proc.Description
}
if proc.ParamsSchema != nil {
processorSchema.Properties["params"] = proc.ParamsSchema
processorSchema.Required = append(processorSchema.Required, "params")
}
processorDefinitionSchemas = append(processorDefinitionSchemas, processorSchema)
}
schema.Items = &jsonschema.Schema{
OneOf: processorDefinitionSchemas,
}
return schema
}

24
internal/schema/routes.go Normal file
View File

@@ -0,0 +1,24 @@
package schema
import "github.com/google/jsonschema-go/jsonschema"
var RoutesConfigSchema = jsonschema.Schema{
Schema: "https://json-schema.org/draft/2020-12/schema",
ID: "https://showbridge.io/routes.schema.json",
Title: "Routes",
Description: "route configurations",
Type: "array",
Items: &jsonschema.Schema{
Type: "object",
Properties: map[string]*jsonschema.Schema{
"input": {
Type: "string",
MinLength: jsonschema.Ptr(1),
},
"processors": {
Ref: "https://showbridge.io/processors.schema.json",
},
},
Required: []string{"input"},
},
}

27
internal/schema/schema.go Normal file
View File

@@ -0,0 +1,27 @@
package schema
import (
"fmt"
"net/url"
"github.com/google/jsonschema-go/jsonschema"
)
func GetResolvedConfigSchema() (*jsonschema.Resolved, error) {
configSchema := ConfigSchema
return configSchema.Resolve(&jsonschema.ResolveOptions{
Loader: func(uri *url.URL) (*jsonschema.Schema, error) {
switch uri.String() {
case "https://showbridge.io/modules.schema.json":
return GetModulesSchema(), nil
case "https://showbridge.io/processors.schema.json":
return GetProcessorsSchema(), nil
case "https://showbridge.io/routes.schema.json":
return &RoutesConfigSchema, nil
default:
return nil, fmt.Errorf("unknown schema reference: %s", uri.String())
}
},
})
}