Clean up and reorganize API generation

pull/22/head
Jeff Mitchell 6 years ago
parent 9325a6422d
commit be1063d168

@ -5,11 +5,13 @@ THIS_FILE := $(lastword $(MAKEFILE_LIST))
TMP_DIR := $(shell mktemp -d)
REPO_PATH := github.com/hashicorp/watchtower
export APIGEN_BASEPATH := $(shell pwd)
bootstrap:
go generate -tags tools tools/tools.go
api:
APIGEN_BASEPATH=$(shell pwd) go generate -tags genapi api/internal/genapi/makeimports.go
$(MAKE) --environment-overrides -C api/internal/genapi api
### oplog requires protoc-gen-go v1.20.0 or later
# GO111MODULE=on go get -u github.com/golang/protobuf/protoc-gen-go@v1.40
@ -19,7 +21,8 @@ protolint:
@buf check lint
protobuild:
# To add a new directory containing a proto pass the proto's root path in through the --proto_path flag.
# To add a new directory containing a proto pass the proto's root path in
# through the --proto_path flag.
@bash make/protoc_gen_plugin.bash \
"--proto_path=internal/proto/local" \
"--proto_include_path=internal/proto/third_party" \

@ -0,0 +1,9 @@
# Determine this makefile's path.
# Be sure to place this BEFORE `include` directives, if any.
THIS_FILE := $(lastword $(MAKEFILE_LIST))
api:
go run -tags genapi .
goimports -w ${APIGEN_BASEPATH}/api
.PHONY: api

@ -0,0 +1,97 @@
// +build genapi
package main
import "os"
type generateInfo struct {
inFile string
inName string
outFile string
outName string
outPkg string
structFields string
parentName string
detailName string
templateType templateType
}
var inputStructs = []*generateInfo{
{
os.Getenv("APIGEN_BASEPATH") + "/internal/gen/controller/api/error.pb.go",
"Error",
os.Getenv("APIGEN_BASEPATH") + "/api/error.go",
"Error",
"api",
"",
"",
"",
templateTypeResource,
},
{
os.Getenv("APIGEN_BASEPATH") + "/internal/gen/controller/api/error.pb.go",
"ErrorDetails",
os.Getenv("APIGEN_BASEPATH") + "/api/error_details.go",
"ErrorDetails",
"api",
"",
"",
"",
templateTypeResource,
},
{
os.Getenv("APIGEN_BASEPATH") + "/internal/gen/controller/api/resources/hosts/host.pb.go",
"Host",
os.Getenv("APIGEN_BASEPATH") + "/api/hosts/host.go",
"Host",
"hosts",
"",
"",
"",
templateTypeResource,
},
{
os.Getenv("APIGEN_BASEPATH") + "/internal/gen/controller/api/resources/hosts/host_set.pb.go",
"HostSet",
os.Getenv("APIGEN_BASEPATH") + "/api/hosts/host_set.go",
"HostSet",
"hosts",
"",
"",
"",
templateTypeResource,
},
{
os.Getenv("APIGEN_BASEPATH") + "/internal/gen/controller/api/resources/hosts/host_catalog.pb.go",
"HostCatalog",
os.Getenv("APIGEN_BASEPATH") + "/api/hosts/host_catalog.go",
"HostCatalog",
"hosts",
"",
"",
"",
templateTypeResource,
},
{
os.Getenv("APIGEN_BASEPATH") + "/internal/gen/controller/api/resources/hosts/host_catalog.pb.go",
"StaticHostCatalogDetails",
os.Getenv("APIGEN_BASEPATH") + "/api/hosts/static_host_catalog.go",
"StaticHostCatalogDetails",
"hosts",
"",
"HostCatalog",
"StaticHostCatalog",
templateTypeDetail,
},
{
os.Getenv("APIGEN_BASEPATH") + "/internal/gen/controller/api/resources/hosts/host_catalog.pb.go",
"AwsEc2HostCatalogDetails",
os.Getenv("APIGEN_BASEPATH") + "/api/hosts/awsec2_host_catalog.go",
"AwsEc2HostCatalogDetails",
"hosts",
"",
"HostCatalog",
"AwsEc2HostCatalog",
templateTypeDetail,
},
}

@ -0,0 +1,8 @@
// +build genapi
package main
func main() {
parsePBs()
writeTemplates()
}

@ -1,7 +0,0 @@
package main
// +build genapi
//go:generate go run genapi.go
//go:generate goimports -w ../../hosts
//go:generate goimports -w ../..

@ -1,7 +1,7 @@
package main
// +build genapi
package main
import (
"bufio"
"bytes"
@ -10,117 +10,13 @@ import (
"go/format"
"go/parser"
"go/token"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strings"
"text/template"
"time"
)
type templateType int
const (
templateTypeResource templateType = iota
templateTypeDetail
)
type generateInfo struct {
inFile string
inName string
outFile string
outName string
outPkg string
structFields string
parentName string
detailName string
templateType templateType
}
var (
regex = regexp.MustCompile(`(json:".*")`)
inputStructs = []*generateInfo{
{
os.Getenv("APIGEN_BASEPATH") + "/internal/gen/controller/api/error.pb.go",
"Error",
os.Getenv("APIGEN_BASEPATH") + "/api/error.go",
"Error",
"api",
"",
"",
"",
templateTypeResource,
},
{
os.Getenv("APIGEN_BASEPATH") + "/internal/gen/controller/api/error.pb.go",
"ErrorDetails",
os.Getenv("APIGEN_BASEPATH") + "/api/error_details.go",
"ErrorDetails",
"api",
"",
"",
"",
templateTypeResource,
},
{
os.Getenv("APIGEN_BASEPATH") + "/internal/gen/controller/api/resources/hosts/host.pb.go",
"Host",
os.Getenv("APIGEN_BASEPATH") + "/api/hosts/host.go",
"Host",
"hosts",
"",
"",
"",
templateTypeResource,
},
{
os.Getenv("APIGEN_BASEPATH") + "/internal/gen/controller/api/resources/hosts/host_set.pb.go",
"HostSet",
os.Getenv("APIGEN_BASEPATH") + "/api/hosts/host_set.go",
"HostSet",
"hosts",
"",
"",
"",
templateTypeResource,
},
{
os.Getenv("APIGEN_BASEPATH") + "/internal/gen/controller/api/resources/hosts/host_catalog.pb.go",
"HostCatalog",
os.Getenv("APIGEN_BASEPATH") + "/api/hosts/host_catalog.go",
"HostCatalog",
"hosts",
"",
"",
"",
templateTypeResource,
},
{
os.Getenv("APIGEN_BASEPATH") + "/internal/gen/controller/api/resources/hosts/host_catalog.pb.go",
"StaticHostCatalogDetails",
os.Getenv("APIGEN_BASEPATH") + "/api/hosts/static_host_catalog.go",
"StaticHostCatalogDetails",
"hosts",
"",
"HostCatalog",
"StaticHostCatalog",
templateTypeDetail,
},
{
os.Getenv("APIGEN_BASEPATH") + "/internal/gen/controller/api/resources/hosts/host_catalog.pb.go",
"AwsEc2HostCatalogDetails",
os.Getenv("APIGEN_BASEPATH") + "/api/hosts/awsec2_host_catalog.go",
"AwsEc2HostCatalogDetails",
"hosts",
"",
"HostCatalog",
"AwsEc2HostCatalog",
templateTypeDetail,
},
}
)
var regex = regexp.MustCompile(`(json:".*")`)
type visitFn func(node ast.Node)
@ -129,12 +25,7 @@ func (fn visitFn) Visit(node ast.Node) ast.Visitor {
return fn
}
func main() {
createStructs()
createUtilFuncs()
}
func createStructs() {
func parsePBs() {
for _, inputStruct := range inputStructs {
inFile, err := filepath.Abs(inputStruct.inFile)
if err != nil {
@ -367,116 +258,3 @@ func createStructs() {
inputStruct.structFields = strings.Join(outBuf, "\n")
}
}
func createUtilFuncs() {
for _, inputStruct := range inputStructs {
outBuf := new(bytes.Buffer)
switch inputStruct.templateType {
case templateTypeResource:
utilFuncsTemplate.Execute(outBuf, struct {
Timestamp time.Time
Name string
Package string
StructFields string
}{
Name: inputStruct.outName,
Package: inputStruct.outPkg,
StructFields: inputStruct.structFields,
})
case templateTypeDetail:
detailTemplate.Execute(outBuf, struct {
Timestamp time.Time
Package string
StructFields string
ParentName string
DetailName string
}{
Package: inputStruct.outPkg,
StructFields: inputStruct.structFields,
ParentName: inputStruct.parentName,
DetailName: inputStruct.detailName,
})
}
outFile, err := filepath.Abs(inputStruct.outFile)
if err != nil {
fmt.Printf("error opening file %q: %v\n", inputStruct.outFile, err)
os.Exit(1)
}
if err := ioutil.WriteFile(outFile, outBuf.Bytes(), 0644); err != nil {
fmt.Printf("error writing file %q: %v\n", outFile, err)
os.Exit(1)
}
}
}
var utilFuncsTemplate = template.Must(template.New("").Parse(`// Code generated by go generate; DO NOT EDIT.
package {{ .Package }}
import (
"encoding/json"
"github.com/fatih/structs"
"github.com/hashicorp/watchtower/api/internal/strutil"
)
type {{ .Name }} struct {
{{ .StructFields }}
}
func (s *{{ .Name }}) SetDefault(key string) {
s.defaultFields = strutil.AppendIfMissing(s.defaultFields, key)
}
func (s *{{ .Name }}) UnsetDefault(key string) {
s.defaultFields = strutil.StrListDelete(s.defaultFields, key)
}
func (s {{ .Name }}) MarshalJSON() ([]byte, error) {
m := structs.Map(s)
if m == nil {
m = make(map[string]interface{})
}
for _, k := range s.defaultFields {
m[k] = nil
}
return json.Marshal(m)
}
`))
// TODO: Figure out the right way to write out the specific fields
var detailTemplate = template.Must(template.New("").Parse(`// Code generated by go generate; DO NOT EDIT.
package {{ .Package }}
import (
"fmt"
"github.com/mitchellh/mapstructure"
)
type {{ .DetailName }} struct {
*{{ .ParentName }}
{{ .StructFields }}
}
func (s {{ .ParentName }}) As{{ .DetailName }}() (*{{ .DetailName }}, error) {
out := &{{ .DetailName }}{
{{ .ParentName }}: &s,
}
decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
Result: out,
TagName: "json",
})
if err != nil {
return nil, fmt.Errorf("error creating map decoder: %w", err)
}
if err := decoder.Decode(s.Attributes); err != nil {
return nil, fmt.Errorf("error decoding attributes map: %w", err)
}
return out, nil
}
`))

@ -0,0 +1,132 @@
// +build genapi
package main
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"text/template"
"time"
)
type templateType int
const (
templateTypeResource templateType = iota
templateTypeDetail
)
func writeTemplates() {
for _, inputStruct := range inputStructs {
outBuf := new(bytes.Buffer)
switch inputStruct.templateType {
case templateTypeResource:
utilFuncsTemplate.Execute(outBuf, struct {
Timestamp time.Time
Name string
Package string
StructFields string
}{
Name: inputStruct.outName,
Package: inputStruct.outPkg,
StructFields: inputStruct.structFields,
})
case templateTypeDetail:
detailTemplate.Execute(outBuf, struct {
Timestamp time.Time
Package string
StructFields string
ParentName string
DetailName string
}{
Package: inputStruct.outPkg,
StructFields: inputStruct.structFields,
ParentName: inputStruct.parentName,
DetailName: inputStruct.detailName,
})
}
outFile, err := filepath.Abs(inputStruct.outFile)
if err != nil {
fmt.Printf("error opening file %q: %v\n", inputStruct.outFile, err)
os.Exit(1)
}
if err := ioutil.WriteFile(outFile, outBuf.Bytes(), 0644); err != nil {
fmt.Printf("error writing file %q: %v\n", outFile, err)
os.Exit(1)
}
}
}
var utilFuncsTemplate = template.Must(template.New("").Parse(`// Code generated by go generate; DO NOT EDIT.
package {{ .Package }}
import (
"encoding/json"
"github.com/fatih/structs"
"github.com/hashicorp/watchtower/api/internal/strutil"
)
type {{ .Name }} struct {
{{ .StructFields }}
}
func (s *{{ .Name }}) SetDefault(key string) {
s.defaultFields = strutil.AppendIfMissing(s.defaultFields, key)
}
func (s *{{ .Name }}) UnsetDefault(key string) {
s.defaultFields = strutil.StrListDelete(s.defaultFields, key)
}
func (s {{ .Name }}) MarshalJSON() ([]byte, error) {
m := structs.Map(s)
if m == nil {
m = make(map[string]interface{})
}
for _, k := range s.defaultFields {
m[k] = nil
}
return json.Marshal(m)
}
`))
var detailTemplate = template.Must(template.New("").Parse(`// Code generated by go generate; DO NOT EDIT.
package {{ .Package }}
import (
"fmt"
"github.com/mitchellh/mapstructure"
)
type {{ .DetailName }} struct {
*{{ .ParentName }}
{{ .StructFields }}
}
func (s {{ .ParentName }}) As{{ .DetailName }}() (*{{ .DetailName }}, error) {
out := &{{ .DetailName }}{
{{ .ParentName }}: &s,
}
decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
Result: out,
TagName: "json",
})
if err != nil {
return nil, fmt.Errorf("error creating map decoder: %w", err)
}
if err := decoder.Decode(s.Attributes); err != nil {
return nil, fmt.Errorf("error decoding attributes map: %w", err)
}
return out, nil
}
`))
Loading…
Cancel
Save