Skip to content

Commit 38e58ca

Browse files
authored
impl(sidekick): create skeleton for methods (#1894)
Add methods (with many details missing) to the services created from a discovery doc.
1 parent 2d9911d commit 38e58ca

File tree

6 files changed

+202
-7
lines changed

6 files changed

+202
-7
lines changed

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,9 @@ func NewAPI(serviceConfig *serviceconfig.Service, contents []byte) (*api.API, er
8383
}
8484

8585
for _, resource := range doc.Resources {
86-
addServiceRecursive(result, resource)
86+
if err := addServiceRecursive(result, resource); err != nil {
87+
return nil, err
88+
}
8789
}
8890

8991
return result, nil

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,20 @@ func TestMessageErrors(t *testing.T) {
191191
}
192192
}
193193

194+
func TestServiceErrors(t *testing.T) {
195+
for _, test := range []struct {
196+
Name string
197+
Contents string
198+
}{
199+
{"bad method", `{"resources": {"withBadMethod": {"methods": {"uploadNotSupported": { "mediaUpload": {} }}}}}`},
200+
} {
201+
contents := []byte(test.Contents)
202+
if got, err := NewAPI(nil, contents); err == nil {
203+
t.Fatalf("expected error for %s input, got=%v", test.Name, got)
204+
}
205+
}
206+
}
207+
194208
func PublicCaDisco(t *testing.T, sc *serviceconfig.Service) (*api.API, error) {
195209
t.Helper()
196210
contents, err := os.ReadFile("../../../testdata/disco/publicca.v1.json")
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
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 makeServiceMethods(model *api.API, serviceID string, resource *resource) ([]*api.Method, error) {
24+
var methods []*api.Method
25+
for _, input := range resource.Methods {
26+
method, err := makeMethod(model, serviceID, input)
27+
if err != nil {
28+
return nil, err
29+
}
30+
methods = append(methods, method)
31+
}
32+
33+
return methods, nil
34+
}
35+
36+
func makeMethod(model *api.API, serviceID string, input *method) (*api.Method, error) {
37+
id := fmt.Sprintf("%s.%s", serviceID, input.Name)
38+
if input.MediaUpload != nil {
39+
return nil, fmt.Errorf("media upload methods are not supported, id=%s", id)
40+
}
41+
inputID, err := getMethodType(model, id, "request type", input.Request)
42+
if err != nil {
43+
return nil, err
44+
}
45+
outputID, err := getMethodType(model, id, "response type", input.Response)
46+
if err != nil {
47+
return nil, err
48+
}
49+
method := &api.Method{
50+
ID: id,
51+
Name: input.Name,
52+
Documentation: input.Description,
53+
// TODO(#1850) - handle deprecated methods
54+
// Deprecated: ...,
55+
InputTypeID: inputID,
56+
OutputTypeID: outputID,
57+
}
58+
return method, nil
59+
}
60+
61+
func getMethodType(model *api.API, methodID, name string, typez *schema) (string, error) {
62+
if typez == nil {
63+
return ".google.protobuf.Empty", nil
64+
}
65+
if typez.Ref == "" {
66+
return "", fmt.Errorf("expected a ref-like schema for %s in method %s", name, methodID)
67+
}
68+
return fmt.Sprintf(".%s.%s", model.PackageName, typez.Ref), nil
69+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
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 "testing"
18+
19+
func TestMakeServiceMethodsError(t *testing.T) {
20+
model, err := PublicCaDisco(t, nil)
21+
if err != nil {
22+
t.Fatal(err)
23+
}
24+
input := &resource{
25+
Name: "testResource",
26+
Methods: []*method{
27+
{
28+
Name: "upload",
29+
MediaUpload: &mediaUpload{},
30+
},
31+
},
32+
}
33+
if methods, err := makeServiceMethods(model, "..testResource", input); err == nil {
34+
t.Errorf("expected error on method with media upload, got=%v", methods)
35+
}
36+
}
37+
38+
func TestMakeMethodError(t *testing.T) {
39+
model, err := PublicCaDisco(t, nil)
40+
if err != nil {
41+
t.Fatal(err)
42+
}
43+
44+
for _, test := range []struct {
45+
Name string
46+
Input method
47+
}{
48+
{"mediaUploadMustBeNil", method{MediaUpload: &mediaUpload{}}},
49+
{"requestMustHaveRef", method{Request: &schema{}}},
50+
{"responseMustHaveRef", method{Response: &schema{}}},
51+
} {
52+
if method, err := makeMethod(model, "..Test", &test.Input); err == nil {
53+
t.Errorf("expected error on method[%s], got=%v", test.Name, method)
54+
}
55+
}
56+
57+
}

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

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,28 +20,38 @@ import (
2020
"github.com/googleapis/librarian/internal/sidekick/internal/api"
2121
)
2222

23-
func addServiceRecursive(model *api.API, resource *resource) {
23+
func addServiceRecursive(model *api.API, resource *resource) error {
2424
if len(resource.Methods) != 0 {
25-
addService(model, resource)
25+
if err := addService(model, resource); err != nil {
26+
return err
27+
}
2628
}
2729
for _, child := range resource.Resources {
28-
addServiceRecursive(model, child)
30+
if err := addServiceRecursive(model, child); err != nil {
31+
return err
32+
}
2933
}
34+
return nil
3035
}
3136

32-
func addService(model *api.API, resource *resource) {
37+
func addService(model *api.API, resource *resource) error {
3338
id := fmt.Sprintf(".%s.%s", model.PackageName, resource.Name)
39+
methods, err := makeServiceMethods(model, id, resource)
40+
if err != nil {
41+
return err
42+
}
43+
3444
var service *api.Service
3545
if _, ok := model.State.ServiceByID[id]; !ok {
3646
service = &api.Service{
3747
ID: id,
3848
Name: resource.Name,
3949
Package: model.PackageName,
4050
Documentation: fmt.Sprintf("Service for the `%s` resource.", resource.Name),
51+
Methods: methods,
4152
}
4253
model.Services = append(model.Services, service)
4354
model.State.ServiceByID[id] = service
4455
}
45-
// TODO(#1850) - add the methods, if the service already exists then merge
46-
// the methods.
56+
return nil
4757
}

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

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,49 @@ func TestService(t *testing.T) {
4040
ID: id,
4141
Package: "",
4242
Documentation: "Service for the `externalAccountKeys` resource.",
43+
Methods: []*api.Method{
44+
{
45+
ID: "..externalAccountKeys.create",
46+
Name: "create",
47+
Documentation: "Creates a new ExternalAccountKey bound to the project.",
48+
InputTypeID: "..ExternalAccountKey",
49+
OutputTypeID: "..ExternalAccountKey",
50+
},
51+
},
4352
}
4453
apitest.CheckService(t, got, want)
4554
}
55+
56+
func TestServiceTopLevelMethodErrors(t *testing.T) {
57+
model, err := PublicCaDisco(t, nil)
58+
if err != nil {
59+
t.Fatal(err)
60+
}
61+
input := resource{
62+
Methods: []*method{
63+
{MediaUpload: &mediaUpload{}},
64+
},
65+
}
66+
if err := addServiceRecursive(model, &input); err == nil {
67+
t.Errorf("expected error in addServiceRecursive invalid top-level method, got=%v", model.Services)
68+
}
69+
}
70+
71+
func TestServiceChildMethodErrors(t *testing.T) {
72+
model, err := PublicCaDisco(t, nil)
73+
if err != nil {
74+
t.Fatal(err)
75+
}
76+
input := resource{
77+
Resources: []*resource{
78+
{
79+
Methods: []*method{
80+
{MediaUpload: &mediaUpload{}},
81+
},
82+
},
83+
},
84+
}
85+
if err := addServiceRecursive(model, &input); err == nil {
86+
t.Errorf("expected error in addServiceRecursive invalid child method, got=%v", model.Services)
87+
}
88+
}

0 commit comments

Comments
 (0)