Shuffle things around out of new modules
This commit is contained in:
1 parent
70a8051974
commit
5cbfcf8eb6
20 files changed
+2
-29
No files matched your search
@@ -0,0 +1,11 @@
|
||||
# Determine this makefile's path.
|
||||
# Be sure to place this BEFORE `include` directives, if any.
|
||||
THIS_FILE := $(lastword $(MAKEFILE_LIST))
|
||||
|
||||
export API_GEN_BASEPATH := ${GEN_BASEPATH}/api
|
||||
|
||||
api:
|
||||
go run .
|
||||
goimports -w ${API_GEN_BASEPATH}
|
||||
|
||||
.PHONY: api
|
||||
@@ -0,0 +1,408 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"text/template"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/gen/controller/api"
|
||||
"github.com/hashicorp/boundary/internal/gen/controller/api/resources/accounts"
|
||||
"github.com/hashicorp/boundary/internal/gen/controller/api/resources/authmethods"
|
||||
"github.com/hashicorp/boundary/internal/gen/controller/api/resources/authtokens"
|
||||
"github.com/hashicorp/boundary/internal/gen/controller/api/resources/groups"
|
||||
"github.com/hashicorp/boundary/internal/gen/controller/api/resources/hostcatalogs"
|
||||
"github.com/hashicorp/boundary/internal/gen/controller/api/resources/hosts"
|
||||
"github.com/hashicorp/boundary/internal/gen/controller/api/resources/hostsets"
|
||||
"github.com/hashicorp/boundary/internal/gen/controller/api/resources/roles"
|
||||
"github.com/hashicorp/boundary/internal/gen/controller/api/resources/scopes"
|
||||
"github.com/hashicorp/boundary/internal/gen/controller/api/resources/sessions"
|
||||
"github.com/hashicorp/boundary/internal/gen/controller/api/resources/targets"
|
||||
"github.com/hashicorp/boundary/internal/gen/controller/api/resources/users"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
type structureInfo struct {
|
||||
pkg string
|
||||
name string
|
||||
fields []fieldInfo
|
||||
}
|
||||
|
||||
type fieldInfo struct {
|
||||
Name string
|
||||
ProtoName string
|
||||
FieldType string
|
||||
GenerateSdkOption bool
|
||||
SubtypeName string
|
||||
Query bool
|
||||
SkipDefault bool
|
||||
}
|
||||
|
||||
type structInfo struct {
|
||||
inProto proto.Message
|
||||
outFile string
|
||||
generatedStructure structureInfo
|
||||
templates []*template.Template
|
||||
|
||||
// Subtype name for types implementing an abstract resource type. This is
|
||||
// used as text to insert into With/Default function calls to separate out
|
||||
// implementations of the same abstract type. This way e.g. a WithLoginName
|
||||
// option turns into WithPasswordAccountLoginName which is wordy but
|
||||
// unambiguous. It also switches the behavior of the functions to work on
|
||||
// the attributes map.
|
||||
subtypeName string
|
||||
|
||||
// For non-top-level collections, this can be used to indicate the name of
|
||||
// the argument that should be used
|
||||
parentTypeName string
|
||||
|
||||
// mappings of names of resources and param names for sub slice types, e.g.
|
||||
// role principals and group members
|
||||
sliceSubTypes map[string]string
|
||||
|
||||
// outputOnly indicates that we shouldn't create options for setting members
|
||||
// for this struct
|
||||
outputOnly bool
|
||||
|
||||
// versionEnabled indicates that we should build a Version handler in
|
||||
// update. Some structs are embedded in others and shouldn't have version
|
||||
// fields.
|
||||
versionEnabled bool
|
||||
|
||||
// The parameters passed into the path. These should be non-pluralized resource names.
|
||||
// The templates will convert '-' to '_' and append an _id to them in the SDK param
|
||||
// and append an 's' to it when building the path.
|
||||
// The final value should be the path name of the resource since for single resource
|
||||
// operations all values are used for the function argument.
|
||||
// For collection based operations the last value is ignored for generating function argument.
|
||||
pathArgs []string
|
||||
|
||||
// typeOnCreate indicates that create will be creating a concrete
|
||||
// implementation of an abstract type and thus a type field is necessary
|
||||
typeOnCreate bool
|
||||
|
||||
// extraOptions allows specifying extra options that will be created for a
|
||||
// given type, e.g. arguments only valid for one call or purpose and not
|
||||
// conveyed within the item itself
|
||||
extraOptions []fieldInfo
|
||||
|
||||
// createResponseTypes controls for which structs response types are created
|
||||
createResponseTypes bool
|
||||
|
||||
// fieldFilter is a set of field names that will not result in generated API
|
||||
// fields
|
||||
fieldFilter []string
|
||||
}
|
||||
|
||||
var inputStructs = []*structInfo{
|
||||
{
|
||||
inProto: &api.Error{},
|
||||
outFile: "error.gen.go",
|
||||
outputOnly: true,
|
||||
},
|
||||
{
|
||||
inProto: &api.ErrorDetails{},
|
||||
outFile: "error_details.gen.go",
|
||||
outputOnly: true,
|
||||
},
|
||||
{
|
||||
inProto: &api.FieldError{},
|
||||
outFile: "field_error.gen.go",
|
||||
outputOnly: true,
|
||||
},
|
||||
// Scope related resources
|
||||
{
|
||||
inProto: &scopes.ScopeInfo{},
|
||||
outFile: "scopes/scope_info.gen.go",
|
||||
outputOnly: true,
|
||||
},
|
||||
{
|
||||
inProto: &scopes.Scope{},
|
||||
outFile: "scopes/scope.gen.go",
|
||||
templates: []*template.Template{
|
||||
clientTemplate,
|
||||
createTemplate,
|
||||
readTemplate,
|
||||
updateTemplate,
|
||||
deleteTemplate,
|
||||
listTemplate,
|
||||
},
|
||||
pathArgs: []string{"scope"},
|
||||
extraOptions: []fieldInfo{
|
||||
{
|
||||
Name: "SkipAdminRoleCreation",
|
||||
ProtoName: "skip_admin_role_creation",
|
||||
FieldType: "bool",
|
||||
Query: true,
|
||||
},
|
||||
{
|
||||
Name: "SkipDefaultRoleCreation",
|
||||
ProtoName: "skip_default_role_creation",
|
||||
FieldType: "bool",
|
||||
Query: true,
|
||||
},
|
||||
},
|
||||
versionEnabled: true,
|
||||
createResponseTypes: true,
|
||||
},
|
||||
// User related resources
|
||||
{
|
||||
inProto: &users.Account{},
|
||||
outFile: "users/account.gen.go",
|
||||
outputOnly: true,
|
||||
},
|
||||
{
|
||||
inProto: &users.User{},
|
||||
outFile: "users/user.gen.go",
|
||||
templates: []*template.Template{
|
||||
clientTemplate,
|
||||
createTemplate,
|
||||
readTemplate,
|
||||
updateTemplate,
|
||||
deleteTemplate,
|
||||
listTemplate,
|
||||
},
|
||||
sliceSubTypes: map[string]string{
|
||||
"Accounts": "accountIds",
|
||||
},
|
||||
pathArgs: []string{"user"},
|
||||
versionEnabled: true,
|
||||
createResponseTypes: true,
|
||||
},
|
||||
// Group related resources
|
||||
{
|
||||
inProto: &groups.Member{},
|
||||
outFile: "groups/member.gen.go",
|
||||
outputOnly: true,
|
||||
},
|
||||
{
|
||||
inProto: &groups.Group{},
|
||||
outFile: "groups/group.gen.go",
|
||||
templates: []*template.Template{
|
||||
clientTemplate,
|
||||
createTemplate,
|
||||
readTemplate,
|
||||
updateTemplate,
|
||||
deleteTemplate,
|
||||
listTemplate,
|
||||
},
|
||||
sliceSubTypes: map[string]string{
|
||||
"Members": "memberIds",
|
||||
},
|
||||
pathArgs: []string{"group"},
|
||||
versionEnabled: true,
|
||||
createResponseTypes: true,
|
||||
},
|
||||
// Role related resources
|
||||
{
|
||||
inProto: &roles.Grant{},
|
||||
outFile: "roles/grant.gen.go",
|
||||
outputOnly: true,
|
||||
},
|
||||
{
|
||||
inProto: &roles.Principal{},
|
||||
outFile: "roles/principal.gen.go",
|
||||
outputOnly: true,
|
||||
},
|
||||
{
|
||||
inProto: &roles.GrantJson{},
|
||||
outFile: "roles/grant_json.gen.go",
|
||||
outputOnly: true,
|
||||
},
|
||||
{
|
||||
inProto: &roles.Role{},
|
||||
outFile: "roles/role.gen.go",
|
||||
templates: []*template.Template{
|
||||
clientTemplate,
|
||||
createTemplate,
|
||||
readTemplate,
|
||||
updateTemplate,
|
||||
deleteTemplate,
|
||||
listTemplate,
|
||||
},
|
||||
sliceSubTypes: map[string]string{
|
||||
"Principals": "principalIds",
|
||||
"Grants": "grantStrings",
|
||||
},
|
||||
pathArgs: []string{"role"},
|
||||
versionEnabled: true,
|
||||
createResponseTypes: true,
|
||||
},
|
||||
// Auth Methods related resources
|
||||
{
|
||||
inProto: &authmethods.AuthMethod{},
|
||||
outFile: "authmethods/authmethods.gen.go",
|
||||
templates: []*template.Template{
|
||||
clientTemplate,
|
||||
createTemplate,
|
||||
readTemplate,
|
||||
updateTemplate,
|
||||
deleteTemplate,
|
||||
listTemplate,
|
||||
},
|
||||
pathArgs: []string{"auth-method"},
|
||||
typeOnCreate: true,
|
||||
versionEnabled: true,
|
||||
createResponseTypes: true,
|
||||
},
|
||||
{
|
||||
inProto: &authmethods.PasswordAuthMethodAttributes{},
|
||||
outFile: "authmethods/password_auth_method_attributes.gen.go",
|
||||
subtypeName: "PasswordAuthMethod",
|
||||
},
|
||||
// Accounts
|
||||
{
|
||||
inProto: &accounts.Account{},
|
||||
outFile: "accounts/account.gen.go",
|
||||
templates: []*template.Template{
|
||||
clientTemplate,
|
||||
createTemplate,
|
||||
readTemplate,
|
||||
updateTemplate,
|
||||
deleteTemplate,
|
||||
listTemplate,
|
||||
},
|
||||
pathArgs: []string{"account"},
|
||||
parentTypeName: "auth-method",
|
||||
versionEnabled: true,
|
||||
createResponseTypes: true,
|
||||
},
|
||||
{
|
||||
inProto: &accounts.PasswordAccountAttributes{},
|
||||
outFile: "accounts/password_account_attributes.gen.go",
|
||||
subtypeName: "PasswordAccount",
|
||||
},
|
||||
// Auth Tokens
|
||||
{
|
||||
inProto: &authtokens.AuthToken{},
|
||||
outFile: "authtokens/authtokens.gen.go",
|
||||
templates: []*template.Template{
|
||||
clientTemplate,
|
||||
readTemplate,
|
||||
deleteTemplate,
|
||||
listTemplate,
|
||||
},
|
||||
pathArgs: []string{"auth-token"},
|
||||
createResponseTypes: true,
|
||||
},
|
||||
// Host related resources
|
||||
{
|
||||
inProto: &hostcatalogs.HostCatalog{},
|
||||
outFile: "hostcatalogs/host_catalog.gen.go",
|
||||
templates: []*template.Template{
|
||||
clientTemplate,
|
||||
createTemplate,
|
||||
readTemplate,
|
||||
updateTemplate,
|
||||
deleteTemplate,
|
||||
listTemplate,
|
||||
},
|
||||
pathArgs: []string{"host-catalog"},
|
||||
typeOnCreate: true,
|
||||
versionEnabled: true,
|
||||
createResponseTypes: true,
|
||||
},
|
||||
{
|
||||
inProto: &hosts.Host{},
|
||||
outFile: "hosts/host.gen.go",
|
||||
templates: []*template.Template{
|
||||
clientTemplate,
|
||||
createTemplate,
|
||||
readTemplate,
|
||||
updateTemplate,
|
||||
deleteTemplate,
|
||||
listTemplate,
|
||||
},
|
||||
pathArgs: []string{"host"},
|
||||
parentTypeName: "host-catalog",
|
||||
versionEnabled: true,
|
||||
createResponseTypes: true,
|
||||
},
|
||||
{
|
||||
inProto: &hosts.StaticHostAttributes{},
|
||||
outFile: "hosts/static_host_attributes.gen.go",
|
||||
subtypeName: "StaticHost",
|
||||
},
|
||||
{
|
||||
inProto: &hostsets.HostSet{},
|
||||
outFile: "hostsets/host_set.gen.go",
|
||||
templates: []*template.Template{
|
||||
clientTemplate,
|
||||
createTemplate,
|
||||
readTemplate,
|
||||
updateTemplate,
|
||||
deleteTemplate,
|
||||
listTemplate,
|
||||
},
|
||||
pathArgs: []string{"host-set"},
|
||||
parentTypeName: "host-catalog",
|
||||
sliceSubTypes: map[string]string{
|
||||
"Hosts": "hostIds",
|
||||
},
|
||||
versionEnabled: true,
|
||||
createResponseTypes: true,
|
||||
},
|
||||
{
|
||||
inProto: &targets.HostSet{},
|
||||
outFile: "targets/host_set.gen.go",
|
||||
},
|
||||
{
|
||||
inProto: &targets.Target{},
|
||||
outFile: "targets/target.gen.go",
|
||||
templates: []*template.Template{
|
||||
clientTemplate,
|
||||
createTemplate,
|
||||
readTemplate,
|
||||
updateTemplate,
|
||||
deleteTemplate,
|
||||
listTemplate,
|
||||
},
|
||||
pathArgs: []string{"target"},
|
||||
sliceSubTypes: map[string]string{
|
||||
"HostSets": "hostSetIds",
|
||||
},
|
||||
extraOptions: []fieldInfo{
|
||||
{
|
||||
Name: "HostId",
|
||||
ProtoName: "host_id",
|
||||
FieldType: "string",
|
||||
SkipDefault: true,
|
||||
},
|
||||
},
|
||||
versionEnabled: true,
|
||||
typeOnCreate: true,
|
||||
createResponseTypes: true,
|
||||
},
|
||||
{
|
||||
inProto: &targets.TcpTargetAttributes{},
|
||||
outFile: "targets/tcp_target_attributes.gen.go",
|
||||
subtypeName: "TcpTarget",
|
||||
},
|
||||
{
|
||||
inProto: &sessions.Session{},
|
||||
outFile: "sessions/session.gen.go",
|
||||
templates: []*template.Template{
|
||||
clientTemplate,
|
||||
readTemplate,
|
||||
listTemplate,
|
||||
},
|
||||
pathArgs: []string{"session"},
|
||||
createResponseTypes: true,
|
||||
fieldFilter: []string{"private_key"},
|
||||
},
|
||||
{
|
||||
inProto: &sessions.SessionState{},
|
||||
outFile: "sessions/state.gen.go",
|
||||
},
|
||||
{
|
||||
inProto: &sessions.WorkerInfo{},
|
||||
outFile: "sessions/workers.gen.go",
|
||||
},
|
||||
{
|
||||
inProto: &targets.SessionAuthorization{},
|
||||
outFile: "targets/session_authorization.gen.go",
|
||||
subtypeName: "SessionAuthorization",
|
||||
},
|
||||
{
|
||||
inProto: &targets.WorkerInfo{},
|
||||
outFile: "targets/worker_info.gen.go",
|
||||
subtypeName: "WorkerInfo",
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
parsePBs()
|
||||
fillTemplates()
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
_struct "github.com/golang/protobuf/ptypes/struct"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
"google.golang.org/protobuf/types/descriptorpb"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
"google.golang.org/protobuf/types/known/wrapperspb"
|
||||
|
||||
"github.com/hashicorp/boundary/internal/gen/controller/protooptions"
|
||||
"github.com/hashicorp/boundary/sdk/strutil"
|
||||
|
||||
"github.com/iancoleman/strcase"
|
||||
)
|
||||
|
||||
func printDebug(desc protoreflect.MessageDescriptor) {
|
||||
fmt.Printf("Evaluating: %q\n", desc.FullName())
|
||||
fmt.Printf("Name: %q\n", strcase.ToCamel(string(desc.Name())))
|
||||
fmt.Printf("ProtoName: %q\n", desc.Name())
|
||||
fmt.Printf("Double Parent: %q\n", desc.FullName().Parent().Parent().Name())
|
||||
fmt.Printf("Field Descriptors:\n")
|
||||
for i := 0; i < desc.Fields().Len(); i++ {
|
||||
fieldDesc := desc.Fields().Get(i)
|
||||
fmt.Printf(" field %d: %#v\n", i, fieldDesc)
|
||||
fieldDesc.Options()
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
func parsePBs() {
|
||||
for _, in := range inputStructs {
|
||||
msg := in.inProto.ProtoReflect()
|
||||
desc := msg.Descriptor()
|
||||
|
||||
//printDebug(desc)
|
||||
|
||||
// Evaluate above, populate below.
|
||||
in.generatedStructure.pkg = packageFromFullName(desc.FullName())
|
||||
in.generatedStructure.name = string(desc.Name())
|
||||
for i := 0; i < desc.Fields().Len(); i++ {
|
||||
fd := desc.Fields().Get(i)
|
||||
if strutil.StrListContains(in.fieldFilter, string(fd.Name())) {
|
||||
continue
|
||||
}
|
||||
fi := fieldInfo{
|
||||
Name: strcase.ToCamel(string(fd.Name())),
|
||||
ProtoName: string(fd.Name()),
|
||||
}
|
||||
// Adjust for slices
|
||||
sliceText := ""
|
||||
if fd.Cardinality() == protoreflect.Repeated {
|
||||
sliceText = "[]"
|
||||
}
|
||||
// Add generate option info
|
||||
opts := fd.Options().(*descriptorpb.FieldOptions)
|
||||
if proto.GetExtension(opts, protooptions.E_GenerateSdkOption).(bool) {
|
||||
fi.GenerateSdkOption = true
|
||||
}
|
||||
switch k := fd.Kind(); k {
|
||||
case protoreflect.MessageKind:
|
||||
ptr, pkg, name := messageKind(fd)
|
||||
if pkg != "" && pkg != in.generatedStructure.pkg {
|
||||
name = fmt.Sprintf("%s.%s", pkg, name)
|
||||
}
|
||||
fi.FieldType = sliceText + ptr + name
|
||||
case protoreflect.BytesKind:
|
||||
fi.FieldType = "[]byte"
|
||||
default:
|
||||
fi.FieldType = sliceText + k.String()
|
||||
}
|
||||
in.generatedStructure.fields = append(in.generatedStructure.fields, fi)
|
||||
}
|
||||
//fmt.Printf("Parsed: %#v\n", in.generatedStructure)
|
||||
}
|
||||
}
|
||||
|
||||
func packageFromFullName(fullName protoreflect.FullName) string {
|
||||
// Example full name: controller.api.resources.groups.v1.Group
|
||||
// Crawling up the parent twice jumps back past v1.Group placing us at "groups".
|
||||
pkgName := fullName.Parent().Parent().Name()
|
||||
if pkgName.IsValid() {
|
||||
return string(pkgName)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var (
|
||||
stringValueName = (&wrapperspb.StringValue{}).ProtoReflect().Descriptor().FullName()
|
||||
boolValueName = (&wrapperspb.BoolValue{}).ProtoReflect().Descriptor().FullName()
|
||||
uInt32ValueName = (&wrapperspb.UInt32Value{}).ProtoReflect().Descriptor().FullName()
|
||||
int32ValueName = (&wrapperspb.Int32Value{}).ProtoReflect().Descriptor().FullName()
|
||||
structValueName = (&_struct.Struct{}).ProtoReflect().Descriptor().FullName()
|
||||
timestampName = (×tamppb.Timestamp{}).ProtoReflect().Descriptor().FullName()
|
||||
)
|
||||
|
||||
func messageKind(fd protoreflect.FieldDescriptor) (ptr, pkg, name string) {
|
||||
switch fd.Message().FullName() {
|
||||
case stringValueName:
|
||||
return "", "", "string"
|
||||
case boolValueName:
|
||||
return "", "", "bool"
|
||||
case uInt32ValueName:
|
||||
return "", "", "uint32"
|
||||
case int32ValueName:
|
||||
return "", "", "int32"
|
||||
case structValueName:
|
||||
return "", "", "map[string]interface{}"
|
||||
case timestampName:
|
||||
return "", "time", "Time"
|
||||
default:
|
||||
return "*", packageFromFullName(fd.Message().FullName()), string(fd.Message().Name())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,738 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/iancoleman/strcase"
|
||||
)
|
||||
|
||||
func getArgsAndPaths(in []string, parentTypeName, action string) (colArg, resArg string, colPath, resPath string) {
|
||||
resArg = fmt.Sprintf("%sId", strcase.ToLowerCamel(strings.ReplaceAll(in[len(in)-1], "-", "_")))
|
||||
strToReplace := "scope"
|
||||
if len(in) == 1 {
|
||||
if parentTypeName != "" {
|
||||
strToReplace = parentTypeName
|
||||
}
|
||||
} else {
|
||||
strToReplace = in[len(in)-2]
|
||||
}
|
||||
colArg = fmt.Sprintf("%sId", strcase.ToLowerCamel(strings.ReplaceAll(strToReplace, "-", "_")))
|
||||
colPath = fmt.Sprintf("%ss", in[len(in)-1])
|
||||
|
||||
if action != "" {
|
||||
action = fmt.Sprintf(":%s", action)
|
||||
}
|
||||
resPath = fmt.Sprintf("fmt.Sprintf(\"%s/%%s%s\", %s)", colPath, action, resArg)
|
||||
return
|
||||
}
|
||||
|
||||
type templateInput struct {
|
||||
Name string
|
||||
Package string
|
||||
Fields []fieldInfo
|
||||
PathArgs []string
|
||||
CollectionFunctionArg string
|
||||
ResourceFunctionArg string
|
||||
CollectionPath string
|
||||
ResourcePath string
|
||||
ParentTypeName string
|
||||
SliceSubTypes map[string]string
|
||||
ExtraOptions []fieldInfo
|
||||
VersionEnabled bool
|
||||
TypeOnCreate bool
|
||||
CreateResponseTypes bool
|
||||
}
|
||||
|
||||
func fillTemplates() {
|
||||
optionsMap := map[string]map[string]fieldInfo{}
|
||||
for _, in := range inputStructs {
|
||||
outBuf := new(bytes.Buffer)
|
||||
input := templateInput{
|
||||
Name: in.generatedStructure.name,
|
||||
Package: in.generatedStructure.pkg,
|
||||
Fields: in.generatedStructure.fields,
|
||||
PathArgs: in.pathArgs,
|
||||
ParentTypeName: in.parentTypeName,
|
||||
ExtraOptions: in.extraOptions,
|
||||
VersionEnabled: in.versionEnabled,
|
||||
TypeOnCreate: in.typeOnCreate,
|
||||
CreateResponseTypes: in.createResponseTypes,
|
||||
}
|
||||
|
||||
if len(in.pathArgs) > 0 {
|
||||
input.CollectionFunctionArg, input.ResourceFunctionArg, input.CollectionPath, input.ResourcePath = getArgsAndPaths(in.pathArgs, in.parentTypeName, "")
|
||||
}
|
||||
|
||||
if err := structTemplate.Execute(outBuf, input); err != nil {
|
||||
fmt.Printf("error executing struct template for resource %s: %v\n", in.generatedStructure.name, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if len(in.sliceSubTypes) > 0 {
|
||||
input.SliceSubTypes = in.sliceSubTypes
|
||||
in.templates = append(in.templates, sliceSubTypeTemplate)
|
||||
}
|
||||
|
||||
for _, t := range in.templates {
|
||||
if err := t.Execute(outBuf, input); err != nil {
|
||||
fmt.Printf("error executing function template for resource %s: %v\n", in.generatedStructure.name, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// We want to generate options per-package, not per-struct, so we
|
||||
// collate them all here for writing later. The map argument of the
|
||||
// package map is to prevent duplicates since we may have multiple e.g.
|
||||
// Name or Description fields.
|
||||
if !in.outputOnly {
|
||||
pkgOptionMap := map[string]fieldInfo{}
|
||||
for _, val := range input.Fields {
|
||||
if val.GenerateSdkOption {
|
||||
val.SubtypeName = in.subtypeName
|
||||
pkgOptionMap[val.Name] = val
|
||||
}
|
||||
}
|
||||
optionMap := optionsMap[input.Package]
|
||||
if optionMap == nil {
|
||||
optionMap = map[string]fieldInfo{}
|
||||
}
|
||||
for name, val := range pkgOptionMap {
|
||||
optionMap[name] = val
|
||||
}
|
||||
optionsMap[input.Package] = optionMap
|
||||
}
|
||||
// Add in extra defined options
|
||||
if len(in.extraOptions) > 0 {
|
||||
optionMap := optionsMap[input.Package]
|
||||
if optionMap == nil {
|
||||
optionMap = map[string]fieldInfo{}
|
||||
}
|
||||
for _, val := range in.extraOptions {
|
||||
optionMap[val.Name] = val
|
||||
}
|
||||
optionsMap[input.Package] = optionMap
|
||||
}
|
||||
|
||||
outFile, err := filepath.Abs(fmt.Sprintf("%s/%s", os.Getenv("API_GEN_BASEPATH"), in.outFile))
|
||||
if err != nil {
|
||||
fmt.Printf("error opening file %q: %v\n", in.outFile, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
outDir := filepath.Dir(outFile)
|
||||
if _, err := os.Stat(outDir); os.IsNotExist(err) {
|
||||
_ = os.Mkdir(outDir, os.ModePerm)
|
||||
}
|
||||
if err := ioutil.WriteFile(outFile, outBuf.Bytes(), 0644); err != nil {
|
||||
fmt.Printf("error writing file %q: %v\n", outFile, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Now reconstruct options per package and write them out
|
||||
for pkg, options := range optionsMap {
|
||||
outBuf := new(bytes.Buffer)
|
||||
|
||||
var fieldNames []string
|
||||
for _, v := range options {
|
||||
fieldNames = append(fieldNames, v.Name)
|
||||
}
|
||||
sort.Strings(fieldNames)
|
||||
|
||||
var fields []fieldInfo
|
||||
for _, v := range fieldNames {
|
||||
fields = append(fields, options[v])
|
||||
}
|
||||
|
||||
input := templateInput{
|
||||
Package: pkg,
|
||||
Fields: fields,
|
||||
}
|
||||
|
||||
if err := optionTemplate.Execute(outBuf, input); err != nil {
|
||||
fmt.Printf("error executing option template for package %s: %v\n", pkg, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
outFile, err := filepath.Abs(fmt.Sprintf("%s/%s/%s", os.Getenv("API_GEN_BASEPATH"), pkg, "option.gen.go"))
|
||||
if err != nil {
|
||||
fmt.Printf("error opening file %q: %v\n", "option.gen.go", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
outDir := filepath.Dir(outFile)
|
||||
if _, err := os.Stat(outDir); os.IsNotExist(err) {
|
||||
_ = os.Mkdir(outDir, os.ModePerm)
|
||||
}
|
||||
if err := ioutil.WriteFile(outFile, outBuf.Bytes(), 0644); err != nil {
|
||||
fmt.Printf("error writing file %q: %v\n", outFile, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var listTemplate = template.Must(template.New("").Funcs(
|
||||
template.FuncMap{
|
||||
"snakeCase": snakeCase,
|
||||
},
|
||||
).Parse(`
|
||||
func (c *Client) List(ctx context.Context, {{ .CollectionFunctionArg }} string, opt... Option) (*{{ .Name }}ListResult, error) {
|
||||
if {{ .CollectionFunctionArg }} == "" {
|
||||
return nil, fmt.Errorf("empty {{ .CollectionFunctionArg }} value passed into List request")
|
||||
}
|
||||
if c.client == nil {
|
||||
return nil, fmt.Errorf("nil client")
|
||||
}
|
||||
|
||||
opts, apiOpts := getOpts(opt...)
|
||||
opts.queryMap["{{ snakeCase .CollectionFunctionArg }}"] = {{ .CollectionFunctionArg }}
|
||||
|
||||
req, err := c.client.NewRequest(ctx, "GET", "{{ .CollectionPath }}", nil, apiOpts...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating List request: %w", err)
|
||||
}
|
||||
{{ if ( eq .CollectionPath "\"scopes\"" ) }}
|
||||
opts.queryMap["scope_id"] = scopeId
|
||||
{{ end }}
|
||||
if len(opts.queryMap) > 0 {
|
||||
q := url.Values{}
|
||||
for k, v := range opts.queryMap {
|
||||
q.Add(k, v)
|
||||
}
|
||||
req.URL.RawQuery = q.Encode()
|
||||
}
|
||||
|
||||
resp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error performing client request during List call: %w", err)
|
||||
}
|
||||
|
||||
target := new({{ .Name }}ListResult)
|
||||
apiErr, err := resp.Decode(target)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decoding List response: %w", err)
|
||||
}
|
||||
if apiErr != nil {
|
||||
return nil, apiErr
|
||||
}
|
||||
target.responseBody = resp.Body
|
||||
target.responseMap = resp.Map
|
||||
return target, nil
|
||||
}
|
||||
`))
|
||||
|
||||
var readTemplate = template.Must(template.New("").Parse(`
|
||||
func (c *Client) Read(ctx context.Context, {{ .ResourceFunctionArg }} string, opt... Option) (*{{ .Name }}ReadResult, error) {
|
||||
if {{ .ResourceFunctionArg }} == "" {
|
||||
return nil, fmt.Errorf("empty {{ .ResourceFunctionArg }} value passed into Read request")
|
||||
}
|
||||
if c.client == nil {
|
||||
return nil, fmt.Errorf("nil client")
|
||||
}
|
||||
|
||||
opts, apiOpts := getOpts(opt...)
|
||||
|
||||
req, err := c.client.NewRequest(ctx, "GET", {{ .ResourcePath }}, nil, apiOpts...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating Read request: %w", err)
|
||||
}
|
||||
|
||||
if len(opts.queryMap) > 0 {
|
||||
q := url.Values{}
|
||||
for k, v := range opts.queryMap {
|
||||
q.Add(k, v)
|
||||
}
|
||||
req.URL.RawQuery = q.Encode()
|
||||
}
|
||||
|
||||
resp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error performing client request during Read call: %w", err)
|
||||
}
|
||||
|
||||
target := new({{ .Name }}ReadResult)
|
||||
target.Item = new({{ .Name }})
|
||||
apiErr, err := resp.Decode(target.Item)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decoding Read response: %w", err)
|
||||
}
|
||||
if apiErr != nil {
|
||||
return nil, apiErr
|
||||
}
|
||||
target.responseBody = resp.Body
|
||||
target.responseMap = resp.Map
|
||||
return target, nil
|
||||
}
|
||||
`))
|
||||
|
||||
var deleteTemplate = template.Must(template.New("").Parse(`
|
||||
func (c *Client) Delete(ctx context.Context, {{ .ResourceFunctionArg }} string, opt... Option) (*{{ .Name }}DeleteResult, error) {
|
||||
if {{ .ResourceFunctionArg }} == "" {
|
||||
return nil, fmt.Errorf("empty {{ .ResourceFunctionArg }} value passed into Delete request")
|
||||
}
|
||||
if c.client == nil {
|
||||
return nil, fmt.Errorf("nil client")
|
||||
}
|
||||
|
||||
opts, apiOpts := getOpts(opt...)
|
||||
|
||||
req, err := c.client.NewRequest(ctx, "DELETE", {{ .ResourcePath }}, nil, apiOpts...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating Delete request: %w", err)
|
||||
}
|
||||
|
||||
if len(opts.queryMap) > 0 {
|
||||
q := url.Values{}
|
||||
for k, v := range opts.queryMap {
|
||||
q.Add(k, v)
|
||||
}
|
||||
req.URL.RawQuery = q.Encode()
|
||||
}
|
||||
|
||||
resp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error performing client request during Delete call: %w", err)
|
||||
}
|
||||
|
||||
apiErr, err := resp.Decode(nil)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decoding Delete response: %w", err)
|
||||
}
|
||||
if apiErr != nil {
|
||||
return nil, apiErr
|
||||
}
|
||||
|
||||
target := &{{ .Name }}DeleteResult{
|
||||
responseBody: resp.Body,
|
||||
responseMap: resp.Map,
|
||||
}
|
||||
return target, nil
|
||||
}
|
||||
`))
|
||||
|
||||
var createTemplate = template.Must(template.New("").Funcs(
|
||||
template.FuncMap{
|
||||
"snakeCase": snakeCase,
|
||||
},
|
||||
).Parse(`
|
||||
func (c *Client) Create (ctx context.Context, {{ if .TypeOnCreate }} resourceType string, {{ end }} {{ .CollectionFunctionArg }} string, opt... Option) (*{{ .Name }}CreateResult, error) {
|
||||
if {{ .CollectionFunctionArg }} == "" {
|
||||
return nil, fmt.Errorf("empty {{ .CollectionFunctionArg }} value passed into Create request")
|
||||
}
|
||||
|
||||
opts, apiOpts := getOpts(opt...)
|
||||
|
||||
if c.client == nil {
|
||||
return nil, fmt.Errorf("nil client")
|
||||
}
|
||||
{{ if .TypeOnCreate }} if resourceType == "" {
|
||||
return nil, fmt.Errorf("empty resourceType value passed into Create request")
|
||||
} else {
|
||||
opts.postMap["type"] = resourceType
|
||||
}{{ end }}
|
||||
|
||||
opts.postMap["{{ snakeCase .CollectionFunctionArg }}"] = {{ .CollectionFunctionArg }}
|
||||
|
||||
req, err := c.client.NewRequest(ctx, "POST", "{{ .CollectionPath }}", opts.postMap, apiOpts...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating Create request: %w", err)
|
||||
}
|
||||
{{ if ( eq .CollectionPath "\"scopes\"" ) }}
|
||||
opts.queryMap["scope_id"] = scopeId
|
||||
{{ end }}
|
||||
if len(opts.queryMap) > 0 {
|
||||
q := url.Values{}
|
||||
for k, v := range opts.queryMap {
|
||||
q.Add(k, v)
|
||||
}
|
||||
req.URL.RawQuery = q.Encode()
|
||||
}
|
||||
|
||||
resp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error performing client request during Create call: %w", err)
|
||||
}
|
||||
|
||||
target := new({{ .Name }}CreateResult)
|
||||
target.Item = new({{ .Name }})
|
||||
apiErr, err := resp.Decode(target.Item)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decoding Create response: %w", err)
|
||||
}
|
||||
if apiErr != nil {
|
||||
return nil, apiErr
|
||||
}
|
||||
target.responseBody = resp.Body
|
||||
target.responseMap = resp.Map
|
||||
return target, nil
|
||||
}
|
||||
`))
|
||||
|
||||
var updateTemplate = template.Must(template.New("").Parse(`
|
||||
func (c *Client) Update(ctx context.Context, {{ .ResourceFunctionArg }} string, version uint32, opt... Option) (*{{ .Name }}UpdateResult, error) {
|
||||
if {{ .ResourceFunctionArg }} == "" {
|
||||
return nil, fmt.Errorf("empty {{ .ResourceFunctionArg }} value passed into Update request")
|
||||
}
|
||||
if c.client == nil {
|
||||
return nil, fmt.Errorf("nil client")
|
||||
}
|
||||
|
||||
opts, apiOpts := getOpts(opt...)
|
||||
|
||||
{{ if .VersionEnabled }}
|
||||
if version == 0 {
|
||||
if !opts.withAutomaticVersioning {
|
||||
return nil, errors.New("zero version number passed into Update request and automatic versioning not specified")
|
||||
}
|
||||
existingTarget, existingErr := c.Read(ctx, {{ .ResourceFunctionArg }}, opt...)
|
||||
if existingErr != nil {
|
||||
if api.AsServerError(existingErr) != nil {
|
||||
return nil, fmt.Errorf("error from controller when performing initial check-and-set read: %w", existingErr)
|
||||
}
|
||||
return nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr)
|
||||
}
|
||||
if existingTarget == nil {
|
||||
return nil, errors.New("nil resource response found when performing initial check-and-set read")
|
||||
}
|
||||
if existingTarget.Item == nil {
|
||||
return nil, errors.New("nil resource found when performing initial check-and-set read")
|
||||
}
|
||||
version = existingTarget.Item.Version
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
opts.postMap["version"] = version
|
||||
|
||||
req, err := c.client.NewRequest(ctx, "PATCH", {{ .ResourcePath }}, opts.postMap, apiOpts...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating Update request: %w", err)
|
||||
}
|
||||
|
||||
if len(opts.queryMap) > 0 {
|
||||
q := url.Values{}
|
||||
for k, v := range opts.queryMap {
|
||||
q.Add(k, v)
|
||||
}
|
||||
req.URL.RawQuery = q.Encode()
|
||||
}
|
||||
|
||||
resp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error performing client request during Update call: %w", err)
|
||||
}
|
||||
|
||||
target := new({{ .Name }}UpdateResult)
|
||||
target.Item = new({{ .Name }})
|
||||
apiErr, err := resp.Decode(target.Item)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decoding Update response: %w", err)
|
||||
}
|
||||
if apiErr != nil {
|
||||
return nil, apiErr
|
||||
}
|
||||
target.responseBody = resp.Body
|
||||
target.responseMap = resp.Map
|
||||
return target, nil
|
||||
}
|
||||
`))
|
||||
|
||||
var sliceSubTypeTemplate = template.Must(template.New("").Funcs(
|
||||
template.FuncMap{
|
||||
"makeSlice": makeSlice,
|
||||
"snakeCase": snakeCase,
|
||||
"kebabCase": kebabCase,
|
||||
"getPathWithAction": getPathWithAction,
|
||||
},
|
||||
).Parse(`
|
||||
{{ $input := . }}
|
||||
{{ range $index, $op := makeSlice "Add" "Set" "Remove" }}
|
||||
{{ range $key, $value := $input.SliceSubTypes }}
|
||||
{{ $fullName := print $op $key }}
|
||||
{{ $actionName := kebabCase $fullName }}
|
||||
{{ $resPath := getPathWithAction $input.PathArgs $input.ParentTypeName $actionName }}
|
||||
func (c *Client) {{ $fullName }}(ctx context.Context, {{ $input.ResourceFunctionArg }} string, version uint32, {{ $value }} []string, opt... Option) (*{{ $input.Name }}UpdateResult, error) {
|
||||
if {{ $input.ResourceFunctionArg }} == "" {
|
||||
return nil, fmt.Errorf("empty {{ $input.ResourceFunctionArg }} value passed into {{ $fullName }} request")
|
||||
}
|
||||
{{ if ( not ( eq $op "Set" ) ) }}if len({{ $value }}) == 0 {
|
||||
return nil, errors.New("empty {{ $value }} passed into {{ $fullName }} request")
|
||||
}{{ end }}
|
||||
if c.client == nil {
|
||||
return nil, errors.New("nil client")
|
||||
}
|
||||
|
||||
opts, apiOpts := getOpts(opt...)
|
||||
|
||||
{{ if $input.VersionEnabled }}
|
||||
if version == 0 {
|
||||
if !opts.withAutomaticVersioning {
|
||||
return nil, errors.New("zero version number passed into {{ $fullName }} request")
|
||||
}
|
||||
existingTarget, existingErr := c.Read(ctx, {{ $input.ResourceFunctionArg }}, opt...)
|
||||
if existingErr != nil {
|
||||
if api.AsServerError(existingErr) != nil {
|
||||
return nil, fmt.Errorf("error from controller when performing initial check-and-set read: %w", existingErr)
|
||||
}
|
||||
return nil, fmt.Errorf("error performing initial check-and-set read: %w", existingErr)
|
||||
}
|
||||
if existingTarget == nil {
|
||||
return nil, errors.New("nil resource response found when performing initial check-and-set read")
|
||||
}
|
||||
if existingTarget.Item == nil {
|
||||
return nil, errors.New("nil resource found when performing initial check-and-set read")
|
||||
}
|
||||
version = existingTarget.Item.Version
|
||||
}
|
||||
{{ end }}
|
||||
opts.postMap["version"] = version
|
||||
opts.postMap["{{ snakeCase $value }}"] = {{ $value }}
|
||||
|
||||
req, err := c.client.NewRequest(ctx, "POST", {{ $resPath }}, opts.postMap, apiOpts...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating {{ $fullName }} request: %w", err)
|
||||
}
|
||||
|
||||
if len(opts.queryMap) > 0 {
|
||||
q := url.Values{}
|
||||
for k, v := range opts.queryMap {
|
||||
q.Add(k, v)
|
||||
}
|
||||
req.URL.RawQuery = q.Encode()
|
||||
}
|
||||
|
||||
resp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error performing client request during {{ $fullName }} call: %w", err)
|
||||
}
|
||||
|
||||
target := new({{ $input.Name }}UpdateResult)
|
||||
target.Item = new({{ $input.Name }})
|
||||
apiErr, err := resp.Decode(target.Item)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decoding {{ $fullName }} response: %w", err)
|
||||
}
|
||||
if apiErr != nil {
|
||||
return nil, apiErr
|
||||
}
|
||||
target.responseBody = resp.Body
|
||||
target.responseMap = resp.Map
|
||||
return target, nil
|
||||
}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
`))
|
||||
|
||||
var structTemplate = template.Must(template.New("").Parse(
|
||||
fmt.Sprint(`// Code generated by "make api"; DO NOT EDIT.
|
||||
package {{ .Package }}
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/kr/pretty"
|
||||
|
||||
"github.com/hashicorp/boundary/api"
|
||||
"github.com/hashicorp/boundary/api/scopes"
|
||||
)
|
||||
|
||||
type {{ .Name }} struct { {{ range .Fields }}
|
||||
{{ .Name }} {{ .FieldType }} `, "`json:\"{{ .ProtoName }},omitempty\"`", `{{ end }}
|
||||
{{ if ( or .CreateResponseTypes ( eq .Name "Error" ) ) }}
|
||||
responseBody *bytes.Buffer
|
||||
responseMap map[string]interface{}
|
||||
{{ end }}
|
||||
}
|
||||
|
||||
{{ if ( or .CreateResponseTypes ( eq .Name "Error" ) ) }}
|
||||
func (n {{ .Name }}) ResponseBody() *bytes.Buffer {
|
||||
return n.responseBody
|
||||
}
|
||||
|
||||
func (n {{ .Name }}) ResponseMap() map[string]interface{} {
|
||||
return n.responseMap
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
{{ if .CreateResponseTypes }}
|
||||
type {{ .Name }}ReadResult struct {
|
||||
Item *{{ .Name }}
|
||||
responseBody *bytes.Buffer
|
||||
responseMap map[string]interface{}
|
||||
}
|
||||
|
||||
func (n {{ .Name }}ReadResult) GetItem() interface{} {
|
||||
return n.Item
|
||||
}
|
||||
|
||||
func (n {{ .Name }}ReadResult) GetResponseBody() *bytes.Buffer {
|
||||
return n.responseBody
|
||||
}
|
||||
|
||||
func (n {{ .Name }}ReadResult) GetResponseMap() map[string]interface{} {
|
||||
return n.responseMap
|
||||
}
|
||||
|
||||
type {{ .Name }}CreateResult = {{ .Name }}ReadResult
|
||||
type {{ .Name }}UpdateResult = {{ .Name }}ReadResult
|
||||
|
||||
type {{ .Name }}DeleteResult struct {
|
||||
responseBody *bytes.Buffer
|
||||
responseMap map[string]interface{}
|
||||
}
|
||||
|
||||
func (n {{ .Name }}DeleteResult) GetResponseBody() *bytes.Buffer {
|
||||
return n.responseBody
|
||||
}
|
||||
|
||||
func (n {{ .Name }}DeleteResult) GetResponseMap() map[string]interface{} {
|
||||
return n.responseMap
|
||||
}
|
||||
|
||||
type {{ .Name }}ListResult struct {
|
||||
Items []*{{ .Name }}
|
||||
responseBody *bytes.Buffer
|
||||
responseMap map[string]interface{}
|
||||
}
|
||||
|
||||
func (n {{ .Name }}ListResult) GetItems() interface{} {
|
||||
return n.Items
|
||||
}
|
||||
|
||||
func (n {{ .Name }}ListResult) GetResponseBody() *bytes.Buffer {
|
||||
return n.responseBody
|
||||
}
|
||||
|
||||
func (n {{ .Name }}ListResult) GetResponseMap() map[string]interface{} {
|
||||
return n.responseMap
|
||||
}
|
||||
{{ end }}
|
||||
`)))
|
||||
|
||||
var clientTemplate = template.Must(template.New("").Parse(`
|
||||
// Client is a client for this collection
|
||||
type Client struct {
|
||||
client *api.Client
|
||||
}
|
||||
|
||||
// Creates a new client for this collection. The submitted API client is cloned;
|
||||
// modifications to it after generating this client will not have effect. If you
|
||||
// need to make changes to the underlying API client, use ApiClient() to access
|
||||
// it.
|
||||
func NewClient(c *api.Client) *Client {
|
||||
return &Client{ client: c.Clone() }
|
||||
}
|
||||
|
||||
// ApiClient returns the underlying API client
|
||||
func (c *Client) ApiClient() *api.Client {
|
||||
return c.client
|
||||
}
|
||||
`))
|
||||
|
||||
var optionTemplate = template.Must(template.New("").Parse(`
|
||||
package {{ .Package }}
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/boundary/api"
|
||||
)
|
||||
|
||||
// Option is a func that sets optional attributes for a call. This does not need
|
||||
// to be used directly, but instead option arguments are built from the
|
||||
// functions in this package. WithX options set a value to that given in the
|
||||
// argument; DefaultX options indicate that the value should be set to its
|
||||
// default. When an API call is made options are processed in ther order they
|
||||
// appear in the function call, so for a given argument X, a succession of WithX
|
||||
// or DefaultX calls will result in the last call taking effect.
|
||||
type Option func(*options)
|
||||
|
||||
type options struct {
|
||||
postMap map[string]interface{}
|
||||
queryMap map[string]string
|
||||
withAutomaticVersioning bool
|
||||
}
|
||||
|
||||
func getDefaultOptions() options {
|
||||
return options{
|
||||
postMap: make(map[string]interface{}),
|
||||
queryMap: make(map[string]string),
|
||||
}
|
||||
}
|
||||
|
||||
func getOpts(opt ...Option) (options, []api.Option) {
|
||||
opts := getDefaultOptions()
|
||||
for _, o := range opt {
|
||||
o(&opts)
|
||||
}
|
||||
var apiOpts []api.Option
|
||||
return opts, apiOpts
|
||||
}
|
||||
|
||||
// If set, and if the version is zero during an update, the API will perform a
|
||||
// fetch to get the current version of the resource and populate it during the
|
||||
// update call. This is convenient but opens up the possibility for subtle
|
||||
// order-of-modification issues, so use carefully.
|
||||
func WithAutomaticVersioning(enable bool) Option {
|
||||
return func(o *options) {
|
||||
o.withAutomaticVersioning = enable
|
||||
}
|
||||
}
|
||||
{{ range .Fields }}
|
||||
func With{{ .SubtypeName }}{{ .Name }}(in{{ .Name }} {{ .FieldType }}) Option {
|
||||
return func(o *options) { {{ if ( not ( eq .SubtypeName "" ) ) }}
|
||||
raw, ok := o.postMap["attributes"]
|
||||
if !ok {
|
||||
raw = interface{}(map[string]interface{}{})
|
||||
}
|
||||
val := raw.(map[string]interface{})
|
||||
val["{{ .ProtoName }}"] = in{{ .Name }}
|
||||
o.postMap["attributes"] = val
|
||||
{{ else if .Query }}
|
||||
o.queryMap["{{ .ProtoName }}"] = fmt.Sprintf("%v", in{{ .Name }})
|
||||
{{ else }}
|
||||
o.postMap["{{ .ProtoName }}"] = in{{ .Name }}
|
||||
{{ end }} }
|
||||
}
|
||||
{{ if ( not .SkipDefault ) }}
|
||||
func Default{{ .SubtypeName }}{{ .Name }}() Option {
|
||||
return func(o *options) { {{ if ( not ( eq .SubtypeName "" ) ) }}
|
||||
raw, ok := o.postMap["attributes"]
|
||||
if !ok {
|
||||
raw = interface{}(map[string]interface{}{})
|
||||
}
|
||||
val := raw.(map[string]interface{})
|
||||
val["{{ .ProtoName }}"] = nil
|
||||
o.postMap["attributes"] = val
|
||||
{{ else }}
|
||||
o.postMap["{{ .ProtoName }}"] = nil
|
||||
{{ end }} }
|
||||
}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
`))
|
||||
|
||||
func makeSlice(strs ...string) []string {
|
||||
return strs
|
||||
}
|
||||
|
||||
func snakeCase(in string) string {
|
||||
return strcase.ToSnake(in)
|
||||
}
|
||||
|
||||
func kebabCase(in string) string {
|
||||
return strcase.ToKebab(in)
|
||||
}
|
||||
|
||||
func getPathWithAction(resArgs []string, parentTypeName, action string) string {
|
||||
_, _, _, resPath := getArgsAndPaths(resArgs, parentTypeName, action)
|
||||
return resPath
|
||||
}
|
||||
Reference in new issue
Block a user