Files
zeekayandHanzo Dev a5c5cc019e Go 1.27.1, and the code it can say more simply
Modules declare go 1.27.1 and the build images follow. go fix applied the
modernizers; omitzero is not applied, since it changes what the JSON says.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-16 22:42:15 -07:00

121 lines
2.7 KiB
Go

package jsonschema
import (
"bytes"
"context"
"encoding/json"
"errors"
"io"
"net/http"
"strings"
"github.com/hanzoai/gateway/v2/internal/lura/config"
"github.com/hanzoai/gateway/v2/internal/lura/logging"
"github.com/hanzoai/gateway/v2/internal/lura/proxy"
"github.com/santhosh-tekuri/jsonschema/v6"
)
const Namespace = "github.com/devopsfaith/krakend-jsonschema"
var ErrEmptyBody = &malformedError{err: errors.New("could not validate an empty body")}
// ProxyFactory creates an proxy factory over the injected one adding a JSON Schema
// validator middleware to the pipe when required
func ProxyFactory(logger logging.Logger, pf proxy.Factory) proxy.FactoryFunc {
return proxy.FactoryFunc(func(cfg *config.EndpointConfig) (proxy.Proxy, error) {
next, err := pf.New(cfg)
if err != nil {
return proxy.NoopProxy, err
}
jschema := configGetter(cfg.ExtraConfig)
if jschema == nil {
return next, nil
}
c := jsonschema.NewCompiler()
c.AddResource("./schema.json", jschema)
s, err := c.Compile("./schema.json")
if err != nil {
logger.Error("[ENDPOINT: " + cfg.Endpoint + "][JSONSchema] Parsing the definition:" + err.Error())
return next, nil
}
logger.Debug("[ENDPOINT: " + cfg.Endpoint + "][JSONSchema] Validator enabled")
return newProxy(s, next), nil
})
}
func newProxy(schema *jsonschema.Schema, next proxy.Proxy) proxy.Proxy {
return func(ctx context.Context, r *proxy.Request) (*proxy.Response, error) {
if r.Body == nil {
return nil, ErrEmptyBody
}
body, err := io.ReadAll(r.Body)
if err != nil {
return nil, err
}
r.Body.Close()
if len(body) == 0 {
return nil, ErrEmptyBody
}
r.Body = io.NopCloser(bytes.NewBuffer(body))
b, err := jsonschema.UnmarshalJSON(bytes.NewReader(body))
if err != nil {
return nil, &malformedError{err: err}
}
err = schema.Validate(b)
if err != nil {
return nil, &validationError{error: err}
}
return next(ctx, r)
}
}
func configGetter(cfg config.ExtraConfig) any {
v, ok := cfg[Namespace]
if !ok {
return nil
}
buf := new(bytes.Buffer)
if err := json.NewEncoder(buf).Encode(v); err != nil {
return nil
}
schema, err := jsonschema.UnmarshalJSON(buf)
if err != nil {
return nil
}
return schema
}
type validationError struct {
error
}
func (v *validationError) Error() string {
s := v.error.Error()
if strings.HasPrefix(s, "jsonschema validation failed with") {
if ss := strings.SplitN(s, "\n", 2); len(ss) == 2 {
return ss[1]
}
}
return s
}
func (*validationError) StatusCode() int {
return http.StatusBadRequest
}
type malformedError struct {
err error
}
func (m *malformedError) Error() string {
return m.err.Error()
}
func (*malformedError) StatusCode() int {
return http.StatusBadRequest
}