From 85e6c3d879c98ef5f2d88e069871719b87f53548 Mon Sep 17 00:00:00 2001 From: michel-laterman Date: Thu, 12 May 2022 08:26:56 -0500 Subject: [PATCH] Support for decoding string/date-time to time.Time Support decoding string/date-time in the json schema to a time.Time in golang. This is done by adding a new internal attribute that will track additional (golang) imports that a struct needs to import for code generation. --- generator.go | 6 ++++- generator_test.go | 57 +++++++++++++++++++++++++++++++++++++++++++ output.go | 9 +++++-- test/calendar.json | 17 +++++++++++++ test/calendar_test.go | 23 +++++++++++++++++ 5 files changed, 109 insertions(+), 3 deletions(-) create mode 100644 test/calendar.json create mode 100644 test/calendar_test.go diff --git a/generator.go b/generator.go index 32d23bd..7f9e6c1 100644 --- a/generator.go +++ b/generator.go @@ -201,6 +201,9 @@ func (g *Generator) processObject(name string, schema *Schema, conventions map[s Description: prop.Description, Format: prop.Format, } + if f.Type == "string" && f.Format == "date-time" { + strct.importTypes = append(strct.importTypes, "time") + } if f.Required { strct.GenerateCode = true } @@ -324,7 +327,7 @@ func (g *Generator) getSchemaName(keyName string, schema *Schema, conventions ma return getGolangName(schema.JSONKey, conventions) } if schema.Parent != nil && schema.Parent.JSONKey != "" { - return getGolangName(schema.Parent.JSONKey + "Item", conventions) + return getGolangName(schema.Parent.JSONKey+"Item", conventions) } g.anonCount++ return fmt.Sprintf("Anonymous%d", g.anonCount) @@ -395,6 +398,7 @@ type Struct struct { GenerateCode bool AdditionalType string + importTypes []string // addition packages to include when importing } // Field defines the data required to generate a field in Go. diff --git a/generator_test.go b/generator_test.go index 6309b24..6f59d65 100644 --- a/generator_test.go +++ b/generator_test.go @@ -785,6 +785,63 @@ func TestTypeAliases(t *testing.T) { } } +func TestImportTypeDetection(t *testing.T) { + tests := []struct { + name string + input *Schema + expect []string + }{{ + name: "no additional types", + input: &Schema{ + Title: "example", + TypeValue: "object", + Properties: map[string]*Schema{ + "key": {TypeValue: "string"}, + }, + }, + expect: []string{}, + }, { + name: "string/date-time imports time", + input: &Schema{ + Title: "example", + TypeValue: "object", + Properties: map[string]*Schema{ + "key": { + TypeValue: "string", + Format: "date-time", + }, + }, + }, + expect: []string{"time"}, + }} + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + test.input.Init() + g := New(test.input) + err := g.CreateTypes(conventionStub) + + if err != nil { + t.Fatal(err) + } + vals := make([]string, 0) + for _, strct := range g.Structs { + vals = append(vals, strct.importTypes...) + } + + if len(vals) != len(test.expect) { + t.Fatalf("Expected %d values in import types, got %d", len(test.expect), len(vals)) + } + + for i, s := range test.expect { + if vals[i] != s { + t.Errorf("Expected value %d of import types to be %q, got %q", i, s, vals[i]) + } + } + + }) + } +} + // Root is an example of a generated type. type Root struct { Name interface{} `json:"name,omitempty"` diff --git a/output.go b/output.go index 6ae77f6..383667a 100644 --- a/output.go +++ b/output.go @@ -84,6 +84,9 @@ func Output(w io.Writer, g *Generator, pkg string, skipCode bool, esdoc bool) { } else { imports["encoding/json"] = true } + for _, str := range s.importTypes { + imports[str] = true + } } if len(imports) > 0 { @@ -168,6 +171,8 @@ func Output(w io.Writer, g *Generator, pkg string, skipCode bool, esdoc bool) { ftype := f.Type if ftype == "int" { ftype = "int64" + } else if ftype == "string" && f.Format == "date-time" { + ftype = "time.Time" } if f.Format == "raw" { ftype = "json.RawMessage" @@ -215,8 +220,8 @@ func (strct *%s) MarshalJSON() ([]byte, error) { fmt.Fprintf(w, ` // Marshal the "%[1]s" field - if comma { - buf.WriteString(",") + if comma { + buf.WriteString(",") } buf.WriteString("\"%[1]s\": ") if tmp, err := json.Marshal(strct.%[2]s); err != nil { diff --git a/test/calendar.json b/test/calendar.json new file mode 100644 index 0000000..d04280a --- /dev/null +++ b/test/calendar.json @@ -0,0 +1,17 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Calendar", + "description": "A calendar", + "type": "object", + "properties": { + "date": { + "description": "A date", + "type": "string", + "format": "date-time" + } + }, + "required": [ + "date" + ] +} + diff --git a/test/calendar_test.go b/test/calendar_test.go new file mode 100644 index 0000000..c1baf41 --- /dev/null +++ b/test/calendar_test.go @@ -0,0 +1,23 @@ +package test + +import ( + "encoding/json" + "testing" + "time" + + calendar "github.com/elastic/go-json-schema-generate/test/calendar_gen" +) + +func TestCalendar(t *testing.T) { + data := []byte(`{ + "date": "2022-01-02T12:00:00Z" + }`) + cal := &calendar.Calendar{} + if err := json.Unmarshal(data, &cal); err != nil { + t.Fatal(err) + } + ts := time.Date(2022, 1, 2, 12, 0, 0, 0, time.UTC) + if !cal.Date.Equal(ts) { + t.Errorf("expected date to be %v got %v", ts, cal.Date) + } +}