Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

impl: read service config #83

Merged
merged 1 commit into from
Nov 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 20 additions & 8 deletions generator/cmd/protoc-gen-gclient/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,17 +73,26 @@ func run(r io.Reader, w io.Writer, inputPath, outDir, templateDir string) error
// If capture-input is set, content pass to protoc will be written to a
// sample-input-{timestamp}.bin file, so that protoc does not need to be
// used on future iterations.
if opts.CaptureInput {
if opts.captureInput {
if err := captureInput(genReq); err != nil {
return err
}
}

req, err := protobuf.Translate(genReq, &protobuf.Options{
popts := &protobuf.Options{
OutDir: outDir,
Language: opts.Language,
Language: opts.language,
TemplateDir: templateDir,
})
}
if opts.serviceConfig != "" {
cfg, err := genclient.ReadServiceConfig(opts.serviceConfig)
if err != nil {
return err
}
popts.ServiceConfig = cfg
}

req, err := protobuf.Translate(genReq, popts)
if err != nil {
return err
}
Expand All @@ -102,8 +111,9 @@ func run(r io.Reader, w io.Writer, inputPath, outDir, templateDir string) error
}

type protobufOptions struct {
CaptureInput bool
Language string
captureInput bool
language string
serviceConfig string
}

func parseOpts(optStr string) (*protobufOptions, error) {
Expand All @@ -119,15 +129,17 @@ func parseOpts(optStr string) (*protobufOptions, error) {
continue
}
switch sp[0] {
case "service-config":
opts.serviceConfig = strings.TrimSpace(sp[1])
case "capture-input":
b, err := strconv.ParseBool(sp[1])
if err != nil {
slog.Error("invalid bool in option string, skipping", "option", s)
return nil, err
}
opts.CaptureInput = b
opts.captureInput = b
case "language":
opts.Language = strings.ToLower(strings.TrimSpace(sp[1]))
opts.language = strings.ToLower(strings.TrimSpace(sp[1]))
default:
slog.Warn("unknown option", "option", s)
}
Expand Down
11 changes: 6 additions & 5 deletions generator/devtools/cmd/generate/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,11 @@ import (
)

var (
output = flag.String("out", "output", "the path to the output directory")
language = flag.String("language", "", "the generated language")
protoFiles = flag.String("files", "testdata/googleapis/google/cloud/secretmanager/v1/", "path to protos to generate from")
protoPath = flag.String("proto_path", "testdata/googleapis", "directory in which to search for imports")
language = flag.String("language", "", "the generated language")
output = flag.String("out", "output", "the path to the output directory")
protoFiles = flag.String("files", "testdata/googleapis/google/cloud/secretmanager/v1/", "path to protos to generate from")
protoPath = flag.String("proto_path", "testdata/googleapis", "directory in which to search for imports")
serviceConfig = flag.String("service-config", "testdata/google/cloud/secretmanager/v1/secretmanager_v1.yaml", "path to service config")
)

func main() {
Expand Down Expand Up @@ -59,7 +60,7 @@ func run(language, testdata, input, output string) error {
args := []string{
"-I", testdata,
fmt.Sprintf("--gclient_out=%s", output),
fmt.Sprintf("--gclient_opt=language=%s", language),
fmt.Sprintf("--gclient_opt=language=%s,service-config=%s", language, *serviceConfig),
}
args = append(args, files...)

Expand Down
2 changes: 1 addition & 1 deletion generator/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ require (
github.com/pb33f/libopenapi v0.18.6
google.golang.org/genproto/googleapis/api v0.0.0-20241021214115-324edc3d5d38
google.golang.org/protobuf v1.35.1
gopkg.in/yaml.v3 v3.0.1
)

require (
Expand All @@ -20,5 +21,4 @@ require (
github.com/vmware-labs/yaml-jsonpath v0.3.2 // indirect
github.com/wk8/go-ordered-map/v2 v2.1.9-0.20240815153524-6ea36470d1bd // indirect
golang.org/x/sync v0.8.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
42 changes: 42 additions & 0 deletions generator/internal/genclient/service_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package genclient

import (
"fmt"
"os"

"google.golang.org/genproto/googleapis/api/serviceconfig"
"gopkg.in/yaml.v3"
)

func ReadServiceConfig(serviceConfigPath string) (*serviceconfig.Service, error) {
y, err := os.ReadFile(serviceConfigPath)
if err != nil {
return nil, fmt.Errorf("error reading service config: %v", err)
}

var cfg serviceconfig.Service
if err := yaml.Unmarshal(y, &cfg); err != nil {
return nil, fmt.Errorf("error unmarshalling service config: %v", err)
}

// An API Service Config will always have a `name` so if it is not populated,
// it's an invalid config.
if cfg.GetName() == "" {
return nil, fmt.Errorf("invalid API service config file %q", serviceConfigPath)
}
return &cfg, nil
}
49 changes: 49 additions & 0 deletions generator/internal/genclient/service_config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package genclient

import (
"testing"

"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/googleapis/google-cloud-rust/generator/internal/sample"
"google.golang.org/genproto/googleapis/api/annotations"
"google.golang.org/genproto/googleapis/api/serviceconfig"
"google.golang.org/protobuf/types/known/apipb"
)

func TestReadServiceConfig(t *testing.T) {
const serviceConfigPath = "../../testdata/googleapis/google/cloud/secretmanager/v1/secretmanager_v1.yaml"
got, err := ReadServiceConfig(serviceConfigPath)
if err != nil {
t.Fatal(err)
}
if diff := cmp.Diff(sample.ServiceConfig, got,
cmpopts.IgnoreUnexported(annotations.HttpRule{}),
cmpopts.IgnoreUnexported(annotations.Http{}),
cmpopts.IgnoreUnexported(apipb.Api{}),
cmpopts.IgnoreUnexported(serviceconfig.AuthenticationRule{}),
cmpopts.IgnoreUnexported(serviceconfig.Authentication{}),
cmpopts.IgnoreUnexported(serviceconfig.BackendRule{}),
cmpopts.IgnoreUnexported(serviceconfig.Backend{}),
cmpopts.IgnoreUnexported(serviceconfig.DocumentationRule{}),
cmpopts.IgnoreUnexported(serviceconfig.Documentation{}),
cmpopts.IgnoreUnexported(serviceconfig.OAuthRequirements{}),
cmpopts.IgnoreUnexported(serviceconfig.Service{}),
); diff != "" {
t.Errorf("mismatch (-want +got):\n%s", diff)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ func TestSimpleObject(t *testing.T) {
}
api, err := makeAPI(model)
if err != nil {
t.Errorf("Error in makeAPI() %q", err)
t.Fatalf("Error in makeAPI() %q", err)
}

checkMessage(t, *api.Messages[0], genclient.Message{
Expand Down
18 changes: 10 additions & 8 deletions generator/internal/genclient/translator/protobuf/protobuf.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (

"github.com/googleapis/google-cloud-rust/generator/internal/genclient"
"github.com/googleapis/google-cloud-rust/generator/internal/genclient/language"
"google.golang.org/genproto/googleapis/api/serviceconfig"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/descriptorpb"
"google.golang.org/protobuf/types/pluginpb"
Expand All @@ -47,8 +48,9 @@ const (
type Options struct {
Language string
// Only used for local testing
OutDir string
TemplateDir string
OutDir string
TemplateDir string
ServiceConfig *serviceconfig.Service
}

// Translate translates proto representation into a [genclienGenerateRequest].
Expand Down Expand Up @@ -100,11 +102,12 @@ func makeAPI(req *pluginpb.CodeGeneratorRequest) *genclient.API {
}
state.ServiceByID[service.ID] = service
for _, m := range s.Method {
method := &genclient.Method{}
method.HTTPInfo = parseHTTPInfo(m.GetOptions())
method.Name = m.GetName()
method.InputTypeID = m.GetInputType()
method.OutputTypeID = m.GetOutputType()
method := &genclient.Method{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change seem unrelated, but meh.

Copy link
Member Author

@julieqiu julieqiu Nov 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was more for readability, but happy to put in a separate PR

HTTPInfo: parseHTTPInfo(m.GetOptions()),
Name: m.GetName(),
InputTypeID: m.GetInputType(),
OutputTypeID: m.GetOutputType(),
}
service.Methods = append(service.Methods, method)
}
fileServices = append(fileServices, service)
Expand Down Expand Up @@ -133,7 +136,6 @@ func makeAPI(req *pluginpb.CodeGeneratorRequest) *genclient.API {
}
api.Services = append(api.Services, fileServices...)
}

return api
}

Expand Down
88 changes: 88 additions & 0 deletions generator/internal/sample/sample.go
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file seems unrelated.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package sample provides sample data for testing.
package sample

import (
"google.golang.org/genproto/googleapis/api/annotations"
"google.golang.org/genproto/googleapis/api/serviceconfig"
"google.golang.org/protobuf/types/known/apipb"
)

var ServiceConfig = &serviceconfig.Service{
Name: "secretmanager.googleapis.com",
Title: "Secret Manager API",
Apis: []*apipb.Api{
{
Name: "google.cloud.secretmanager.v1.SecretManagerService",
},
},
Documentation: &serviceconfig.Documentation{
Summary: "Stores sensitive data such as API keys, passwords, and certificates.\nProvides convenience while improving security.",
Rules: []*serviceconfig.DocumentationRule{
{
Selector: "google.cloud.location.Locations.GetLocation",
Description: "Gets information about a location.",
},
{
Selector: "google.cloud.location.Locations.ListLocations",
Description: "Lists information about the supported locations for this service.",
},
},
Overview: "Secret Manager Overview",
},
Backend: &serviceconfig.Backend{
Rules: []*serviceconfig.BackendRule{
{
Selector: "google.cloud.location.Locations.GetLocation",
Deadline: 60,
},
{
Selector: "google.cloud.location.Locations.ListLocations",
Deadline: 60,
},
{
Selector: "google.cloud.secretmanager.v1.SecretManagerService.*",
Deadline: 60,
},
},
},
Http: &annotations.Http{
Rules: []*annotations.HttpRule{
{
Selector: "google.cloud.location.Locations.GetLocation",
},
{
Selector: "google.cloud.location.Locations.ListLocations",
},
},
},
Authentication: &serviceconfig.Authentication{
Rules: []*serviceconfig.AuthenticationRule{
{
Selector: "google.cloud.location.Locations.GetLocation",
Oauth: &serviceconfig.OAuthRequirements{},
},
{
Selector: "google.cloud.location.Locations.ListLocations",
Oauth: &serviceconfig.OAuthRequirements{},
},
{
Selector: "google.cloud.secretmanager.v1.SecretManagerService.*",
Oauth: &serviceconfig.OAuthRequirements{},
},
},
},
}
Loading