From 4d0c53c57a03f33e86f58785f0c14b9a2e5e77d8 Mon Sep 17 00:00:00 2001 From: Max Altgelt Date: Mon, 8 Dec 2025 14:52:04 +0100 Subject: [PATCH] fix: make schema generation deterministic Ensure that schema generation always produces the same JSON. Previously, this was not the case since iteration over maps is not deterministic. --- thorlog/jsonschema/generateschema.go | 16 +++++++++++++--- thorlog/jsonschema/go.sum | 2 -- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/thorlog/jsonschema/generateschema.go b/thorlog/jsonschema/generateschema.go index 56ca1aa..18c7b10 100644 --- a/thorlog/jsonschema/generateschema.go +++ b/thorlog/jsonschema/generateschema.go @@ -3,8 +3,10 @@ package main import ( "encoding/json" "fmt" + "maps" "os" "reflect" + "slices" "strings" "github.com/NextronSystems/jsonlog" @@ -19,11 +21,17 @@ func makeObjectSchema() (mainEntry string, defs map[string]*jsonschema.Schema) { var allLogObjects []*jsonschema.Schema var logObjectTypes []any var reflector jsonschema.Reflector + reflector.AllowAdditionalProperties = true err := reflector.AddGoComments("github.com/NextronSystems/jsonlog/thorlog/v3", "../v3") if err != nil { panic(err) } defs = map[string]*jsonschema.Schema{} + + // Sort the object type names to have a stable output + var objectTypeNames = slices.Collect(maps.Keys(thorlog.LogObjectTypes)) + slices.Sort(objectTypeNames) + reflector.Mapper = func(r reflect.Type) *jsonschema.Schema { if r.Kind() == reflect.Interface { if r.Implements(objectType) { @@ -32,7 +40,8 @@ func makeObjectSchema() (mainEntry string, defs map[string]*jsonschema.Schema) { // we can filter for the types that implement the interface, // and generate a oneOf schema for them. var implementations = &jsonschema.Schema{} - for _, t := range thorlog.LogObjectTypes { + for _, typename := range objectTypeNames { + t := thorlog.LogObjectTypes[typename] if reflect.TypeOf(t).Implements(r) { structName := reflect.TypeOf(t).Elem().Name() implementations.OneOf = append(implementations.OneOf, &jsonschema.Schema{ @@ -52,8 +61,8 @@ func makeObjectSchema() (mainEntry string, defs map[string]*jsonschema.Schema) { } return nil } - for typename, object := range thorlog.LogObjectTypes { - schema := reflector.Reflect(object) + for _, typename := range objectTypeNames { + schema := reflector.Reflect(thorlog.LogObjectTypes[typename]) refName := strings.TrimPrefix(schema.Ref, "#/$defs/") typeSchema := schema.Definitions[refName] typenameDef, ok := typeSchema.Properties.Get("type") @@ -63,6 +72,7 @@ func makeObjectSchema() (mainEntry string, defs map[string]*jsonschema.Schema) { typenameDef.Const = typename allLogObjects = append(allLogObjects, schema) logObjectTypes = append(logObjectTypes, typename) + defs[refName] = typeSchema } var logObjectSchema = &jsonschema.Schema{ Properties: orderedmap.New[string, *jsonschema.Schema](), diff --git a/thorlog/jsonschema/go.sum b/thorlog/jsonschema/go.sum index 1f4e659..0f11df4 100644 --- a/thorlog/jsonschema/go.sum +++ b/thorlog/jsonschema/go.sum @@ -1,5 +1,3 @@ -github.com/NextronSystems/jsonlog v0.0.0-20250522074152-0a205b123911 h1:jmmvwkhul9DtWWOBhyeyXV9LA4cQzqJ0UyniD3e/HZQ= -github.com/NextronSystems/jsonlog v0.0.0-20250522074152-0a205b123911/go.mod h1:Hk47VW018TX8o/0sxK+EJt16iRE7gB91zGZGiaAjcww= github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk= github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg= github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=