Skip to content

Commit cfcb46f

Browse files
authored
feat(internal/librarian/golang): fill Go module with API defaults (#4097)
Fill Go module, e.g., import path, client directory with defaults derived from API path. Skip non cloud APIs. This change can reduce config size of handwritten libraries with multiple API paths. For #3617
1 parent e61ac1f commit cfcb46f

3 files changed

Lines changed: 212 additions & 2 deletions

File tree

internal/librarian/golang/module.go

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,47 @@
1414

1515
package golang
1616

17-
import "github.com/googleapis/librarian/internal/config"
17+
import (
18+
"fmt"
19+
"strings"
20+
21+
"github.com/googleapis/librarian/internal/config"
22+
)
23+
24+
// Fill populates empty Go-specific fields from the api path.
25+
// Library configs takes precedence.
26+
func Fill(library *config.Library) *config.Library {
27+
if library.Go == nil {
28+
library.Go = &config.GoModule{}
29+
}
30+
var goAPIs []*config.GoAPI
31+
for _, api := range library.APIs {
32+
goAPI := findGoAPI(library, api.Path)
33+
if !strings.HasPrefix(api.Path, "google/cloud/") {
34+
// Do nothing for non cloud API.
35+
if goAPI != nil {
36+
goAPIs = append(goAPIs, goAPI)
37+
}
38+
continue
39+
}
40+
if goAPI == nil {
41+
goAPI = &config.GoAPI{
42+
Path: api.Path,
43+
}
44+
}
45+
importPath, clientDir := defaultImportPathAndClientDir(api.Path)
46+
if goAPI.ImportPath == "" {
47+
goAPI.ImportPath = importPath
48+
}
49+
if goAPI.ClientDirectory == "" {
50+
goAPI.ClientDirectory = clientDir
51+
}
52+
goAPIs = append(goAPIs, goAPI)
53+
}
54+
library.Go.GoAPIs = goAPIs
55+
56+
return library
57+
}
1858

1959
func findGoAPI(library *config.Library, apiPath string) *config.GoAPI {
2060
if library.Go == nil {
@@ -27,3 +67,19 @@ func findGoAPI(library *config.Library, apiPath string) *config.GoAPI {
2767
}
2868
return nil
2969
}
70+
71+
// defaultImportPathAndClientDir returns the default Go import path and client directory
72+
// based on the provided API path.
73+
//
74+
// The API path is expected to be either google/cloud/{dir}/{version} or
75+
// google/cloud/{dir}/{nested}/{version}.
76+
func defaultImportPathAndClientDir(apiPath string) (string, string) {
77+
dirs := strings.Split(apiPath, "/")
78+
if len(dirs) < 4 {
79+
return "", ""
80+
}
81+
if len(dirs) == 5 {
82+
return fmt.Sprintf("%s/%s", dirs[2], dirs[3]), dirs[3]
83+
}
84+
return dirs[2], ""
85+
}

internal/librarian/golang/module_test.go

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,149 @@ import (
2121
"github.com/googleapis/librarian/internal/config"
2222
)
2323

24+
func TestFill(t *testing.T) {
25+
for _, test := range []struct {
26+
name string
27+
library *config.Library
28+
want *config.Library
29+
}{
30+
{
31+
name: "fill default import path",
32+
library: &config.Library{
33+
Name: "secretmanager",
34+
APIs: []*config.API{{Path: "google/cloud/secretmanager/v1"}},
35+
},
36+
want: &config.Library{
37+
Name: "secretmanager",
38+
APIs: []*config.API{{Path: "google/cloud/secretmanager/v1"}},
39+
Go: &config.GoModule{
40+
GoAPIs: []*config.GoAPI{
41+
{
42+
Path: "google/cloud/secretmanager/v1",
43+
ImportPath: "secretmanager",
44+
},
45+
},
46+
},
47+
},
48+
},
49+
{
50+
name: "fill default import path and client directory",
51+
library: &config.Library{
52+
Name: "bigquery",
53+
APIs: []*config.API{
54+
{
55+
Path: "google/cloud/bigquery/analyticshub/v1",
56+
},
57+
{
58+
Path: "google/cloud/bigquery/biglake/v1",
59+
},
60+
},
61+
},
62+
want: &config.Library{
63+
Name: "bigquery",
64+
APIs: []*config.API{
65+
{
66+
Path: "google/cloud/bigquery/analyticshub/v1",
67+
},
68+
{
69+
Path: "google/cloud/bigquery/biglake/v1",
70+
},
71+
},
72+
Go: &config.GoModule{
73+
GoAPIs: []*config.GoAPI{
74+
{
75+
Path: "google/cloud/bigquery/analyticshub/v1",
76+
ClientDirectory: "analyticshub",
77+
ImportPath: "bigquery/analyticshub",
78+
},
79+
{
80+
Path: "google/cloud/bigquery/biglake/v1",
81+
ClientDirectory: "biglake",
82+
ImportPath: "bigquery/biglake",
83+
},
84+
},
85+
},
86+
},
87+
},
88+
{
89+
name: "skip non cloud api with nil Go module",
90+
library: &config.Library{
91+
Name: "ai",
92+
APIs: []*config.API{{Path: "google/ai/generativelanguage/v1"}},
93+
},
94+
want: &config.Library{
95+
Name: "ai",
96+
APIs: []*config.API{{Path: "google/ai/generativelanguage/v1"}},
97+
Go: &config.GoModule{},
98+
},
99+
},
100+
{
101+
name: "skip non cloud api with Go module",
102+
library: &config.Library{
103+
Name: "ai",
104+
APIs: []*config.API{{Path: "google/ai/generativelanguage/v1"}},
105+
Go: &config.GoModule{
106+
GoAPIs: []*config.GoAPI{
107+
{
108+
Path: "google/ai/generativelanguage/v1",
109+
ClientDirectory: "generativelanguage",
110+
},
111+
},
112+
},
113+
},
114+
want: &config.Library{
115+
Name: "ai",
116+
APIs: []*config.API{{Path: "google/ai/generativelanguage/v1"}},
117+
Go: &config.GoModule{
118+
GoAPIs: []*config.GoAPI{
119+
{
120+
Path: "google/ai/generativelanguage/v1",
121+
ClientDirectory: "generativelanguage",
122+
},
123+
},
124+
},
125+
},
126+
},
127+
{
128+
name: "defaults do not override library config",
129+
library: &config.Library{
130+
Name: "example",
131+
APIs: []*config.API{{Path: "google/cloud/example/v1"}},
132+
Go: &config.GoModule{
133+
DeleteGenerationOutputPaths: []string{"example"},
134+
GoAPIs: []*config.GoAPI{
135+
{
136+
Path: "google/cloud/example/v1",
137+
NoRESTNumericEnums: true, // this value will be kept.
138+
},
139+
},
140+
},
141+
},
142+
want: &config.Library{
143+
Name: "example",
144+
APIs: []*config.API{{Path: "google/cloud/example/v1"}},
145+
Go: &config.GoModule{
146+
DeleteGenerationOutputPaths: []string{"example"},
147+
GoAPIs: []*config.GoAPI{
148+
{
149+
Path: "google/cloud/example/v1",
150+
ImportPath: "example",
151+
NoRESTNumericEnums: true,
152+
},
153+
},
154+
},
155+
},
156+
},
157+
} {
158+
t.Run(test.name, func(t *testing.T) {
159+
got := Fill(test.library)
160+
if diff := cmp.Diff(test.want, got); diff != "" {
161+
t.Errorf("mismatch (-want +got):\n%s", diff)
162+
}
163+
})
164+
}
165+
}
166+
24167
func TestFindGoAPI(t *testing.T) {
25168
for _, test := range []struct {
26169
name string

internal/librarian/library.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"strings"
2121

2222
"github.com/googleapis/librarian/internal/config"
23+
"github.com/googleapis/librarian/internal/librarian/golang"
2324
)
2425

2526
// fillDefaults populates empty library fields from the provided defaults.
@@ -179,7 +180,7 @@ func applyDefaults(language string, lib *config.Library, defaults *config.Defaul
179180
}
180181
lib.Output = defaultOutput(language, lib.Name, lib.APIs[0].Path, defaults.Output)
181182
}
182-
return fillDefaults(lib, defaults), nil
183+
return fillLibraryDefaults(language, fillDefaults(lib, defaults))
183184
}
184185

185186
// mergeMaps merges key-values of src and dst maps.
@@ -193,3 +194,13 @@ func mergeMaps(dst, src map[string]string) map[string]string {
193194
}
194195
return res
195196
}
197+
198+
// fillLibraryDefaults populates language-specific default values for the library.
199+
func fillLibraryDefaults(language string, lib *config.Library) (*config.Library, error) {
200+
switch language {
201+
case languageGo:
202+
return golang.Fill(lib), nil
203+
default:
204+
return lib, nil
205+
}
206+
}

0 commit comments

Comments
 (0)