Skip to content

Commit 1d83853

Browse files
authored
feat(sidekick): deprecated elements in discovery docs (#2399)
1 parent 2f52aa7 commit 1d83853

File tree

15 files changed

+392
-11
lines changed

15 files changed

+392
-11
lines changed

internal/sidekick/internal/parser/disco_test.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,26 @@ func TestDisco_ParsePagination(t *testing.T) {
111111
}
112112
}
113113

114+
func TestDisco_ParseDeprecatedEnum(t *testing.T) {
115+
model, err := ParseDisco(discoSourceFile, "", map[string]string{})
116+
if err != nil {
117+
t.Fatal(err)
118+
}
119+
wantEnum := &api.Enum{
120+
ID: "..AcceleratorTypeAggregatedList.warning.code",
121+
}
122+
got, ok := model.State.EnumByID[wantEnum.ID]
123+
if !ok {
124+
t.Fatalf("expected method %s in the API model", wantEnum.ID)
125+
}
126+
if len(got.Values) < 7 {
127+
t.Fatalf("expected at least 7 values in the enum value list, got=%v", got)
128+
}
129+
if !got.Values[6].Deprecated {
130+
t.Errorf("expected a deprecated enum value, got=%v", got.Values[6])
131+
}
132+
}
133+
114134
func TestDisco_ParseBadFiles(t *testing.T) {
115135
if _, err := ParseDisco("-invalid-file-name-", secretManagerYamlFullPath, map[string]string{}); err == nil {
116136
t.Fatalf("expected error with missing source file")

internal/sidekick/internal/parser/discovery/discovery.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -212,9 +212,11 @@ type schema struct {
212212
Ref string `json:"$ref"`
213213
Default string
214214
Pattern string
215+
Deprecated bool
215216
Enums []string `json:"enum"`
216217
// Google extensions to JSON Schema
217218
EnumDescriptions []string
219+
EnumDeprecated []bool
218220
Variant *variant
219221

220222
RefSchema *schema `json:"-"` // Schema referred to by $ref
@@ -373,10 +375,11 @@ func (rl *resourceList) UnmarshalJSON(data []byte) error {
373375

374376
// A resource holds information about a Google API resource.
375377
type resource struct {
376-
Name string
377-
FullName string // {parent.FullName}.{Name}
378-
Methods methodList
379-
Resources resourceList
378+
Name string
379+
FullName string // {parent.FullName}.{Name}
380+
Methods methodList
381+
Resources resourceList
382+
Deprecated bool
380383
}
381384

382385
func (r *resource) init(parentFullName string, topLevelSchemas map[string]*schema) error {
@@ -426,6 +429,7 @@ type method struct {
426429
MediaUpload *mediaUpload
427430
SupportsMediaDownload bool
428431
APIVersion string
432+
Deprecated bool
429433
}
430434

431435
type mediaUpload struct {

internal/sidekick/internal/parser/discovery/discovery_test.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"testing"
2222

2323
"github.com/google/go-cmp/cmp"
24+
"github.com/google/go-cmp/cmp/cmpopts"
2425
"github.com/googleapis/librarian/internal/sidekick/internal/api"
2526
"github.com/googleapis/librarian/internal/sidekick/internal/api/apitest"
2627
"github.com/googleapis/librarian/internal/sidekick/internal/sample"
@@ -169,6 +170,35 @@ func TestMessage(t *testing.T) {
169170
apitest.CheckMessage(t, got, want)
170171
}
171172

173+
func TestDeprecatedField(t *testing.T) {
174+
model, err := ComputeDisco(t, nil)
175+
if err != nil {
176+
t.Fatal(err)
177+
}
178+
id := "..BackendService"
179+
gotMessage, ok := model.State.MessageByID[id]
180+
if !ok {
181+
t.Fatalf("expected message %s in the API model", id)
182+
}
183+
idx := slices.IndexFunc(gotMessage.Fields, func(f *api.Field) bool { return f.Name == "port" })
184+
if idx == -1 {
185+
t.Fatalf("expected a `port` field in the message, got=%v", gotMessage)
186+
}
187+
gotField := gotMessage.Fields[idx]
188+
wantField := &api.Field{
189+
Name: "port",
190+
JSONName: "port",
191+
ID: "..BackendService.port",
192+
Deprecated: true,
193+
Documentation: gotField.Documentation,
194+
Typez: api.INT32_TYPE,
195+
TypezID: "int32",
196+
}
197+
if diff := cmp.Diff(wantField, gotField, cmpopts.IgnoreFields(api.Field{}, "Parent")); diff != "" {
198+
t.Errorf("mismatch (-want, +got):\n%s", diff)
199+
}
200+
}
201+
172202
func TestMessageErrors(t *testing.T) {
173203
for _, test := range []struct {
174204
Name string

internal/sidekick/internal/parser/discovery/enum.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,21 +27,31 @@ func makeMessageEnum(model *api.API, message *api.Message, name string, schema *
2727
if len(schema.Enums) != len(schema.EnumDescriptions) {
2828
return fmt.Errorf("mismatched enum value list vs. enum value descriptions list")
2929
}
30+
if len(schema.EnumDeprecated) != 0 && len(schema.Enums) != len(schema.EnumDeprecated) {
31+
// The list of deprecated enums is omitted in some cases.
32+
return fmt.Errorf("mismatched enum value list vs. enum deprecated values list")
33+
}
3034
id := fmt.Sprintf("%s.%s", message.ID, name)
3135
enum := &api.Enum{
3236
Name: name,
3337
ID: id,
3438
Package: message.Package,
3539
Documentation: fmt.Sprintf("The enumerated type for the [%s][%s] field.", name, id[1:]),
40+
Deprecated: schema.Deprecated,
3641
Parent: message,
3742
}
3843
for number, name := range schema.Enums {
44+
deprecated := false
45+
if len(schema.EnumDeprecated) != 0 {
46+
deprecated = schema.EnumDeprecated[number]
47+
}
3948
value := &api.EnumValue{
4049
Name: name,
4150
Number: int32(number),
4251
ID: fmt.Sprintf("%s.%s", enum.ID, name),
4352
Documentation: schema.EnumDescriptions[number],
4453
Parent: enum,
54+
Deprecated: deprecated,
4555
}
4656
enum.Values = append(enum.Values, value)
4757
enum.UniqueNumberValues = append(enum.UniqueNumberValues, value)

internal/sidekick/internal/parser/discovery/enum_field_test.go

Lines changed: 223 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,196 @@ func TestMakeEnumFields(t *testing.T) {
120120
}
121121
}
122122

123-
func TestMakeEnumFieldsError(t *testing.T) {
123+
func TestMakeEnumFieldsDeprecated(t *testing.T) {
124+
model := api.NewTestAPI([]*api.Message{}, []*api.Enum{}, []*api.Service{})
125+
model.PackageName = "package"
126+
input := &schema{
127+
Properties: []*property{
128+
{
129+
Name: "networkTier",
130+
Schema: &schema{
131+
Description: "The networking tier.",
132+
Deprecated: true,
133+
Enums: []string{
134+
"FIXED_STANDARD",
135+
"PREMIUM",
136+
},
137+
EnumDescriptions: []string{
138+
"Public internet quality with fixed bandwidth.",
139+
"High quality, Google-grade network tier, support for all networking products.",
140+
},
141+
Type: "string",
142+
},
143+
},
144+
},
145+
}
146+
message := &api.Message{ID: ".package.Message"}
147+
if err := makeMessageFields(model, message, input); err != nil {
148+
t.Fatal(err)
149+
}
150+
151+
wantEnum := &api.Enum{
152+
Name: "networkTier",
153+
ID: ".package.Message.networkTier",
154+
Documentation: "The enumerated type for the [networkTier][package.Message.networkTier] field.",
155+
Deprecated: true,
156+
Values: []*api.EnumValue{
157+
{
158+
Name: "FIXED_STANDARD",
159+
ID: ".package.Message.networkTier.FIXED_STANDARD",
160+
Number: 0,
161+
Documentation: "Public internet quality with fixed bandwidth.",
162+
},
163+
{
164+
Name: "PREMIUM",
165+
ID: ".package.Message.networkTier.PREMIUM",
166+
Number: 1,
167+
Documentation: "High quality, Google-grade network tier, support for all networking products.",
168+
},
169+
},
170+
}
171+
wantEnum.UniqueNumberValues = wantEnum.Values
172+
gotEnum, ok := model.State.EnumByID[wantEnum.ID]
173+
if !ok {
174+
t.Fatalf("missing enum %s", wantEnum.ID)
175+
}
176+
apitest.CheckEnum(t, *gotEnum, *wantEnum)
177+
if gotEnum.Parent == nil {
178+
t.Errorf("expected non-nil parent in enum: %v", gotEnum)
179+
}
180+
for _, value := range gotEnum.Values {
181+
if value.Parent != gotEnum {
182+
t.Errorf("mismatched parent in enumValue: %v", value)
183+
}
184+
}
185+
186+
want := &api.Message{
187+
ID: ".package.Message",
188+
Fields: []*api.Field{
189+
{
190+
Name: "networkTier",
191+
JSONName: "networkTier",
192+
ID: ".package.Message.networkTier",
193+
Documentation: "The networking tier.",
194+
Deprecated: true,
195+
Typez: api.ENUM_TYPE,
196+
TypezID: ".package.Message.networkTier",
197+
},
198+
},
199+
}
200+
apitest.CheckMessage(t, message, want)
201+
wantEnums := []*api.Enum{wantEnum}
202+
if diff := cmp.Diff(wantEnums, message.Enums, cmpopts.IgnoreFields(api.Enum{}, "Parent"), cmpopts.IgnoreFields(api.EnumValue{}, "Parent")); diff != "" {
203+
t.Errorf("mismatch (-want, +got):\n%s", diff)
204+
}
205+
}
206+
207+
func TestMakeEnumFieldsWithDeprecatedValues(t *testing.T) {
208+
model := api.NewTestAPI([]*api.Message{}, []*api.Enum{}, []*api.Service{})
209+
model.PackageName = "package"
210+
input := &schema{
211+
Properties: []*property{
212+
{
213+
Name: "networkTier",
214+
Schema: &schema{
215+
Description: "The networking tier.",
216+
Enums: []string{
217+
"FIXED_STANDARD",
218+
"PREMIUM",
219+
"STANDARD",
220+
"STANDARD_OVERRIDES_FIXED_STANDARD",
221+
},
222+
EnumDescriptions: []string{
223+
"Public internet quality with fixed bandwidth.",
224+
"High quality, Google-grade network tier, support for all networking products.",
225+
"Public internet quality, only limited support for other networking products.",
226+
"(Output only) Temporary tier for FIXED_STANDARD when fixed standard tier is expired or not configured.",
227+
},
228+
EnumDeprecated: []bool{
229+
true,
230+
false,
231+
true,
232+
false,
233+
},
234+
Type: "string",
235+
},
236+
},
237+
},
238+
}
239+
message := &api.Message{ID: ".package.Message"}
240+
if err := makeMessageFields(model, message, input); err != nil {
241+
t.Fatal(err)
242+
}
243+
244+
wantEnum := &api.Enum{
245+
Name: "networkTier",
246+
ID: ".package.Message.networkTier",
247+
Documentation: "The enumerated type for the [networkTier][package.Message.networkTier] field.",
248+
Values: []*api.EnumValue{
249+
{
250+
Name: "FIXED_STANDARD",
251+
ID: ".package.Message.networkTier.FIXED_STANDARD",
252+
Number: 0,
253+
Documentation: "Public internet quality with fixed bandwidth.",
254+
Deprecated: true,
255+
},
256+
{
257+
Name: "PREMIUM",
258+
ID: ".package.Message.networkTier.PREMIUM",
259+
Number: 1,
260+
Documentation: "High quality, Google-grade network tier, support for all networking products.",
261+
},
262+
{
263+
Name: "STANDARD",
264+
ID: ".package.Message.networkTier.STANDARD",
265+
Number: 2,
266+
Documentation: "Public internet quality, only limited support for other networking products.",
267+
Deprecated: true,
268+
},
269+
{
270+
Name: "STANDARD_OVERRIDES_FIXED_STANDARD",
271+
ID: ".package.Message.networkTier.STANDARD_OVERRIDES_FIXED_STANDARD",
272+
Number: 3,
273+
Documentation: "(Output only) Temporary tier for FIXED_STANDARD when fixed standard tier is expired or not configured.",
274+
},
275+
},
276+
}
277+
wantEnum.UniqueNumberValues = wantEnum.Values
278+
gotEnum, ok := model.State.EnumByID[wantEnum.ID]
279+
if !ok {
280+
t.Fatalf("missing enum %s", wantEnum.ID)
281+
}
282+
apitest.CheckEnum(t, *gotEnum, *wantEnum)
283+
if gotEnum.Parent == nil {
284+
t.Errorf("expected non-nil parent in enum: %v", gotEnum)
285+
}
286+
for _, value := range gotEnum.Values {
287+
if value.Parent != gotEnum {
288+
t.Errorf("mismatched parent in enumValue: %v", value)
289+
}
290+
}
291+
292+
want := &api.Message{
293+
ID: ".package.Message",
294+
Fields: []*api.Field{
295+
{
296+
Name: "networkTier",
297+
JSONName: "networkTier",
298+
ID: ".package.Message.networkTier",
299+
Documentation: "The networking tier.",
300+
Typez: api.ENUM_TYPE,
301+
TypezID: ".package.Message.networkTier",
302+
},
303+
},
304+
}
305+
apitest.CheckMessage(t, message, want)
306+
wantEnums := []*api.Enum{wantEnum}
307+
if diff := cmp.Diff(wantEnums, message.Enums, cmpopts.IgnoreFields(api.Enum{}, "Parent"), cmpopts.IgnoreFields(api.EnumValue{}, "Parent")); diff != "" {
308+
t.Errorf("mismatch (-want, +got):\n%s", diff)
309+
}
310+
}
311+
312+
func TestMakeEnumFieldsDescriptionError(t *testing.T) {
124313
model := api.NewTestAPI([]*api.Message{}, []*api.Enum{}, []*api.Service{})
125314
model.PackageName = "package"
126315
input := &schema{
@@ -149,3 +338,36 @@ func TestMakeEnumFieldsError(t *testing.T) {
149338
t.Errorf("expected error in enum with mismatched description count, got=%v", message)
150339
}
151340
}
341+
342+
func TestMakeEnumFieldsDeprecatedError(t *testing.T) {
343+
model := api.NewTestAPI([]*api.Message{}, []*api.Enum{}, []*api.Service{})
344+
model.PackageName = "package"
345+
input := &schema{
346+
Properties: []*property{
347+
{
348+
Name: "networkTier",
349+
Schema: &schema{
350+
Description: "The networking tier.",
351+
Enums: []string{
352+
"FIXED_STANDARD",
353+
"PREMIUM",
354+
},
355+
EnumDescriptions: []string{
356+
"Public internet quality with fixed bandwidth.",
357+
"High quality, Google-grade network tier, support for all networking products.",
358+
},
359+
EnumDeprecated: []bool{
360+
false,
361+
true,
362+
true,
363+
},
364+
Type: "string",
365+
},
366+
},
367+
},
368+
}
369+
message := &api.Message{ID: ".package.Message"}
370+
if err := makeMessageFields(model, message, input); err == nil {
371+
t.Errorf("expected error in enum with mismatched deprecated values count, got=%v", message)
372+
}
373+
}

internal/sidekick/internal/parser/discovery/inline_object_field.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ func maybeInlineObjectField(model *api.API, parent *api.Message, name string, in
4444
JSONName: name,
4545
ID: id,
4646
Documentation: input.Description,
47+
Deprecated: input.Deprecated,
4748
Optional: true,
4849
Typez: api.MESSAGE_TYPE,
4950
TypezID: id,

internal/sidekick/internal/parser/discovery/inline_object_field_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ func TestMaybeInlineObject(t *testing.T) {
2828
input := &schema{
2929
Type: "object",
3030
Description: "A field with an inline object.",
31+
Deprecated: true,
3132
Properties: []*property{
3233
{
3334
Name: "stringField",
@@ -57,6 +58,7 @@ func TestMaybeInlineObject(t *testing.T) {
5758
JSONName: "inline",
5859
ID: ".package.Message.inline",
5960
Documentation: "A field with an inline object.",
61+
Deprecated: true,
6062
Optional: true,
6163
Typez: api.MESSAGE_TYPE,
6264
TypezID: ".package.Message.inline",

0 commit comments

Comments
 (0)