Skip to content

Commit 13962d7

Browse files
authored
feat(sidekick): parse discovery doc enums (#2358)
1 parent 95d403b commit 13962d7

File tree

4 files changed

+207
-1
lines changed

4 files changed

+207
-1
lines changed

internal/sidekick/internal/api/apitest/apitest.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import (
2626
func CheckMessage(t *testing.T, got *api.Message, want *api.Message) {
2727
t.Helper()
2828
// Checking Parent, Messages, Fields, and OneOfs requires special handling.
29-
if diff := cmp.Diff(want, got, cmpopts.IgnoreFields(api.Message{}, "Fields", "OneOfs", "Parent", "Messages")); diff != "" {
29+
if diff := cmp.Diff(want, got, cmpopts.IgnoreFields(api.Message{}, "Fields", "OneOfs", "Parent", "Messages", "Enums")); diff != "" {
3030
t.Errorf("message attributes mismatch (-want +got):\n%s", diff)
3131
}
3232
less := func(a, b *api.Field) bool { return a.Name < b.Name }
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package discovery
16+
17+
import (
18+
"fmt"
19+
20+
"github.com/googleapis/librarian/internal/sidekick/internal/api"
21+
)
22+
23+
func makeMessageEnum(model *api.API, message *api.Message, name string, schema *schema) error {
24+
if schema.Enums == nil {
25+
return nil
26+
}
27+
if len(schema.Enums) != len(schema.EnumDescriptions) {
28+
return fmt.Errorf("mismatched enum value list vs. enum value descriptions list")
29+
}
30+
id := fmt.Sprintf("%s.%s", message.ID, name)
31+
enum := &api.Enum{
32+
Name: name,
33+
ID: id,
34+
Package: message.Package,
35+
Documentation: fmt.Sprintf("The enumerated type for the [%s][%s] field.", name, id[1:]),
36+
Parent: message,
37+
}
38+
for number, name := range schema.Enums {
39+
value := &api.EnumValue{
40+
Name: name,
41+
Number: int32(number),
42+
ID: fmt.Sprintf("%s.%s", enum.ID, name),
43+
Documentation: schema.EnumDescriptions[number],
44+
Parent: enum,
45+
}
46+
enum.Values = append(enum.Values, value)
47+
}
48+
model.State.EnumByID[enum.ID] = enum
49+
message.Enums = append(message.Enums, enum)
50+
return nil
51+
}
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
// Copyright 2025 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// https://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package discovery
16+
17+
import (
18+
"testing"
19+
20+
"github.com/google/go-cmp/cmp"
21+
"github.com/google/go-cmp/cmp/cmpopts"
22+
"github.com/googleapis/librarian/internal/sidekick/internal/api"
23+
"github.com/googleapis/librarian/internal/sidekick/internal/api/apitest"
24+
)
25+
26+
func TestMakeEnumFields(t *testing.T) {
27+
model := api.NewTestAPI([]*api.Message{}, []*api.Enum{}, []*api.Service{})
28+
model.PackageName = "package"
29+
input := &schema{
30+
Properties: []*property{
31+
{
32+
Name: "networkTier",
33+
Schema: &schema{
34+
Description: "The networking tier.",
35+
Enums: []string{
36+
"FIXED_STANDARD",
37+
"PREMIUM",
38+
"STANDARD",
39+
"STANDARD_OVERRIDES_FIXED_STANDARD",
40+
},
41+
EnumDescriptions: []string{
42+
"Public internet quality with fixed bandwidth.",
43+
"High quality, Google-grade network tier, support for all networking products.",
44+
"Public internet quality, only limited support for other networking products.",
45+
"(Output only) Temporary tier for FIXED_STANDARD when fixed standard tier is expired or not configured.",
46+
},
47+
Type: "string",
48+
},
49+
},
50+
},
51+
}
52+
message := &api.Message{ID: ".package.Message"}
53+
if err := makeMessageFields(model, message, input); err != nil {
54+
t.Fatal(err)
55+
}
56+
57+
wantEnum := &api.Enum{
58+
Name: "networkTier",
59+
ID: ".package.Message.networkTier",
60+
Documentation: "The enumerated type for the [networkTier][package.Message.networkTier] field.",
61+
Values: []*api.EnumValue{
62+
{
63+
Name: "FIXED_STANDARD",
64+
ID: ".package.Message.networkTier.FIXED_STANDARD",
65+
Number: 0,
66+
Documentation: "Public internet quality with fixed bandwidth.",
67+
},
68+
{
69+
Name: "PREMIUM",
70+
ID: ".package.Message.networkTier.PREMIUM",
71+
Number: 1,
72+
Documentation: "High quality, Google-grade network tier, support for all networking products.",
73+
},
74+
{
75+
Name: "STANDARD",
76+
ID: ".package.Message.networkTier.STANDARD",
77+
Number: 2,
78+
Documentation: "Public internet quality, only limited support for other networking products.",
79+
},
80+
{
81+
Name: "STANDARD_OVERRIDES_FIXED_STANDARD",
82+
ID: ".package.Message.networkTier.STANDARD_OVERRIDES_FIXED_STANDARD",
83+
Number: 3,
84+
Documentation: "(Output only) Temporary tier for FIXED_STANDARD when fixed standard tier is expired or not configured.",
85+
},
86+
},
87+
}
88+
gotEnum, ok := model.State.EnumByID[wantEnum.ID]
89+
if !ok {
90+
t.Fatalf("missing enum %s", wantEnum.ID)
91+
}
92+
apitest.CheckEnum(t, *gotEnum, *wantEnum)
93+
if gotEnum.Parent == nil {
94+
t.Errorf("expected non-nil parent in enum: %v", gotEnum)
95+
}
96+
for _, value := range gotEnum.Values {
97+
if value.Parent != gotEnum {
98+
t.Errorf("mismatched parent in enumValue: %v", value)
99+
}
100+
}
101+
102+
want := &api.Message{
103+
ID: ".package.Message",
104+
Fields: []*api.Field{
105+
{
106+
Name: "networkTier",
107+
JSONName: "networkTier",
108+
Documentation: "The networking tier.",
109+
Typez: api.ENUM_TYPE,
110+
TypezID: ".package.Message.networkTier",
111+
},
112+
},
113+
}
114+
apitest.CheckMessage(t, message, want)
115+
wantEnums := []*api.Enum{wantEnum}
116+
if diff := cmp.Diff(wantEnums, message.Enums, cmpopts.IgnoreFields(api.Enum{}, "Parent"), cmpopts.IgnoreFields(api.EnumValue{}, "Parent")); diff != "" {
117+
t.Errorf("mismatch (-want, +got):\n%s", diff)
118+
}
119+
}
120+
121+
func TestMakeEnumFieldsError(t *testing.T) {
122+
model := api.NewTestAPI([]*api.Message{}, []*api.Enum{}, []*api.Service{})
123+
model.PackageName = "package"
124+
input := &schema{
125+
Properties: []*property{
126+
{
127+
Name: "networkTier",
128+
Schema: &schema{
129+
Description: "The networking tier.",
130+
Enums: []string{
131+
"FIXED_STANDARD",
132+
"PREMIUM",
133+
},
134+
EnumDescriptions: []string{
135+
"Public internet quality with fixed bandwidth.",
136+
"High quality, Google-grade network tier, support for all networking products.",
137+
"Public internet quality, only limited support for other networking products.",
138+
"(Output only) Temporary tier for FIXED_STANDARD when fixed standard tier is expired or not configured.",
139+
},
140+
Type: "string",
141+
},
142+
},
143+
},
144+
}
145+
message := &api.Message{ID: ".package.Message"}
146+
if err := makeMessageFields(model, message, input); err == nil {
147+
t.Errorf("expected error in enum with mismatched description count, got=%v", message)
148+
}
149+
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ func makeArrayField(model *api.API, message *api.Message, input *property) (*api
6565
}
6666

6767
func makeScalarField(model *api.API, message *api.Message, name string, schema *schema) (*api.Field, error) {
68+
if err := makeMessageEnum(model, message, name, schema); err != nil {
69+
return nil, err
70+
}
6871
typez, typezID, err := scalarType(model, message.ID, name, schema)
6972
if err != nil {
7073
return nil, err
@@ -128,6 +131,9 @@ func scalarTypeForNumberFormats(messageID, name string, input *schema) (api.Type
128131
}
129132

130133
func scalarTypeForStringFormats(messageID, name string, input *schema) (api.Typez, string, error) {
134+
if input.Enums != nil {
135+
return api.ENUM_TYPE, fmt.Sprintf("%s.%s", messageID, name), nil
136+
}
131137
switch input.Format {
132138
case "":
133139
return api.STRING_TYPE, "string", nil

0 commit comments

Comments
 (0)