diff --git a/generator.go b/generator.go index 9b9e421..8331980 100644 --- a/generator.go +++ b/generator.go @@ -191,12 +191,21 @@ func (g *Generator) processObject(name string, schema *Schema) (typ string, err if err != nil { return "", err } + + access := Access_RW + if prop.ReadOnly { + access = Access_RO + } else if prop.WriteOnly { + access = Access_WO + } + f := Field{ Name: fieldName, JSONName: propKey, Type: fieldType, Required: contains(schema.Required, propKey), Description: prop.Description, + Access: access, } if f.Required { strct.GenerateCode = true @@ -384,6 +393,14 @@ type Struct struct { AdditionalType string } +type Access int + +const ( + Access_RW Access = iota + Access_RO + Access_WO +) + // Field defines the data required to generate a field in Go. type Field struct { // The golang name, e.g. "Address1" @@ -395,5 +412,8 @@ type Field struct { Type string // Required is set to true when the field is required. Required bool + // Access type, either R/W (default), R/O, or W/O. + Access Access + Description string } diff --git a/jsonschema.go b/jsonschema.go index 5c0616a..9ea46e3 100644 --- a/jsonschema.go +++ b/jsonschema.go @@ -43,6 +43,11 @@ type Schema struct { // "additionalProperties": false AdditionalPropertiesBool *bool `json:"-"` + // ReadOnly and WriteOnly specify access to a field. + // https://json-schema.org/draft-07/json-schema-validation.html#rfc.section.10.3 + ReadOnly bool + WriteOnly bool + AnyOf []*Schema AllOf []*Schema OneOf []*Schema diff --git a/output.go b/output.go index 809bd3d..f554082 100644 --- a/output.go +++ b/output.go @@ -83,12 +83,23 @@ func Output(w io.Writer, g *Generator, pkg string) { if f.Required { omitempty = "" } + schemaTags := []string{} + if f.Access == Access_RO { + schemaTags = append(schemaTags, "readonly") + } else if f.Access == Access_WO { + schemaTags = append(schemaTags, "writeonly") + } + + schemaTag := "" + if len(schemaTags) > 0 { + schemaTag = fmt.Sprintf(" jsonSchema:\"%s\"", strings.Join(schemaTags, ",")) + } if f.Description != "" { outputFieldDescriptionComment(f.Description, w) } - fmt.Fprintf(w, " %s %s `json:\"%s%s\"`\n", f.Name, f.Type, f.JSONName, omitempty) + fmt.Fprintf(w, " %s %s `json:\"%s%s\"%s`\n", f.Name, f.Type, f.JSONName, omitempty, schemaTag) } fmt.Fprintln(w, "}") diff --git a/test/example1.json b/test/example1.json index f217c7f..cb4688d 100644 --- a/test/example1.json +++ b/test/example1.json @@ -24,6 +24,14 @@ }, "minItems": 1, "uniqueItems": true + }, + "inStock": { + "type": "boolean", + "readOnly": true + }, + "couponCode": { + "type": "string", + "writeOnly": true } }, "required": [ diff --git a/test/example1_test.go b/test/example1_test.go index 78e8563..3e7c43d 100644 --- a/test/example1_test.go +++ b/test/example1_test.go @@ -1,9 +1,11 @@ package test import ( + "strings" "encoding/json" - "testing" "github.com/a-h/generate/test/example1_gen" + "reflect" + "testing" ) func TestExample1(t *testing.T) { @@ -45,3 +47,24 @@ func TestExample1(t *testing.T) { } } } + +func TestExample1Access(t *testing.T) { + fs := reflect.VisibleFields(reflect.TypeOf(example1.Product{})) + for _, f := range fs { + if f.Name == "CouponCode" { + if v, ok := f.Tag.Lookup("jsonSchema"); !ok { + t.Fatal("Expected CouponCode field to have jsonSchema tag") + } else if !strings.Contains(v, "writeonly") { + t.Fatalf("CouponCode's json %q doesn't specify writeonly", v) + } + } + + if f.Name == "InStock" { + if v, ok := f.Tag.Lookup("jsonSchema"); !ok { + t.Fatal("Expected InStock field to have jsonSchema tag") + } else if !strings.Contains(v, "readonly") { + t.Fatalf("InStock's json %q doesn't specify readonly", v) + } + } + } +}