Repository: ogen-go/ogen
Pull request #399 (2022-06-15 14:12:16)

feat(gen): add deprecation notice for types, parameters and fields


Patch:
diff --git a/examples/ex_github/oas_schemas_gen.go b/examples/ex_github/oas_schemas_gen.go
index 8aa65f5a..8355e545 100644
--- a/examples/ex_github/oas_schemas_gen.go
+++ b/examples/ex_github/oas_schemas_gen.go
@@ -2532,10 +2532,11 @@ type CodeOfConductSimple struct {
 
 // Ref: #/components/schemas/code-scanning-alert
 type CodeScanningAlert struct {
-	Number             AlertNumber                         "json:\"number\""
-	CreatedAt          AlertCreatedAt                      "json:\"created_at\""
-	URL                AlertURL                            "json:\"url\""
-	HTMLURL            AlertHTMLURL                        "json:\"html_url\""
+	Number    AlertNumber    "json:\"number\""
+	CreatedAt AlertCreatedAt "json:\"created_at\""
+	URL       AlertURL       "json:\"url\""
+	HTMLURL   AlertHTMLURL   "json:\"html_url\""
+	// Deprecated: schema marks this property as deprecated.
 	Instances          jx.Raw                              "json:\"instances\""
 	InstancesURL       AlertInstancesURL                   "json:\"instances_url\""
 	State              CodeScanningAlertState              "json:\"state\""
@@ -4363,8 +4364,10 @@ type GistHistoryChangeStatus struct {
 // Gist Simple.
 // Ref: #/components/schemas/gist-simple
 type GistSimple struct {
-	Forks   OptNilGistSimpleForksItemArray "json:\"forks\""
-	History OptNilGistHistoryArray         "json:\"history\""
+	// Deprecated: schema marks this property as deprecated.
+	Forks OptNilGistSimpleForksItemArray "json:\"forks\""
+	// Deprecated: schema marks this property as deprecated.
+	History OptNilGistHistoryArray "json:\"history\""
 	// Gist.
 	ForkOf      OptNilGistSimpleForkOf "json:\"fork_of\""
 	URL         OptString              "json:\"url\""
diff --git a/gen/ir/description.go b/gen/ir/description.go
index 5eff7a73..936bbfaa 100644
--- a/gen/ir/description.go
+++ b/gen/ir/description.go
@@ -43,7 +43,7 @@ const (
 	lineLimit = 100
 )
 
-func prettyDoc(s string) (r []string) {
+func prettyDoc(s, deprecation string) (r []string) {
 	// TODO(tdakkota): basic common mark rendering?
 	for _, line := range strings.Split(s, "\n") {
 		r = append(r, splitLine(line, lineLimit)...)
@@ -55,6 +55,13 @@ func prettyDoc(s string) (r []string) {
 			r[len(r)-1] = last + "."
 		}
 	}
+	if deprecation != "" {
+		if len(r) > 0 {
+			// Insert empty line between description and deprecated notice.
+			r = append(r, "")
+		}
+		r = append(r, deprecation)
+	}
 
 	return r
 }
diff --git a/gen/ir/field.go b/gen/ir/field.go
index f31588f5..a0e8ab88 100644
--- a/gen/ir/field.go
+++ b/gen/ir/field.go
@@ -70,13 +70,20 @@ func (f Field) Default() Default {
 
 // GoDoc returns field godoc.
 func (f Field) GoDoc() []string {
-	if f.Spec == nil {
+	s := f.Spec
+	if s == nil {
 		if f.Inline == InlinePattern {
 			return []string{fmt.Sprintf("Pattern: %q.", f.Type.MapPattern)}
 		}
 		return nil
 	}
-	return prettyDoc(f.Spec.Description)
+
+	var notice string
+	if sch := s.Schema; sch != nil && sch.Deprecated {
+		notice = "Deprecated: schema marks this property as deprecated."
+	}
+
+	return prettyDoc(s.Description, notice)
 }
 
 // DefaultFields returns fields with default values.
diff --git a/gen/ir/operation.go b/gen/ir/operation.go
index 8414a934..667f2e7d 100644
--- a/gen/ir/operation.go
+++ b/gen/ir/operation.go
@@ -36,12 +36,11 @@ func (op Operation) GoDoc() []string {
 		doc = op.Summary
 	}
 
-	r := prettyDoc(doc)
+	var notice string
 	if op.Deprecated {
-		// Empty line between description and deprecated notice.
-		r = append(r, "", "Deprecated: schema marks this operation as deprecated.")
+		notice = "Deprecated: schema marks this operation as deprecated."
 	}
-	return r
+	return prettyDoc(doc, notice)
 }
 
 type PathPart struct {
@@ -62,6 +61,19 @@ type Parameter struct {
 	Spec *openapi.Parameter
 }
 
+func (op Parameter) GoDoc() []string {
+	s := op.Spec
+	if s == nil {
+		return nil
+	}
+
+	var notice string
+	if s.Deprecated {
+		notice = "Deprecated: schema marks this parameter as deprecated."
+	}
+	return prettyDoc(s.Description, notice)
+}
+
 // Default returns default value of this field, if it is set.
 func (op Parameter) Default() Default {
 	var schema *jsonschema.Schema
@@ -80,13 +92,6 @@ func (op Parameter) Default() Default {
 	return Default{}
 }
 
-func (op Parameter) GoDoc() []string {
-	if op.Spec == nil {
-		return nil
-	}
-	return prettyDoc(op.Spec.Description)
-}
-
 type Request struct {
 	Type     *Type
 	Contents map[ContentType]*Type
diff --git a/gen/ir/security.go b/gen/ir/security.go
index 933987ba..e41fd0b4 100644
--- a/gen/ir/security.go
+++ b/gen/ir/security.go
@@ -76,7 +76,7 @@ type Security struct {
 }
 
 func (s *Security) GoDoc() []string {
-	return prettyDoc(s.Description)
+	return prettyDoc(s.Description, "")
 }
 
 type SecurityRequirement struct {
diff --git a/gen/ir/type.go b/gen/ir/type.go
index 3fd73122..4d236773 100644
--- a/gen/ir/type.go
+++ b/gen/ir/type.go
@@ -85,11 +85,17 @@ func (t Type) GoDoc() []string {
 	if s == nil {
 		return nil
 	}
+
 	doc := s.Description
 	if doc == "" {
 		doc = s.Summary
 	}
-	return prettyDoc(doc)
+
+	var notice string
+	if s.Deprecated {
+		notice = "Deprecated: schema marks this type as deprecated."
+	}
+	return prettyDoc(doc, notice)
 }
 
 // Default returns default value of this type, if it is set.
diff --git a/jsonschema/parser.go b/jsonschema/parser.go
index cdcce553..0322ce7a 100644
--- a/jsonschema/parser.go
+++ b/jsonschema/parser.go
@@ -369,6 +369,7 @@ func (p *Parser) parseMany(schemas []*RawSchema, ctx *resolveCtx) ([]*Schema, er
 func (p *Parser) extendInfo(schema *RawSchema, s *Schema) *Schema {
 	s.Summary = schema.Summary
 	s.Description = schema.Description
+	s.Deprecated = schema.Deprecated
 	s.AddExample(schema.Example)
 
 	// Nullable enums will be handled later.
diff --git a/jsonschema/schema.go b/jsonschema/schema.go
index b6b10e46..a56224d1 100644
--- a/jsonschema/schema.go
+++ b/jsonschema/schema.go
@@ -35,6 +35,7 @@ type Schema struct {
 	Format      string // Schema format, optional.
 	Summary     string // Schema summary from Reference Object, optional.
 	Description string // Schema description, optional.
+	Deprecated  bool
 	Ref         string // Whether schema is referenced.
 
 	Item                 *Schema           // Only for Array and Object with additional properties.
diff --git a/openapi/parameter.go b/openapi/parameter.go
index f3a81fe3..c10851bd 100644
--- a/openapi/parameter.go
+++ b/openapi/parameter.go
@@ -39,6 +39,7 @@ type Parameter struct {
 	Style       ParameterStyle
 	Explode     bool
 	Required    bool
+	Deprecated  bool
 }
 
 // ParameterContent describes OpenAPI Parameter content field.
diff --git a/openapi/parser/parse_header.go b/openapi/parser/parse_header.go
index 62dab34b..d113aabf 100644
--- a/openapi/parser/parse_header.go
+++ b/openapi/parser/parse_header.go
@@ -62,14 +62,15 @@ func (p *parser) parseHeader(name string, header *ogen.Header, ctx *resolveCtx)
 
 	locatedIn := openapi.LocationHeader
 	op := &openapi.Header{
-		In:          locatedIn,
 		Name:        name,
 		Description: header.Description,
 		Schema:      schema,
 		Content:     content,
+		In:          locatedIn,
 		Style:       inferParamStyle(locatedIn, header.Style),
 		Explode:     inferParamExplode(locatedIn, header.Explode),
 		Required:    header.Required,
+		Deprecated:  header.Deprecated,
 	}
 
 	if header.Content != nil {
diff --git a/openapi/parser/parse_parameter.go b/openapi/parser/parse_parameter.go
index 778c3134..d6f05e7e 100644
--- a/openapi/parser/parse_parameter.go
+++ b/openapi/parser/parse_parameter.go
@@ -102,12 +102,13 @@ func (p *parser) parseParameter(param *ogen.Parameter, ctx *resolveCtx) (*openap
 	op := &openapi.Parameter{
 		Name:        param.Name,
 		Description: param.Description,
-		In:          locatedIn,
 		Schema:      schema,
 		Content:     content,
+		In:          locatedIn,
 		Style:       inferParamStyle(locatedIn, param.Style),
 		Explode:     inferParamExplode(locatedIn, param.Explode),
 		Required:    param.Required,
+		Deprecated:  param.Deprecated,
 	}
 
 	if param.Content != nil {
diff --git a/spec.go b/spec.go
index d42cc528..fa895fb8 100644
--- a/spec.go
+++ b/spec.go
@@ -211,7 +211,7 @@ type Parameter struct {
 
 	// Specifies that a parameter is deprecated and SHOULD be transitioned out of usage.
 	// Default value is false.
-	Deprecated bool `json:"deprecated,omitempty"` // TODO: implement
+	Deprecated bool `json:"deprecated,omitempty"`
 
 	// For more complex scenarios, the content property can define the media type and schema of the parameter.
 	// A parameter MUST contain either a schema property, or a content property, but not both.
@@ -221,7 +221,7 @@ type Parameter struct {
 	// A map containing the representations for the parameter.
 	// The key is the media type and the value describes it.
 	// The map MUST only contain one entry.
-	Content map[string]Media `json:"content,omitempty"` // TODO: implement
+	Content map[string]Media `json:"content,omitempty"`
 
 	// Describes how the parameter value will be serialized
 	// depending on the type of the parameter value.
@@ -376,10 +376,10 @@ type Schema struct {
 	AllOf []*Schema `json:"allOf,omitempty"` // TODO: implement.
 
 	// OneOf validates the value against exactly one of the subschemas
-	OneOf []*Schema `json:"oneOf,omitempty"` // TODO: implement.
+	OneOf []*Schema `json:"oneOf,omitempty"`
 
 	// AnyOf validates the value against any (one or more) of the subschemas
-	AnyOf []*Schema `json:"anyOf,omitempty"` // TODO: implement.
+	AnyOf []*Schema `json:"anyOf,omitempty"`
 
 	// Discriminator for subschemas.
 	Discriminator *Discriminator `json:"discriminator,omitempty"`
@@ -387,7 +387,7 @@ type Schema struct {
 	// The value of this keyword MUST be an array.
 	// This array SHOULD have at least one element.
 	// Elements in the array SHOULD be unique.
-	Enum []json.RawMessage `json:"enum,omitempty"` // TODO: Nullable.
+	Enum []json.RawMessage `json:"enum,omitempty"`
 
 	// The value of "multipleOf" MUST be a number, strictly greater than 0.
 	//
@@ -512,7 +512,7 @@ type Schema struct {
 	MinProperties *uint64 `json:"minProperties,omitempty"`
 
 	// Default value.
-	Default json.RawMessage `json:"default,omitempty"` // TODO: support
+	Default json.RawMessage `json:"default,omitempty"`
 
 	// A free-form property to include an example of an instance for this schema.
 	// To represent examples that cannot be naturally represented in JSON or YAML,


Test patch:
diff --git a/gen/ir/description_test.go b/gen/ir/description_test.go
index 67d5ca52..4edddd34 100644
--- a/gen/ir/description_test.go
+++ b/gen/ir/description_test.go
@@ -12,8 +12,9 @@ import (
 
 func Test_prettyDoc(t *testing.T) {
 	tests := []struct {
-		input string
-		wantR []string
+		input  string
+		notice string
+		wantR  []string
 	}{
 		{
 			input: ``,
@@ -23,6 +24,11 @@ func Test_prettyDoc(t *testing.T) {
 			input: `example`,
 			wantR: []string{`Example.`},
 		},
+		{
+			input:  `example`,
+			notice: "Deprecated: for some reason.",
+			wantR:  []string{`Example.`, ``, `Deprecated: for some reason.`},
+		},
 		{
 			input: "The name of the fully qualified reference (ie: `refs/heads/master`). " +
 				"If it doesn't start with 'refs' and have at least two slashes, it will be rejected.",
@@ -67,7 +73,7 @@ func Test_prettyDoc(t *testing.T) {
 	for i, tt := range tests {
 		tt := tt
 		t.Run(fmt.Sprintf("Test%d", i+1), func(t *testing.T) {
-			require.Equal(t, tt.wantR, prettyDoc(tt.input))
+			require.Equal(t, tt.wantR, prettyDoc(tt.input, tt.notice))
 		})
 	}
 }