diff --git a/fixtures/custom_type_extend.json b/fixtures/custom_type_extend.json new file mode 100644 index 0000000..a1dc9d4 --- /dev/null +++ b/fixtures/custom_type_extend.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/invopop/jsonschema/schema-extend-test", + "$ref": "#/$defs/SchemaExtendTest", + "$defs": { + "SchemaExtendTest": { + "properties": { + "LastName": { + "type": "string", + "description": "some extra words" + }, + "middle_name": { + "type": "string" + } + }, + "additionalProperties": false, + "type": "object", + "required": [ + "LastName" + ] + } + } +} \ No newline at end of file diff --git a/reflect.go b/reflect.go index 4876ec2..6ebc6be 100644 --- a/reflect.go +++ b/reflect.go @@ -108,7 +108,14 @@ type customSchemaImpl interface { JSONSchema() *Schema } +// Function to be run after the schema has been generated. +// this will let you modify a schema afterwards +type extendSchemaImpl interface { + JSONSchemaExtend(*Schema) +} + var customType = reflect.TypeOf((*customSchemaImpl)(nil)).Elem() +var extendType = reflect.TypeOf((*extendSchemaImpl)(nil)).Elem() // customSchemaGetFieldDocString type customSchemaGetFieldDocString interface { @@ -395,6 +402,8 @@ func (r *Reflector) reflectTypeToSchema(definitions Definitions, t reflect.Type) panic("unsupported type " + t.String()) } + r.reflectSchemaExtend(definitions, t, st) + // Always try to reference the definition which may have just been created if def := r.refDefinition(definitions, t); def != nil { return def @@ -422,6 +431,19 @@ func (r *Reflector) reflectCustomSchema(definitions Definitions, t reflect.Type) return nil } +func (r *Reflector) reflectSchemaExtend(definitions Definitions, t reflect.Type, s *Schema) *Schema { + if t.Implements(extendType) { + v := reflect.New(t) + o := v.Interface().(extendSchemaImpl) + o.JSONSchemaExtend(s) + if ref := r.refDefinition(definitions, t); ref != nil { + return ref + } + } + + return s +} + func (r *Reflector) reflectSliceOrArray(definitions Definitions, t reflect.Type, st *Schema) { if t == rawMessageType { return diff --git a/reflect_test.go b/reflect_test.go index 6bc2e1f..c0d2c9f 100644 --- a/reflect_test.go +++ b/reflect_test.go @@ -310,6 +310,25 @@ type KeyNamed struct { RenamedByComputation int `jsonschema_description:"Description was preserved"` } +type SchemaExtendTestBase struct { + FirstName string `json:"FirstName"` + LastName string `json:"LastName"` + Age uint `json:"age"` + MiddleName string `json:"middle_name,omitempty"` +} + +type SchemaExtendTest struct { + SchemaExtendTestBase `json:",inline"` +} + +func (SchemaExtendTest) JSONSchemaExtend(base *Schema) { + base.Properties.Delete("FirstName") + base.Properties.Delete("age") + val, _ := base.Properties.Get("LastName") + (val).(*Schema).Description = "some extra words" + base.Required = []string{"LastName"} +} + type Expression struct { Value int `json:"value" jsonschema_extras:"foo=bar=='baz'"` } @@ -444,6 +463,7 @@ func TestSchemaGeneration(t *testing.T) { }, "fixtures/keynamed.json"}, {MapType{}, &Reflector{}, "fixtures/map_type.json"}, {ArrayType{}, &Reflector{}, "fixtures/array_type.json"}, + {SchemaExtendTest{}, &Reflector{}, "fixtures/custom_type_extend.json"}, {Expression{}, &Reflector{}, "fixtures/schema_with_expression.json"}, }