Add handling for method that use Pages() to retrieve results

Make functions take in *Key rather than value type.
This commit is contained in:
Bowei Du 2018-01-15 15:02:01 -08:00
parent 7dadeee5e8
commit 447823b4a1
7 changed files with 237 additions and 123 deletions

View File

@ -276,7 +276,7 @@ type {{.WrapType}} interface {
{{.WrapTypeOps}}
{{- end}}
{{- if .GenerateGet}}
Get(ctx context.Context, key meta.Key) (*{{.FQObjectType}}, error)
Get(ctx context.Context, key *meta.Key) (*{{.FQObjectType}}, error)
{{- end -}}
{{- if .GenerateList}}
{{- if .KeyIsGlobal}}
@ -290,10 +290,10 @@ type {{.WrapType}} interface {
{{- end -}}
{{- end -}}
{{- if .GenerateInsert}}
Insert(ctx context.Context, key meta.Key, obj *{{.FQObjectType}}) error
Insert(ctx context.Context, key *meta.Key, obj *{{.FQObjectType}}) error
{{- end -}}
{{- if .GenerateDelete}}
Delete(ctx context.Context, key meta.Key) error
Delete(ctx context.Context, key *meta.Key) error
{{- end -}}
{{- if .AggregatedList}}
AggregatedList(ctx context.Context, fl *filter.F) (map[string][]*{{.FQObjectType}}, error)
@ -352,7 +352,7 @@ type {{.MockWrapType}} struct {
// execution flow of the mock. Return (false, nil, nil) to continue with
// normal mock behavior/ after the hook function executes.
{{- if .GenerateGet}}
GetHook func(m *{{.MockWrapType}}, ctx context.Context, key meta.Key) (bool, *{{.FQObjectType}}, error)
GetHook func(m *{{.MockWrapType}}, ctx context.Context, key *meta.Key) (bool, *{{.FQObjectType}}, error)
{{- end -}}
{{- if .GenerateList}}
{{- if .KeyIsGlobal}}
@ -366,10 +366,10 @@ type {{.MockWrapType}} struct {
{{- end}}
{{- end -}}
{{- if .GenerateInsert}}
InsertHook func(m *{{.MockWrapType}}, ctx context.Context, key meta.Key, obj *{{.FQObjectType}}) (bool, error)
InsertHook func(m *{{.MockWrapType}}, ctx context.Context, key *meta.Key, obj *{{.FQObjectType}}) (bool, error)
{{- end -}}
{{- if .GenerateDelete}}
DeleteHook func(m *{{.MockWrapType}}, ctx context.Context, key meta.Key) (bool, error)
DeleteHook func(m *{{.MockWrapType}}, ctx context.Context, key *meta.Key) (bool, error)
{{- end -}}
{{- if .AggregatedList}}
AggregatedListHook func(m *{{.MockWrapType}}, ctx context.Context, fl *filter.F) (bool, map[string][]*{{.FQObjectType}}, error)
@ -388,7 +388,7 @@ type {{.MockWrapType}} struct {
{{- if .GenerateGet}}
// Get returns the object from the mock.
func (m *{{.MockWrapType}}) Get(ctx context.Context, key meta.Key) (*{{.FQObjectType}}, error) {
func (m *{{.MockWrapType}}) Get(ctx context.Context, key *meta.Key) (*{{.FQObjectType}}, error) {
if m.GetHook != nil {
if intercept, obj, err := m.GetHook(m, ctx, key); intercept {
glog.V(5).Infof("{{.MockWrapType}}.Get(%v, %s) = %+v, %v", ctx, key, obj ,err)
@ -399,11 +399,11 @@ func (m *{{.MockWrapType}}) Get(ctx context.Context, key meta.Key) (*{{.FQObject
m.Lock.Lock()
defer m.Lock.Unlock()
if err, ok := m.GetError[key]; ok {
if err, ok := m.GetError[*key]; ok {
glog.V(5).Infof("{{.MockWrapType}}.Get(%v, %s) = nil, %v", ctx, key, err)
return nil, err
}
if obj, ok := m.Objects[key]; ok {
if obj, ok := m.Objects[*key]; ok {
typedObj := obj.To{{.VersionTitle}}()
glog.V(5).Infof("{{.MockWrapType}}.Get(%v, %s) = %+v, nil", ctx, key, typedObj)
return typedObj, nil
@ -503,7 +503,7 @@ func (m *{{.MockWrapType}}) List(ctx context.Context, zone string, fl *filter.F)
{{- if .GenerateInsert}}
// Insert is a mock for inserting/creating a new object.
func (m *{{.MockWrapType}}) Insert(ctx context.Context, key meta.Key, obj *{{.FQObjectType}}) error {
func (m *{{.MockWrapType}}) Insert(ctx context.Context, key *meta.Key, obj *{{.FQObjectType}}) error {
if m.InsertHook != nil {
if intercept, err := m.InsertHook(m, ctx, key, obj); intercept {
glog.V(5).Infof("{{.MockWrapType}}.Insert(%v, %v, %+v) = %v", ctx, key, obj, err)
@ -514,11 +514,11 @@ func (m *{{.MockWrapType}}) Insert(ctx context.Context, key meta.Key, obj *{{.FQ
m.Lock.Lock()
defer m.Lock.Unlock()
if err, ok := m.InsertError[key]; ok {
if err, ok := m.InsertError[*key]; ok {
glog.V(5).Infof("{{.MockWrapType}}.Insert(%v, %v, %+v) = %v", ctx, key, obj, err)
return err
}
if _, ok := m.Objects[key]; ok {
if _, ok := m.Objects[*key]; ok {
err := &googleapi.Error{
Code: http.StatusConflict,
Message: fmt.Sprintf("{{.MockWrapType}} %v exists", key),
@ -532,7 +532,7 @@ func (m *{{.MockWrapType}}) Insert(ctx context.Context, key meta.Key, obj *{{.FQ
obj.SelfLink = SelfLink(meta.Version{{.VersionTitle}}, "mock-project", "{{.Resource}}", key)
}
m.Objects[key] = &Mock{{.Service}}Obj{obj}
m.Objects[*key] = &Mock{{.Service}}Obj{obj}
glog.V(5).Infof("{{.MockWrapType}}.Insert(%v, %v, %+v) = nil", ctx, key, obj)
return nil
}
@ -540,7 +540,7 @@ func (m *{{.MockWrapType}}) Insert(ctx context.Context, key meta.Key, obj *{{.FQ
{{- if .GenerateDelete}}
// Delete is a mock for deleting the object.
func (m *{{.MockWrapType}}) Delete(ctx context.Context, key meta.Key) error {
func (m *{{.MockWrapType}}) Delete(ctx context.Context, key *meta.Key) error {
if m.DeleteHook != nil {
if intercept, err := m.DeleteHook(m, ctx, key); intercept {
glog.V(5).Infof("{{.MockWrapType}}.Delete(%v, %v) = %v", ctx, key, err)
@ -551,11 +551,11 @@ func (m *{{.MockWrapType}}) Delete(ctx context.Context, key meta.Key) error {
m.Lock.Lock()
defer m.Lock.Unlock()
if err, ok := m.DeleteError[key]; ok {
if err, ok := m.DeleteError[*key]; ok {
glog.V(5).Infof("{{.MockWrapType}}.Delete(%v, %v) = %v", ctx, key, err)
return err
}
if _, ok := m.Objects[key]; !ok {
if _, ok := m.Objects[*key]; !ok {
err := &googleapi.Error{
Code: http.StatusNotFound,
Message: fmt.Sprintf("{{.MockWrapType}} %v not found", key),
@ -564,7 +564,7 @@ func (m *{{.MockWrapType}}) Delete(ctx context.Context, key meta.Key) error {
return err
}
delete(m.Objects, key)
delete(m.Objects, *key)
glog.V(5).Infof("{{.MockWrapType}}.Delete(%v, %v) = nil", ctx, key)
return nil
}
@ -621,16 +621,21 @@ func (m *{{.MockWrapType}}) Obj(o *{{.FQObjectType}}) *Mock{{.Service}}Obj {
{{- range .}}
// {{.Name}} is a mock for the corresponding method.
func (m *{{.MockWrapType}}) {{.FcnArgs}} {
{{- if eq .ReturnType "Operation"}}
{{- if .IsOperation }}
if m.{{.MockHookName}} != nil {
return m.{{.MockHookName}}(m, ctx, key {{.CallArgs}})
}
return nil
{{- else}}
{{- else if .IsGet}}
if m.{{.MockHookName}} != nil {
return m.{{.MockHookName}}(m, ctx, key {{.CallArgs}})
}
return nil, fmt.Errorf("{{.MockHookName}} must be set")
{{- else if .IsPaged}}
if m.{{.MockHookName}} != nil {
return m.{{.MockHookName}}(m, ctx, key {{.CallArgs}}, fl)
}
return nil, nil
{{- end}}
}
{{end -}}
@ -642,7 +647,7 @@ type {{.GCEWrapType}} struct {
{{- if .GenerateGet}}
// Get the {{.Object}} named by key.
func (g *{{.GCEWrapType}}) Get(ctx context.Context, key meta.Key) (*{{.FQObjectType}}, error) {
func (g *{{.GCEWrapType}}) Get(ctx context.Context, key *meta.Key) (*{{.FQObjectType}}, error) {
projectID := g.s.ProjectRouter.ProjectID(ctx, "{{.Version}}", "{{.Service}}")
rk := &RateLimitKey{
ProjectID: projectID,
@ -714,7 +719,7 @@ rk := &RateLimitKey{
{{- if .GenerateInsert}}
// Insert {{.Object}} with key of value obj.
func (g *{{.GCEWrapType}}) Insert(ctx context.Context, key meta.Key, obj *{{.FQObjectType}}) error {
func (g *{{.GCEWrapType}}) Insert(ctx context.Context, key *meta.Key, obj *{{.FQObjectType}}) error {
projectID := g.s.ProjectRouter.ProjectID(ctx, "{{.Version}}", "{{.Service}}")
rk := &RateLimitKey{
ProjectID: projectID,
@ -747,7 +752,7 @@ func (g *{{.GCEWrapType}}) Insert(ctx context.Context, key meta.Key, obj *{{.FQO
{{- if .GenerateDelete}}
// Delete the {{.Object}} referenced by key.
func (g *{{.GCEWrapType}}) Delete(ctx context.Context, key meta.Key) error {
func (g *{{.GCEWrapType}}) Delete(ctx context.Context, key *meta.Key) error {
projectID := g.s.ProjectRouter.ProjectID(ctx, "{{.Version}}", "{{.Service}}")
rk := &RateLimitKey{
ProjectID: projectID,
@ -823,7 +828,7 @@ func (g *{{.GCEWrapType}}) {{.FcnArgs}} {
Service: "{{.Service}}",
}
if err := g.s.RateLimiter.Accept(ctx, rk); err != nil {
{{- if eq .ReturnType "Operation"}}
{{- if .IsOperation}}
return err
{{- else}}
return nil, err
@ -838,15 +843,26 @@ func (g *{{.GCEWrapType}}) {{.FcnArgs}} {
{{- if .KeyIsZonal}}
call := g.s.{{.VersionTitle}}.{{.Service}}.{{.Name}}(projectID, key.Zone, key.Name {{.CallArgs}})
{{- end}}
{{- if .IsOperation}}
call.Context(ctx)
{{- if eq .ReturnType "Operation"}}
op, err := call.Do()
if err != nil {
return err
}
return g.s.WaitForCompletion(ctx, op)
{{- else}}
{{- else if .IsGet}}
call.Context(ctx)
return call.Do()
{{- else if .IsPaged}}
var all []*{{.Version}}.{{.ItemType}}
f := func(l *{{.Version}}.{{.ReturnType}}) error {
all = append(all, l.Items...)
return nil
}
if err := call.Pages(ctx, f); err != nil {
return nil, err
}
return all, nil
{{- end}}
}
{{end -}}
@ -933,17 +949,17 @@ func Test{{.Service}}Group(t *testing.T) {
// Get not found.
{{- if .HasAlpha}}{{- if .Alpha.GenerateGet}}
if _, err := mock.Alpha{{.Service}}().Get(ctx, *key); err == nil {
if _, err := mock.Alpha{{.Service}}().Get(ctx, key); err == nil {
t.Errorf("Alpha{{.Service}}().Get(%v, %v) = _, nil; want error", ctx, key)
}
{{- end}}{{- end}}
{{- if .HasBeta}}{{- if .Beta.GenerateGet}}
if _, err := mock.Beta{{.Service}}().Get(ctx, *key); err == nil {
if _, err := mock.Beta{{.Service}}().Get(ctx, key); err == nil {
t.Errorf("Beta{{.Service}}().Get(%v, %v) = _, nil; want error", ctx, key)
}
{{- end}}{{- end}}
{{- if .HasGA}}{{- if .GA.GenerateGet}}
if _, err := mock.{{.Service}}().Get(ctx, *key); err == nil {
if _, err := mock.{{.Service}}().Get(ctx, key); err == nil {
t.Errorf("{{.Service}}().Get(%v, %v) = _, nil; want error", ctx, key)
}
{{- end}}{{- end}}
@ -952,41 +968,41 @@ func Test{{.Service}}Group(t *testing.T) {
{{- if .HasAlpha}}{{- if .Alpha.GenerateInsert}}
{
obj := &alpha.{{.Alpha.Object}}{}
if err := mock.Alpha{{.Service}}().Insert(ctx, *keyAlpha, obj); err != nil {
t.Errorf("Alpha{{.Service}}().Insert(%v, %v, %v) = %v; want nil", ctx, key, obj, err)
if err := mock.Alpha{{.Service}}().Insert(ctx, keyAlpha, obj); err != nil {
t.Errorf("Alpha{{.Service}}().Insert(%v, %v, %v) = %v; want nil", ctx, keyAlpha, obj, err)
}
}
{{- end}}{{- end}}
{{- if .HasBeta}}{{- if .Beta.GenerateInsert}}
{
obj := &beta.{{.Beta.Object}}{}
if err := mock.Beta{{.Service}}().Insert(ctx, *keyBeta, obj); err != nil {
t.Errorf("Beta{{.Service}}().Insert(%v, %v, %v) = %v; want nil", ctx, key, obj, err)
if err := mock.Beta{{.Service}}().Insert(ctx, keyBeta, obj); err != nil {
t.Errorf("Beta{{.Service}}().Insert(%v, %v, %v) = %v; want nil", ctx, keyBeta, obj, err)
}
}
{{- end}}{{- end}}
{{- if .HasGA}}{{- if .GA.GenerateInsert}}
{
obj := &ga.{{.GA.Object}}{}
if err := mock.{{.Service}}().Insert(ctx, *keyGA, obj); err != nil {
t.Errorf("{{.Service}}().Insert(%v, %v, %v) = %v; want nil", ctx, key, obj, err)
if err := mock.{{.Service}}().Insert(ctx, keyGA, obj); err != nil {
t.Errorf("{{.Service}}().Insert(%v, %v, %v) = %v; want nil", ctx, keyGA, obj, err)
}
}
{{- end}}{{- end}}
// Get across versions.
{{- if .HasAlpha}}{{- if .Alpha.GenerateInsert}}
if obj, err := mock.Alpha{{.Service}}().Get(ctx, *key); err != nil {
if obj, err := mock.Alpha{{.Service}}().Get(ctx, key); err != nil {
t.Errorf("Alpha{{.Service}}().Get(%v, %v) = %v, %v; want nil", ctx, key, obj, err)
}
{{- end}}{{- end}}
{{- if .HasBeta}}{{- if .Beta.GenerateInsert}}
if obj, err := mock.Beta{{.Service}}().Get(ctx, *key); err != nil {
if obj, err := mock.Beta{{.Service}}().Get(ctx, key); err != nil {
t.Errorf("Beta{{.Service}}().Get(%v, %v) = %v, %v; want nil", ctx, key, obj, err)
}
{{- end}}{{- end}}
{{- if .HasGA}}{{- if .GA.GenerateInsert}}
if obj, err := mock.{{.Service}}().Get(ctx, *key); err != nil {
if obj, err := mock.{{.Service}}().Get(ctx, key); err != nil {
t.Errorf("{{.Service}}().Get(%v, %v) = %v, %v; want nil", ctx, key, obj, err)
}
{{- end}}{{- end}}
@ -1077,35 +1093,35 @@ func Test{{.Service}}Group(t *testing.T) {
// Delete across versions.
{{- if .HasAlpha}}{{- if .Alpha.GenerateDelete}}
if err := mock.Alpha{{.Service}}().Delete(ctx, *keyAlpha); err != nil {
t.Errorf("Alpha{{.Service}}().Delete(%v, %v) = %v; want nil", ctx, key, err)
if err := mock.Alpha{{.Service}}().Delete(ctx, keyAlpha); err != nil {
t.Errorf("Alpha{{.Service}}().Delete(%v, %v) = %v; want nil", ctx, keyAlpha, err)
}
{{- end}}{{- end}}
{{- if .HasBeta}}{{- if .Beta.GenerateDelete}}
if err := mock.Beta{{.Service}}().Delete(ctx, *keyBeta); err != nil {
t.Errorf("Beta{{.Service}}().Delete(%v, %v) = %v; want nil", ctx, key, err)
if err := mock.Beta{{.Service}}().Delete(ctx, keyBeta); err != nil {
t.Errorf("Beta{{.Service}}().Delete(%v, %v) = %v; want nil", ctx, keyBeta, err)
}
{{- end}}{{- end}}
{{- if .HasGA}}{{- if .GA.GenerateDelete}}
if err := mock.{{.Service}}().Delete(ctx, *keyGA); err != nil {
t.Errorf("{{.Service}}().Delete(%v, %v) = %v; want nil", ctx, key, err)
if err := mock.{{.Service}}().Delete(ctx, keyGA); err != nil {
t.Errorf("{{.Service}}().Delete(%v, %v) = %v; want nil", ctx, keyGA, err)
}
{{- end}}{{- end}}
// Delete not found.
{{- if .HasAlpha}}{{- if .Alpha.GenerateDelete}}
if err := mock.Alpha{{.Service}}().Delete(ctx, *keyAlpha); err == nil {
t.Errorf("Alpha{{.Service}}().Delete(%v, %v) = nil; want error", ctx, key)
if err := mock.Alpha{{.Service}}().Delete(ctx, keyAlpha); err == nil {
t.Errorf("Alpha{{.Service}}().Delete(%v, %v) = nil; want error", ctx, keyAlpha)
}
{{- end}}{{- end}}
{{- if .HasBeta}}{{- if .Beta.GenerateDelete}}
if err := mock.Beta{{.Service}}().Delete(ctx, *keyBeta); err == nil {
t.Errorf("Beta{{.Service}}().Delete(%v, %v) = nil; want error", ctx, key)
if err := mock.Beta{{.Service}}().Delete(ctx, keyBeta); err == nil {
t.Errorf("Beta{{.Service}}().Delete(%v, %v) = nil; want error", ctx, keyBeta)
}
{{- end}}{{- end}}
{{- if .HasGA}}{{- if .GA.GenerateDelete}}
if err := mock.{{.Service}}().Delete(ctx, *keyGA); err == nil {
t.Errorf("{{.Service}}().Delete(%v, %v) = nil; want error", ctx, key)
if err := mock.{{.Service}}().Delete(ctx, keyGA); err == nil {
t.Errorf("{{.Service}}().Delete(%v, %v) = nil; want error", ctx, keyGA)
}
{{- end}}{{- end}}
}

View File

@ -12,7 +12,6 @@ go_library(
importpath = "k8s.io/kubernetes/pkg/cloudprovider/providers/gce/cloud/meta",
visibility = ["//visibility:public"],
deps = [
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/google.golang.org/api/compute/v0.alpha:go_default_library",
"//vendor/google.golang.org/api/compute/v0.beta:go_default_library",
"//vendor/google.golang.org/api/compute/v1:go_default_library",

View File

@ -115,6 +115,18 @@ var AllServices = []*ServiceInfo{
serviceType: reflect.TypeOf(&alpha.BackendServicesService{}),
additionalMethods: []string{"Update"},
},
{
Object: "BackendService",
Service: "RegionBackendServices",
Resource: "backendServices",
version: VersionGA,
keyType: Regional,
serviceType: reflect.TypeOf(&ga.RegionBackendServicesService{}),
additionalMethods: []string{
"GetHealth",
"Update",
},
},
{
Object: "BackendService",
Service: "RegionBackendServices",
@ -285,6 +297,7 @@ var AllServices = []*ServiceInfo{
additionalMethods: []string{
"AttachNetworkEndpoints",
"DetachNetworkEndpoints",
"ListNetworkEndpoints",
},
options: AggregatedList,
},

View File

@ -20,8 +20,6 @@ import (
"fmt"
"reflect"
"strings"
"github.com/golang/glog"
)
func newArg(t reflect.Type) *arg {
@ -86,23 +84,60 @@ func (a *arg) String() string {
// newMethod returns a newly initialized method.
func newMethod(s *ServiceInfo, m reflect.Method) *Method {
ret := &Method{s, m, ""}
ret := &Method{
ServiceInfo: s,
m: m,
kind: MethodOperation,
ReturnType: "",
}
ret.init()
return ret
}
// MethodKind is the type of method that we are generated code for.
type MethodKind int
const (
// MethodOperation is a long running method that returns an operation.
MethodOperation MethodKind = iota
// MethodGet is a method that immediately returns some data.
MethodGet MethodKind = iota
// MethodPaged is a method that returns a paged set of data.
MethodPaged MethodKind = iota
)
// Method is used to generate the calling code for non-standard methods.
type Method struct {
*ServiceInfo
m reflect.Method
kind MethodKind
// ReturnType is the return type for the method.
ReturnType string
// ItemType is the type of the individual elements returns from a
// Pages() call. This is only applicable for MethodPaged kind.
ItemType string
}
// IsOperation is true if the method is an Operation.
func (m *Method) IsOperation() bool {
return m.kind == MethodOperation
}
// IsPaged is true if the method paged.
func (m *Method) IsPaged() bool {
return m.kind == MethodPaged
}
// IsGet is true if the method simple get.
func (m *Method) IsGet() bool {
return m.kind == MethodGet
}
// argsSkip is the number of arguments to skip when generating the
// synthesized method.
func (mr *Method) argsSkip() int {
switch mr.keyType {
func (m *Method) argsSkip() int {
switch m.keyType {
case Zonal:
return 4
case Regional:
@ -110,15 +145,15 @@ func (mr *Method) argsSkip() int {
case Global:
return 3
}
panic(fmt.Errorf("invalid KeyType %v", mr.keyType))
panic(fmt.Errorf("invalid KeyType %v", m.keyType))
}
// args return a list of arguments to the method, skipping the first skip
// elements. If nameArgs is true, then the arguments will include a generated
// parameter name (arg<N>). prefix will be added to the parameters.
func (mr *Method) args(skip int, nameArgs bool, prefix []string) []string {
func (m *Method) args(skip int, nameArgs bool, prefix []string) []string {
var args []*arg
fType := mr.m.Func.Type()
fType := m.m.Func.Type()
for i := 0; i < fType.NumIn(); i++ {
t := fType.In(i)
args = append(args, newArg(t))
@ -135,24 +170,26 @@ func (mr *Method) args(skip int, nameArgs bool, prefix []string) []string {
return append(prefix, a...)
}
// init the method, preforming some rudimentary static checking.
func (mr *Method) init() {
fType := mr.m.Func.Type()
if fType.NumIn() < mr.argsSkip() {
// init the method. This performs some rudimentary static checking as well as
// determines the kind of method by looking at the shape (method signature) of
// the object.
func (m *Method) init() {
fType := m.m.Func.Type()
if fType.NumIn() < m.argsSkip() {
err := fmt.Errorf("method %q.%q, arity = %d which is less than required (< %d)",
mr.Service, mr.Name(), fType.NumIn(), mr.argsSkip())
m.Service, m.Name(), fType.NumIn(), m.argsSkip())
panic(err)
}
// Skipped args should all be string (they will be projectID, zone, region etc).
for i := 1; i < mr.argsSkip(); i++ {
for i := 1; i < m.argsSkip(); i++ {
if fType.In(i).Kind() != reflect.String {
panic(fmt.Errorf("method %q.%q: skipped args can only be strings", mr.Service, mr.Name()))
panic(fmt.Errorf("method %q.%q: skipped args can only be strings", m.Service, m.Name()))
}
}
// Return of the method must return a single value of type *xxxCall.
if fType.NumOut() != 1 || fType.Out(0).Kind() != reflect.Ptr || !strings.HasSuffix(fType.Out(0).Elem().Name(), "Call") {
panic(fmt.Errorf("method %q.%q: generator only supports methods returning an *xxxCall object",
mr.Service, mr.Name()))
m.Service, m.Name()))
}
returnType := fType.Out(0)
returnTypeName := fType.Out(0).Elem().Name()
@ -160,48 +197,64 @@ func (mr *Method) init() {
doMethod, ok := returnType.MethodByName("Do")
if !ok {
panic(fmt.Errorf("method %q.%q: return type %q does not have a Do() method",
mr.Service, mr.Name(), returnTypeName))
m.Service, m.Name(), returnTypeName))
}
_, hasPages := returnType.MethodByName("Pages")
// Do() method must return (*T, error).
switch doMethod.Func.Type().NumOut() {
case 2:
glog.Infof("Method %q.%q: return type %q of Do() = %v, %v",
mr.Service, mr.Name(), returnTypeName, doMethod.Func.Type().Out(0), doMethod.Func.Type().Out(1))
out0 := doMethod.Func.Type().Out(0)
if out0.Kind() != reflect.Ptr {
panic(fmt.Errorf("method %q.%q: return type %q of Do() = S, _; S must be pointer type (%v)",
mr.Service, mr.Name(), returnTypeName, out0))
m.Service, m.Name(), returnTypeName, out0))
}
mr.ReturnType = out0.Elem().Name()
if out0.Elem().Name() == "Operation" {
glog.Infof("Method %q.%q is an *Operation", mr.Service, mr.Name())
} else {
glog.Infof("Method %q.%q returns %v", mr.Service, mr.Name(), out0)
m.ReturnType = out0.Elem().Name()
switch {
case out0.Elem().Name() == "Operation":
m.kind = MethodOperation
case hasPages:
m.kind = MethodPaged
// Pages() returns a xxxList that has the actual list
// of objects in the xxxList.Items field.
listType := out0.Elem()
itemsField, ok := listType.FieldByName("Items")
if !ok {
panic(fmt.Errorf("method %q.%q: paged return type %q does not have a .Items field", m.Service, m.Name(), listType.Name()))
}
// itemsField will be a []*ItemType. Dereference to
// extract the ItemType.
itemsType := itemsField.Type
if itemsType.Kind() != reflect.Slice && itemsType.Elem().Kind() != reflect.Ptr {
panic(fmt.Errorf("method %q.%q: paged return type %q.Items is not an array of pointers", m.Service, m.Name(), listType.Name()))
}
m.ItemType = itemsType.Elem().Elem().Name()
default:
m.kind = MethodGet
}
// Second argument must be "error".
if doMethod.Func.Type().Out(1).Name() != "error" {
panic(fmt.Errorf("method %q.%q: return type %q of Do() = S, T; T must be 'error'",
mr.Service, mr.Name(), returnTypeName))
m.Service, m.Name(), returnTypeName))
}
break
default:
panic(fmt.Errorf("method %q.%q: %q Do() return type is not handled by the generator",
mr.Service, mr.Name(), returnTypeName))
m.Service, m.Name(), returnTypeName))
}
}
// Name is the name of the method.
func (mr *Method) Name() string {
return mr.m.Name
func (m *Method) Name() string {
return m.m.Name
}
// CallArgs is a list of comma separated "argN" used for calling the method.
// For example, if the method has two additional arguments, this will return
// "arg0, arg1".
func (mr *Method) CallArgs() string {
func (m *Method) CallArgs() string {
var args []string
for i := mr.argsSkip(); i < mr.m.Func.Type().NumIn(); i++ {
args = append(args, fmt.Sprintf("arg%d", i-mr.argsSkip()))
for i := m.argsSkip(); i < m.m.Func.Type().NumIn(); i++ {
args = append(args, fmt.Sprintf("arg%d", i-m.argsSkip()))
}
if len(args) == 0 {
return ""
@ -210,41 +263,74 @@ func (mr *Method) CallArgs() string {
}
// MockHookName is the name of the hook function in the mock.
func (mr *Method) MockHookName() string {
return mr.m.Name + "Hook"
func (m *Method) MockHookName() string {
return m.m.Name + "Hook"
}
// MockHook is the definition of the hook function.
func (mr *Method) MockHook() string {
args := mr.args(mr.argsSkip(), false, []string{
fmt.Sprintf("*%s", mr.MockWrapType()),
func (m *Method) MockHook() string {
args := m.args(m.argsSkip(), false, []string{
fmt.Sprintf("*%s", m.MockWrapType()),
"context.Context",
"meta.Key",
"*meta.Key",
})
if mr.ReturnType == "Operation" {
return fmt.Sprintf("%v func(%v) error", mr.MockHookName(), strings.Join(args, ", "))
if m.kind == MethodPaged {
args = append(args, "*filter.F")
}
switch m.kind {
case MethodOperation:
return fmt.Sprintf("%v func(%v) error", m.MockHookName(), strings.Join(args, ", "))
case MethodGet:
return fmt.Sprintf("%v func(%v) (*%v.%v, error)", m.MockHookName(), strings.Join(args, ", "), m.Version(), m.ReturnType)
case MethodPaged:
return fmt.Sprintf("%v func(%v) ([]*%v.%v, error)", m.MockHookName(), strings.Join(args, ", "), m.Version(), m.ItemType)
default:
panic(fmt.Errorf("invalid method kind: %v", m.kind))
}
return fmt.Sprintf("%v func(%v) (*%v.%v, error)", mr.MockHookName(), strings.Join(args, ", "), mr.Version(), mr.ReturnType)
}
// FcnArgs is the function signature for the definition of the method.
func (mr *Method) FcnArgs() string {
args := mr.args(mr.argsSkip(), true, []string{
func (m *Method) FcnArgs() string {
args := m.args(m.argsSkip(), true, []string{
"ctx context.Context",
"key meta.Key",
"key *meta.Key",
})
if mr.ReturnType == "Operation" {
return fmt.Sprintf("%v(%v) error", mr.m.Name, strings.Join(args, ", "))
if m.kind == MethodPaged {
args = append(args, "fl *filter.F")
}
switch m.kind {
case MethodOperation:
return fmt.Sprintf("%v(%v) error", m.m.Name, strings.Join(args, ", "))
case MethodGet:
return fmt.Sprintf("%v(%v) (*%v.%v, error)", m.m.Name, strings.Join(args, ", "), m.Version(), m.ReturnType)
case MethodPaged:
return fmt.Sprintf("%v(%v) ([]*%v.%v, error)", m.m.Name, strings.Join(args, ", "), m.Version(), m.ItemType)
default:
panic(fmt.Errorf("invalid method kind: %v", m.kind))
}
return fmt.Sprintf("%v(%v) (*%v.%v, error)", mr.m.Name, strings.Join(args, ", "), mr.Version(), mr.ReturnType)
}
// InterfaceFunc is the function declaration of the method in the interface.
func (mr *Method) InterfaceFunc() string {
args := mr.args(mr.argsSkip(), false, []string{"context.Context", "meta.Key"})
if mr.ReturnType == "Operation" {
return fmt.Sprintf("%v(%v) error", mr.m.Name, strings.Join(args, ", "))
func (m *Method) InterfaceFunc() string {
args := []string{
"context.Context",
"*meta.Key",
}
args = m.args(m.argsSkip(), false, args)
if m.kind == MethodPaged {
args = append(args, "*filter.F")
}
switch m.kind {
case MethodOperation:
return fmt.Sprintf("%v(%v) error", m.m.Name, strings.Join(args, ", "))
case MethodGet:
return fmt.Sprintf("%v(%v) (*%v.%v, error)", m.m.Name, strings.Join(args, ", "), m.Version(), m.ReturnType)
case MethodPaged:
return fmt.Sprintf("%v(%v) ([]*%v.%v, error)", m.m.Name, strings.Join(args, ", "), m.Version(), m.ItemType)
default:
panic(fmt.Errorf("invalid method kind: %v", m.kind))
}
return fmt.Sprintf("%v(%v) (*%v.%v, error)", mr.m.Name, strings.Join(args, ", "), mr.Version(), mr.ReturnType)
}

View File

@ -45,42 +45,42 @@ func TestMocks(t *testing.T) {
key := keyAlpha
// Get not found.
if _, err := mock.AlphaAddresses().Get(ctx, *key); err == nil {
if _, err := mock.AlphaAddresses().Get(ctx, key); err == nil {
t.Errorf("AlphaAddresses().Get(%v, %v) = _, nil; want error", ctx, key)
}
if _, err := mock.BetaAddresses().Get(ctx, *key); err == nil {
if _, err := mock.BetaAddresses().Get(ctx, key); err == nil {
t.Errorf("BetaAddresses().Get(%v, %v) = _, nil; want error", ctx, key)
}
if _, err := mock.Addresses().Get(ctx, *key); err == nil {
if _, err := mock.Addresses().Get(ctx, key); err == nil {
t.Errorf("Addresses().Get(%v, %v) = _, nil; want error", ctx, key)
}
// Insert.
{
obj := &alpha.Address{}
if err := mock.AlphaAddresses().Insert(ctx, *keyAlpha, obj); err != nil {
if err := mock.AlphaAddresses().Insert(ctx, keyAlpha, obj); err != nil {
t.Errorf("AlphaAddresses().Insert(%v, %v, %v) = %v; want nil", ctx, key, obj, err)
}
}
{
obj := &beta.Address{}
if err := mock.BetaAddresses().Insert(ctx, *keyBeta, obj); err != nil {
if err := mock.BetaAddresses().Insert(ctx, keyBeta, obj); err != nil {
t.Errorf("BetaAddresses().Insert(%v, %v, %v) = %v; want nil", ctx, key, obj, err)
}
}
{
obj := &ga.Address{}
if err := mock.Addresses().Insert(ctx, *keyGA, &ga.Address{Name: "ga"}); err != nil {
if err := mock.Addresses().Insert(ctx, keyGA, &ga.Address{Name: "ga"}); err != nil {
t.Errorf("Addresses().Insert(%v, %v, %v) = %v; want nil", ctx, key, obj, err)
}
}
// Get across versions.
if obj, err := mock.AlphaAddresses().Get(ctx, *key); err != nil {
if obj, err := mock.AlphaAddresses().Get(ctx, key); err != nil {
t.Errorf("AlphaAddresses().Get(%v, %v) = %v, %v; want nil", ctx, key, obj, err)
}
if obj, err := mock.BetaAddresses().Get(ctx, *key); err != nil {
if obj, err := mock.BetaAddresses().Get(ctx, key); err != nil {
t.Errorf("BetaAddresses().Get(%v, %v) = %v, %v; want nil", ctx, key, obj, err)
}
if obj, err := mock.Addresses().Get(ctx, *key); err != nil {
if obj, err := mock.Addresses().Get(ctx, key); err != nil {
t.Errorf("Addresses().Get(%v, %v) = %v, %v; want nil", ctx, key, obj, err)
}
// List across versions.
@ -128,23 +128,23 @@ func TestMocks(t *testing.T) {
}
}
// Delete across versions.
if err := mock.AlphaAddresses().Delete(ctx, *keyAlpha); err != nil {
if err := mock.AlphaAddresses().Delete(ctx, keyAlpha); err != nil {
t.Errorf("AlphaAddresses().Delete(%v, %v) = %v; want nil", ctx, key, err)
}
if err := mock.BetaAddresses().Delete(ctx, *keyBeta); err != nil {
if err := mock.BetaAddresses().Delete(ctx, keyBeta); err != nil {
t.Errorf("BetaAddresses().Delete(%v, %v) = %v; want nil", ctx, key, err)
}
if err := mock.Addresses().Delete(ctx, *keyGA); err != nil {
if err := mock.Addresses().Delete(ctx, keyGA); err != nil {
t.Errorf("Addresses().Delete(%v, %v) = %v; want nil", ctx, key, err)
}
// Delete not found.
if err := mock.AlphaAddresses().Delete(ctx, *keyAlpha); err == nil {
if err := mock.AlphaAddresses().Delete(ctx, keyAlpha); err == nil {
t.Errorf("AlphaAddresses().Delete(%v, %v) = nil; want error", ctx, key)
}
if err := mock.BetaAddresses().Delete(ctx, *keyBeta); err == nil {
if err := mock.BetaAddresses().Delete(ctx, keyBeta); err == nil {
t.Errorf("BetaAddresses().Delete(%v, %v) = nil; want error", ctx, key)
}
if err := mock.Addresses().Delete(ctx, *keyGA); err == nil {
if err := mock.Addresses().Delete(ctx, keyGA); err == nil {
t.Errorf("Addresses().Delete(%v, %v) = nil; want error", ctx, key)
}
}

View File

@ -142,7 +142,7 @@ func copyViaJSON(dest, src interface{}) error {
}
// SelfLink returns the self link URL for the given object.
func SelfLink(ver meta.Version, project, resource string, key meta.Key) string {
func SelfLink(ver meta.Version, project, resource string, key *meta.Key) string {
var prefix string
switch ver {
case meta.VersionAlpha:

View File

@ -165,28 +165,28 @@ func TestSelfLink(t *testing.T) {
ver meta.Version
project string
resource string
key meta.Key
key *meta.Key
want string
}{
{
meta.VersionAlpha,
"proj1",
"addresses",
*meta.RegionalKey("key1", "us-central1"),
meta.RegionalKey("key1", "us-central1"),
"https://www.googleapis.com/compute/alpha/projects/proj1/regions/us-central1/addresses/key1",
},
{
meta.VersionBeta,
"proj3",
"disks",
*meta.ZonalKey("key2", "us-central1-b"),
meta.ZonalKey("key2", "us-central1-b"),
"https://www.googleapis.com/compute/beta/projects/proj3/zones/us-central1-b/disks/key2",
},
{
meta.VersionGA,
"proj4",
"urlMaps",
*meta.GlobalKey("key3"),
meta.GlobalKey("key3"),
"https://www.googleapis.com/compute/v1/projects/proj4/urlMaps/key3",
},
} {