Skip to content

Commit aabae26

Browse files
authored
openapi: update JSON Schema to draft 2020-12 (goadesign#3838)
Update JSON Schema from draft-04 to draft 2020-12: - Change 'definitions' to '$defs' (draft 2020-12 format) - Update schema reference to draft 2020-12 meta-schema - Update all reference paths from #/definitions/ to #/$defs/ - Add conversion functions in v2/builder.go to convert references back to Swagger 2.0 format (#/definitions/) for backward compatibility
1 parent d363862 commit aabae26

File tree

3 files changed

+91
-14
lines changed

3 files changed

+91
-14
lines changed

http/codegen/openapi/json_schema.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ type (
2020
Type Type `json:"type,omitempty" yaml:"type,omitempty"`
2121
Items *Schema `json:"items,omitempty" yaml:"items,omitempty"`
2222
Properties map[string]*Schema `json:"properties,omitempty" yaml:"properties,omitempty"`
23-
Definitions map[string]*Schema `json:"definitions,omitempty" yaml:"definitions,omitempty"`
23+
Defs map[string]*Schema `json:"$defs,omitempty" yaml:"$defs,omitempty"`
2424
Description string `json:"description,omitempty" yaml:"description,omitempty"`
2525
DefaultValue any `json:"default,omitempty" yaml:"default,omitempty"`
2626
Example any `json:"example,omitempty" yaml:"example,omitempty"`
@@ -99,8 +99,8 @@ const (
9999
File = "file"
100100
)
101101

102-
// SchemaRef is the JSON Hyper-schema standard href.
103-
const SchemaRef = "http://json-schema.org/draft-04/hyper-schema"
102+
// SchemaRef is the JSON Schema draft 2020-12 meta-schema identifier.
103+
const SchemaRef = "https://json-schema.org/draft/2020-12/schema"
104104

105105
var (
106106
// Definitions contains the generated JSON schema definitions
@@ -115,8 +115,8 @@ func init() {
115115
// NewSchema instantiates a new JSON schema.
116116
func NewSchema() *Schema {
117117
js := Schema{
118-
Properties: make(map[string]*Schema),
119-
Definitions: make(map[string]*Schema),
118+
Properties: make(map[string]*Schema),
119+
Defs: make(map[string]*Schema),
120120
}
121121
return &js
122122
}
@@ -156,8 +156,8 @@ func APISchema(api *expr.APIExpr, r *expr.RootExpr) *Schema {
156156
Title: api.Title,
157157
Description: api.Description,
158158
Type: Object,
159-
Definitions: Definitions,
160-
Properties: propertiesFromDefs(Definitions, "#/definitions/"),
159+
Defs: Definitions,
160+
Properties: propertiesFromDefs(Definitions, "#/$defs/"),
161161
Links: links,
162162
}
163163
return &s
@@ -250,7 +250,7 @@ func ResultTypeRefWithPrefix(api *expr.APIExpr, mt *expr.ResultTypeExpr, view, p
250250
}
251251
GenerateResultTypeDefinition(api, projected, expr.DefaultView)
252252
}
253-
return fmt.Sprintf("#/definitions/%s", projected.TypeName)
253+
return fmt.Sprintf("#/$defs/%s", projected.TypeName)
254254
}
255255

256256
// TypeRef produces the JSON reference to the type definition.
@@ -271,7 +271,7 @@ func TypeRefWithPrefix(api *expr.APIExpr, ut *expr.UserTypeExpr, prefix string)
271271
if _, ok := Definitions[typeName]; !ok {
272272
GenerateTypeDefinitionWithName(api, ut, typeName)
273273
}
274-
return fmt.Sprintf("#/definitions/%s", typeName)
274+
return fmt.Sprintf("#/$defs/%s", typeName)
275275
}
276276

277277
// GenerateResultTypeDefinition produces the JSON schema corresponding to the
@@ -469,8 +469,8 @@ func (s *Schema) Dup() *Schema {
469469
if s.Items != nil {
470470
js.Items = s.Items.Dup()
471471
}
472-
for n, d := range s.Definitions {
473-
js.Definitions[n] = d.Dup()
472+
for n, d := range s.Defs {
473+
js.Defs[n] = d.Dup()
474474
}
475475
return &js
476476
}

http/codegen/openapi/merge.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@ func (s *Schema) Merge(other *Schema) {
2626
}
2727
}
2828

29-
for n, d := range other.Definitions {
30-
if _, ok := s.Definitions[n]; !ok {
31-
s.Definitions[n] = d
29+
for n, d := range other.Defs {
30+
if _, ok := s.Defs[n]; !ok {
31+
s.Defs[n] = d
3232
}
3333
}
3434

http/codegen/openapi/v2/builder.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ func NewV2(root *expr.RootExpr, h *expr.HostExpr) (*V2, error) {
9393
s.Definitions[n] = d
9494
}
9595
}
96+
// Convert OpenAPI 3.0 references (#/$defs/) to Swagger 2.0 format (#/definitions/)
97+
convertRefsToV2(s)
9698
return s, nil
9799
}
98100

@@ -814,3 +816,78 @@ func initValidations(attr *expr.AttributeExpr, def any) {
814816
initMaxLengthValidation(def, expr.IsArray(attr.Type), val.MaxLength)
815817
}
816818
}
819+
820+
// convertRefsToV2 converts all OpenAPI 3.0 references (#/$defs/) to Swagger 2.0
821+
// format (#/definitions/) in all schemas throughout the V2 specification.
822+
func convertRefsToV2(s *V2) {
823+
// Convert references in definitions
824+
for _, def := range s.Definitions {
825+
convertSchemaRefs(def)
826+
}
827+
// Convert references in paths
828+
for _, pathVal := range s.Paths {
829+
if path, ok := pathVal.(*Path); ok {
830+
convertPathRefs(path)
831+
}
832+
}
833+
// Convert references in parameters
834+
for _, param := range s.Parameters {
835+
if param.Schema != nil {
836+
convertSchemaRefs(param.Schema)
837+
}
838+
}
839+
}
840+
841+
// convertPathRefs converts references in all operations of a path.
842+
func convertPathRefs(path *Path) {
843+
ops := []*Operation{path.Get, path.Put, path.Post, path.Delete, path.Options, path.Head, path.Patch}
844+
for _, op := range ops {
845+
if op == nil {
846+
continue
847+
}
848+
// Convert references in parameters
849+
for _, param := range op.Parameters {
850+
if param.Schema != nil {
851+
convertSchemaRefs(param.Schema)
852+
}
853+
}
854+
// Convert references in responses
855+
for _, resp := range op.Responses {
856+
if resp.Schema != nil {
857+
convertSchemaRefs(resp.Schema)
858+
}
859+
}
860+
}
861+
}
862+
863+
// convertSchemaRefs recursively converts all #/$defs/ references to #/definitions/
864+
// in a schema and all nested schemas.
865+
func convertSchemaRefs(schema *openapi.Schema) {
866+
if schema == nil {
867+
return
868+
}
869+
// Convert the reference itself
870+
if strings.HasPrefix(schema.Ref, "#/$defs/") {
871+
schema.Ref = strings.Replace(schema.Ref, "#/$defs/", "#/definitions/", 1)
872+
}
873+
// Convert references in items
874+
if schema.Items != nil {
875+
convertSchemaRefs(schema.Items)
876+
}
877+
// Convert references in properties
878+
for _, prop := range schema.Properties {
879+
convertSchemaRefs(prop)
880+
}
881+
// Convert references in anyOf
882+
for _, anyOfSchema := range schema.AnyOf {
883+
convertSchemaRefs(anyOfSchema)
884+
}
885+
// Convert references in additionalProperties when it's a schema
886+
if apSchema, ok := schema.AdditionalProperties.(*openapi.Schema); ok {
887+
convertSchemaRefs(apSchema)
888+
}
889+
// Convert references in $defs (though these shouldn't exist in Swagger 2.0)
890+
for _, def := range schema.Defs {
891+
convertSchemaRefs(def)
892+
}
893+
}

0 commit comments

Comments
 (0)