-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: support structpb.Struct as req/resp (#2632)
There are some APIs that have started to use this type for the request and/or respsonse. Similar to how we had to specially handle protos HTTP body we need a good translation for this type as well. `map[string]any` seemed like the best fit as that is the input needed to create a `Struct`. The other choice would have been a `googleapis.RawMessage`. RawMessage is used today when a field would be of type Struct, but this is a less convient type, and less precise type, to use than a map directly. Fixes: #2601
- Loading branch information
Showing
5 changed files
with
327 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
{ | ||
"kind": "discovery#restDescription", | ||
"etag": "\"kEk3sFj6Ef5_yR1-H3bAO6qw9mI/3m5rB86FE5KuW1K3jAl88AxCreg\"", | ||
"discoveryVersion": "v1", | ||
"id": "mapprotostruct:v1", | ||
"name": "mapprotostruct", | ||
"version": "v1", | ||
"title": "Example API", | ||
"description": "The Example API demonstrates handling structpb.Struct.", | ||
"ownerDomain": "google.com", | ||
"ownerName": "Google", | ||
"protocol": "rest", | ||
"schemas": { | ||
"GoogleProtobufStruct": { | ||
"id": "GoogleProtobufStruct", | ||
"description": "`Struct` represents a structured data value, consisting of fields which map to dynamically typed values. In some languages, `Struct` might be supported by a native representation. For example, in scripting languages like JS a struct is represented as an object. The details of that representation are described together with the proto support for the language. The JSON representation for `Struct` is JSON object.", | ||
"type": "object", | ||
"additionalProperties": { | ||
"type": "any", | ||
"description": "Properties of the object." | ||
} | ||
} | ||
}, | ||
"resources": { | ||
"atlas": { | ||
"methods": { | ||
"getMap": { | ||
"id": "mapprotostruct.getMap", | ||
"path": "map", | ||
"httpMethod": "GET", | ||
"description": "Get a map.", | ||
"request": { | ||
"$ref": "GoogleProtobufStruct" | ||
}, | ||
"response": { | ||
"$ref": "GoogleProtobufStruct" | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,235 @@ | ||
// Copyright YEAR Google LLC. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
// Code generated file. DO NOT EDIT. | ||
|
||
// Package mapprotostruct provides access to the Example API. | ||
// | ||
// # Library status | ||
// | ||
// These client libraries are officially supported by Google. However, this | ||
// library is considered complete and is in maintenance mode. This means | ||
// that we will address critical bugs and security issues but will not add | ||
// any new features. | ||
// | ||
// When possible, we recommend using our newer | ||
// [Cloud Client Libraries for Go](https://pkg.go.dev/cloud.google.com/go) | ||
// that are still actively being worked and iterated on. | ||
// | ||
// # Creating a client | ||
// | ||
// Usage example: | ||
// | ||
// import "google.golang.org/api/mapprotostruct/v1" | ||
// ... | ||
// ctx := context.Background() | ||
// mapprotostructService, err := mapprotostruct.NewService(ctx) | ||
// | ||
// In this example, Google Application Default Credentials are used for | ||
// authentication. For information on how to create and obtain Application | ||
// Default Credentials, see https://developers.google.com/identity/protocols/application-default-credentials. | ||
// | ||
// # Other authentication options | ||
// | ||
// To use an API key for authentication (note: some APIs do not support API | ||
// keys), use [google.golang.org/api/option.WithAPIKey]: | ||
// | ||
// mapprotostructService, err := mapprotostruct.NewService(ctx, option.WithAPIKey("AIza...")) | ||
// | ||
// To use an OAuth token (e.g., a user token obtained via a three-legged OAuth | ||
// flow, use [google.golang.org/api/option.WithTokenSource]: | ||
// | ||
// config := &oauth2.Config{...} | ||
// // ... | ||
// token, err := config.Exchange(ctx, ...) | ||
// mapprotostructService, err := mapprotostruct.NewService(ctx, option.WithTokenSource(config.TokenSource(ctx, token))) | ||
// | ||
// See [google.golang.org/api/option.ClientOption] for details on options. | ||
package mapprotostruct // import "google.golang.org/api/mapprotostruct/v1" | ||
|
||
import ( | ||
"bytes" | ||
"context" | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"io" | ||
"net/http" | ||
"net/url" | ||
"strconv" | ||
"strings" | ||
|
||
googleapi "google.golang.org/api/googleapi" | ||
internal "google.golang.org/api/internal" | ||
gensupport "google.golang.org/api/internal/gensupport" | ||
option "google.golang.org/api/option" | ||
internaloption "google.golang.org/api/option/internaloption" | ||
htransport "google.golang.org/api/transport/http" | ||
) | ||
|
||
// Always reference these packages, just in case the auto-generated code | ||
// below doesn't. | ||
var _ = bytes.NewBuffer | ||
var _ = strconv.Itoa | ||
var _ = fmt.Sprintf | ||
var _ = json.NewDecoder | ||
var _ = io.Copy | ||
var _ = url.Parse | ||
var _ = gensupport.MarshalJSON | ||
var _ = googleapi.Version | ||
var _ = errors.New | ||
var _ = strings.Replace | ||
var _ = context.Canceled | ||
var _ = internaloption.WithDefaultEndpoint | ||
var _ = internal.Version | ||
|
||
const apiId = "mapprotostruct:v1" | ||
const apiName = "mapprotostruct" | ||
const apiVersion = "v1" | ||
const basePath = "https://www.googleapis.com/discovery/v1/apis" | ||
const basePathTemplate = "https://www.UNIVERSE_DOMAIN/discovery/v1/apis" | ||
|
||
// NewService creates a new Service. | ||
func NewService(ctx context.Context, opts ...option.ClientOption) (*Service, error) { | ||
opts = append(opts, internaloption.WithDefaultEndpoint(basePath)) | ||
opts = append(opts, internaloption.WithDefaultEndpointTemplate(basePathTemplate)) | ||
opts = append(opts, internaloption.EnableNewAuthLibrary()) | ||
client, endpoint, err := htransport.NewClient(ctx, opts...) | ||
if err != nil { | ||
return nil, err | ||
} | ||
s, err := New(client) | ||
if err != nil { | ||
return nil, err | ||
} | ||
if endpoint != "" { | ||
s.BasePath = endpoint | ||
} | ||
return s, nil | ||
} | ||
|
||
// New creates a new Service. It uses the provided http.Client for requests. | ||
// | ||
// Deprecated: please use NewService instead. | ||
// To provide a custom HTTP client, use option.WithHTTPClient. | ||
// If you are using google.golang.org/api/googleapis/transport.APIKey, use option.WithAPIKey with NewService instead. | ||
func New(client *http.Client) (*Service, error) { | ||
if client == nil { | ||
return nil, errors.New("client is nil") | ||
} | ||
s := &Service{client: client, BasePath: basePath} | ||
s.Atlas = NewAtlasService(s) | ||
return s, nil | ||
} | ||
|
||
type Service struct { | ||
client *http.Client | ||
BasePath string // API endpoint base URL | ||
UserAgent string // optional additional User-Agent fragment | ||
|
||
Atlas *AtlasService | ||
} | ||
|
||
func (s *Service) userAgent() string { | ||
if s.UserAgent == "" { | ||
return googleapi.UserAgent | ||
} | ||
return googleapi.UserAgent + " " + s.UserAgent | ||
} | ||
|
||
func NewAtlasService(s *Service) *AtlasService { | ||
rs := &AtlasService{s: s} | ||
return rs | ||
} | ||
|
||
type AtlasService struct { | ||
s *Service | ||
} | ||
|
||
type AtlasGetMapCall struct { | ||
s *Service | ||
req map[string]any | ||
urlParams_ gensupport.URLParams | ||
ifNoneMatch_ string | ||
ctx_ context.Context | ||
header_ http.Header | ||
} | ||
|
||
// GetMap: Get a map. | ||
func (r *AtlasService) GetMap(req map[string]any) *AtlasGetMapCall { | ||
c := &AtlasGetMapCall{s: r.s, urlParams_: make(gensupport.URLParams)} | ||
c.req = req | ||
return c | ||
} | ||
|
||
// Fields allows partial responses to be retrieved. See | ||
// https://developers.google.com/gdata/docs/2.0/basics#PartialResponse for more | ||
// details. | ||
func (c *AtlasGetMapCall) Fields(s ...googleapi.Field) *AtlasGetMapCall { | ||
c.urlParams_.Set("fields", googleapi.CombineFields(s)) | ||
return c | ||
} | ||
|
||
// IfNoneMatch sets an optional parameter which makes the operation fail if the | ||
// object's ETag matches the given value. This is useful for getting updates | ||
// only after the object has changed since the last request. | ||
func (c *AtlasGetMapCall) IfNoneMatch(entityTag string) *AtlasGetMapCall { | ||
c.ifNoneMatch_ = entityTag | ||
return c | ||
} | ||
|
||
// Context sets the context to be used in this call's Do method. | ||
func (c *AtlasGetMapCall) Context(ctx context.Context) *AtlasGetMapCall { | ||
c.ctx_ = ctx | ||
return c | ||
} | ||
|
||
// Header returns a http.Header that can be modified by the caller to add | ||
// headers to the request. | ||
func (c *AtlasGetMapCall) Header() http.Header { | ||
if c.header_ == nil { | ||
c.header_ = make(http.Header) | ||
} | ||
return c.header_ | ||
} | ||
|
||
func (c *AtlasGetMapCall) doRequest(alt string) (*http.Response, error) { | ||
reqHeaders := gensupport.SetHeaders(c.s.userAgent(), "", c.header_) | ||
if c.ifNoneMatch_ != "" { | ||
reqHeaders.Set("If-None-Match", c.ifNoneMatch_) | ||
} | ||
var body io.Reader = nil | ||
protoBytes, err := json.Marshal(c.req) | ||
if err != nil { | ||
return nil, err | ||
} | ||
body = bytes.NewReader(protoBytes) | ||
urls := googleapi.ResolveRelative(c.s.BasePath, "map") | ||
urls += "?" + c.urlParams_.Encode() | ||
req, err := http.NewRequest("GET", urls, body) | ||
if err != nil { | ||
return nil, err | ||
} | ||
req.Header = reqHeaders | ||
return gensupport.SendRequest(c.ctx_, c.s.client, req) | ||
} | ||
|
||
// Do executes the "mapprotostruct.getMap" call. | ||
func (c *AtlasGetMapCall) Do(opts ...googleapi.CallOption) (map[string]any, error) { | ||
gensupport.SetOptions(c.urlParams_, opts...) | ||
res, err := c.doRequest("json") | ||
if err != nil { | ||
return nil, err | ||
} | ||
defer googleapi.CloseBody(res) | ||
if err := googleapi.CheckResponse(res); err != nil { | ||
return nil, gensupport.WrapError(err) | ||
} | ||
var ret map[string]any | ||
target := &ret | ||
if err := gensupport.DecodeResponse(target, res); err != nil { | ||
return nil, err | ||
} | ||
return ret, nil | ||
} |