Skip to content

Commit

Permalink
Merge pull request #1 from go-batteries/openapiv3
Browse files Browse the repository at this point in the history
Openapiv3
  • Loading branch information
ikouchiha47 authored Oct 7, 2024
2 parents 640daf3 + 83dd17e commit 7b22752
Show file tree
Hide file tree
Showing 44 changed files with 6,700 additions and 44 deletions.
51 changes: 51 additions & 0 deletions .air.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
root = "."
testdata_dir = "testdata"
tmp_dir = "tmp"

[build]
args_bin = []
bin = "./out/bananas --help"
cmd = "make build.cli"
delay = 1000
exclude_dir = ["assets", "tmp", "vendor", "testdata", "out", "releases", "examples"]
exclude_file = []
exclude_regex = ["_test.go"]
exclude_unchanged = false
follow_symlink = false
full_bin = ""
include_dir = []
include_ext = ["go", "tpl", "tmpl", "html"]
include_file = []
kill_delay = "0s"
log = "build-errors.log"
poll = false
poll_interval = 0
post_cmd = []
pre_cmd = []
rerun = false
rerun_delay = 500
send_interrupt = false
stop_on_error = false

[color]
app = ""
build = "yellow"
main = "magenta"
runner = "green"
watcher = "cyan"

[log]
main_only = false
time = false

[misc]
clean_on_exit = false

[proxy]
app_port = 0
enabled = false
proxy_port = 0

[screen]
clear_on_rebuild = false
keep_scroll = true
1 change: 1 addition & 0 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
golang 1.23.2
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
OUT_DIR=out/$(shell uname)
OUT_DIR=out

build.cli:
go build -ldflags "-s -w" -o $(OUT_DIR)/bananas cmd/cli/main.go
Expand All @@ -12,6 +12,7 @@ build.all:
GOOS=darwin GOARCH=amd64 go build -ldflags "-s -w" -o tmp/darwin/amd/bananas cmd/cli/main.go

zip:
mkdir -p releases
zip releases/bananas.linux_amd64.zip tmp/linux/bananas
zip releases/bananas.darwin_amd64.zip tmp/darwin/amd/bananas
zip releases/bananas.darwin_arm64.zip tmp/darwin/bananas
Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,13 @@ $> bananas gen:structs
$> go run cmd/server/main.go
```

__to use openapiv2__

```shell

$> bananas init -n testproj -ov 2
```

__to enable grpc use__

```shell
Expand Down
50 changes: 40 additions & 10 deletions cmd/gendocs.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ func NewGenDocsCmd() *cobra.Command {
// "Name of the controller for which to generate docs",
)

docsCmd.Flags().IntP(
"oa-version",
"",
3,
"Select the openapi version to use. (2 or 3)",
)

return docsCmd
}

Expand All @@ -51,18 +58,34 @@ func (r genDocsRunner) generateDocs(cmd *cobra.Command, args []string) {
// }
path := "./protos/web"

if err := r.genAPIProto(path); err != nil {
openApiVersion, err := cmd.Flags().GetInt("oa-version")
if err != nil {
openApiVersion = DefaultOpenAPIVersion
}

if openApiVersion > DefaultOpenAPIVersion || openApiVersion < 2 {
openApiVersion = DefaultOpenAPIVersion
}

log.Println("Using OpenAPI version", openApiVersion)

if err := r.genAPIProto(path, openApiVersion); err != nil {
log.Fatalln("failed to generate swagger yamls. reason: ", err)
}

if openApiVersion == 3 {
log.Println("skipping merge for openapi v", openApiVersion, "since its already merged")
return
}

if err := r.mergeSwaggerFiles(_outFile); err != nil {
log.Fatalln("failed to merge swagger yamls. reason: ", err)
}

log.Println("combined swagger generated at", _outFile)
}

func (r genDocsRunner) buildArgs(protosPath string) []string {
func (r genDocsRunner) buildArgs(protosPath string, openApiVersion int) []string {
protoFiles, err := filepath.Glob(fmt.Sprintf("%s/**/*.proto", protosPath))
if err != nil {
log.Println("failed to find .proto files. reason:", err)
Expand All @@ -74,21 +97,28 @@ func (r genDocsRunner) buildArgs(protosPath string) []string {
}

// Build the protoc command with all .proto files
args := []string{
"-I", protosPath,
"-I", "protos/includes/googleapis",
"-I", "protos/includes/grpc_ecosystem",
"--openapiv2_out", "./openapiv2",
"--openapiv2_opt", "logtostderr=true",
args := []string{"-I", protosPath}

args = append(args, DefaultProtocArgs...)

if openApiVersion == 2 {
args = append(args,
"--openapiv2_out", "./openapiv2",
"--openapiv2_opt", "logtostderr=true",
)
} else {
args = append(args,
"--openapi_out=default_response=false:./openapiv2",
)
}

// Append all the .proto files found by the glob
args = append(args, protoFiles...)
return args
}

func (r genDocsRunner) genAPIProto(protosPath string) error {
cliArgs := r.buildArgs(protosPath)
func (r genDocsRunner) genAPIProto(protosPath string, openApiVersion int) error {
cliArgs := r.buildArgs(protosPath, openApiVersion)
log.Println("protoc", strings.Join(cliArgs, " "))

return Execute("protoc", cliArgs...)
Expand Down
11 changes: 6 additions & 5 deletions cmd/genstructs.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,14 +96,15 @@ func (r genApiRunner) buildArgs(protosPath string, overrideOutPath string, isgRP
if err != nil {
log.Fatal("failed to find directory, init first")
}

// Build the protoc command with all .proto files
args := []string{
"-I", protosPath,
"-I", "protos/includes/googleapis",
"-I", "protos/includes/grpc_ecosystem",
args := []string{"-I", protosPath}

args = append(args, DefaultProtocArgs...)
args = append(args,
fmt.Sprintf("--go_out=%s", outPath), "--go_opt=paths=import",
fmt.Sprintf("--go-grpc_out=%s", outPath), "--go-grpc_opt=paths=import",
}
)

if isgRPCMode {
args = append(args,
Expand Down
98 changes: 74 additions & 24 deletions cmd/initialize.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cmd

import (
"embed"
"fmt"
"io"
"log"
"net/http"
Expand Down Expand Up @@ -43,6 +44,8 @@ type appInitRunner struct {
middlewareFS embed.FS
}

const DefaultOpenAPIVersion int = 3

func NewInitAppCmd() *cobra.Command {
r := appInitRunner{
dirs: dirs,
Expand Down Expand Up @@ -72,6 +75,13 @@ func NewInitAppCmd() *cobra.Command {
"Specify the project name as per go.mod",
)

initCmd.Flags().IntP(
"oa-version",
"",
DefaultOpenAPIVersion,
"Specify the openapi version (2 or 3)",
)

initCmd.Flags().StringP(
"protos_dir",
"",
Expand Down Expand Up @@ -101,6 +111,17 @@ func (r appInitRunner) initApp(cmd *cobra.Command, args []string) {
log.Fatal("\napp name not provided.")
}

openApiVersion, err := cmd.Flags().GetInt("oa-version")
if err != nil {
openApiVersion = DefaultOpenAPIVersion
}

if openApiVersion > 3 || openApiVersion < 2 {
openApiVersion = DefaultOpenAPIVersion
}

log.Println("Using OpenAPI version", openApiVersion)

{
// Create the base directory structure
log.Println("creating directories..")
Expand All @@ -125,7 +146,7 @@ func (r appInitRunner) initApp(cmd *cobra.Command, args []string) {
// Copy Templates and shit for base project
log.Println("setting up base project..")

r.copyTemplates(appName, isgRPCMode)
r.copyTemplates(appName, isgRPCMode, openApiVersion)

log.Println("setting up base done..")
}
Expand All @@ -147,16 +168,25 @@ func (r appInitRunner) initApp(cmd *cobra.Command, args []string) {
log.Fatal("failed to run go mod tidy", err)
}

if isgRPCMode {
log.Println("bootstraping gprc stuff")
log.Println("installing necessary golang executables")

if err := Execute("bananas", "gen:structs", "--path=./protos/web", "--grpc"); err != nil {
log.Fatal("failed to generate proto bufs", err)
}
if err := Execute("bash", "./scripts/setup_apidocs.sh"); err != nil {
log.Println("Failed to install necessary executables. Reason", err)
log.Println("Please manually check, ./scripts/setup_apidocs.sh")
}

if err := Execute("bananas", "gen:docs", "--path=./protos/web"); err != nil {
log.Fatal("failed to generate openapi json spec", err)
{ // Bootstrap initial doc gen for hellow example
if isgRPCMode {
log.Println("bootstraping gprc stuff")

if err := Execute("bananas", "gen:structs", "--path=./protos/web", "--grpc"); err != nil {
log.Fatal("failed to generate proto bufs", err)
}
}

if err := Execute("bananas", "gen:docs", "--path=./protos/web", fmt.Sprintf("--oa-version=%d", openApiVersion)); err != nil {
log.Fatal("failed to generate openapi json spec", err)
}
}

log.Printf("\nInitialized a new Bananas app %s.\n", appName)
Expand All @@ -168,7 +198,7 @@ type render struct {
fs embed.FS
}

func (r appInitRunner) copyTemplates(projectName string, isgRPCMode bool) {
func (r appInitRunner) copyTemplates(projectName string, isgRPCMode bool, openApiVersion int) {
binaryName := getBinaryName(projectName)

commonData := bananas.TemplData{
Expand Down Expand Up @@ -242,9 +272,14 @@ func (r appInitRunner) copyTemplates(projectName string, isgRPCMode bool) {
},

"proto": {
tmplFilePath: "templates/cmd/hellow.api.proto.tmpl",
tmplFilePath: "templates/cmd/hellow.api.v3.proto.tmpl",
fs: r.baseFS,
},
"protoserver": {
tmplFilePath: "templates/cmd/http.server.go.tmpl",
fs: r.baseFS,
data: commonData,
},
}

if isgRPCMode {
Expand All @@ -253,11 +288,12 @@ func (r appInitRunner) copyTemplates(projectName string, isgRPCMode bool) {
fs: r.baseFS,
data: commonData,
}
} else {
renderers["protocolserver"] = render{
tmplFilePath: "templates/cmd/http.server.go.tmpl",
}

if openApiVersion == 2 {
renderers["proto"] = render{
tmplFilePath: "templates/cmd/hellow.api.proto.tmpl",
fs: r.baseFS,
data: commonData,
}
}

Expand All @@ -283,19 +319,21 @@ func (r appInitRunner) copyTemplates(projectName string, isgRPCMode bool) {
func (r appInitRunner) setupRequiredProtos() error {
googleApiDirRoot := "protos/includes/googleapis"
grpcEcosystemDirRoot := "protos/includes/grpc_ecosystem/protoc-gen-openapiv2"
gnosticDirRoot := "protos/includes/gnostic"

// Create directories
err := os.MkdirAll(filepath.Join(googleApiDirRoot, "google/api"), os.ModePerm)
if err != nil {
return err
}
err = os.MkdirAll(filepath.Join(googleApiDirRoot, "google/protobuf"), os.ModePerm)
if err != nil {
return err
protosDirs := []string{
filepath.Join(googleApiDirRoot, "google/api"),
filepath.Join(googleApiDirRoot, "google/protobuf"),
filepath.Join(grpcEcosystemDirRoot, "options"),
filepath.Join(gnosticDirRoot, "openapiv3"),
}
err = os.MkdirAll(filepath.Join(grpcEcosystemDirRoot, "options"), os.ModePerm)
if err != nil {
return err

for _, path := range protosDirs {
err := os.MkdirAll(path, os.ModePerm)
if err != nil {
return err
}
}

// Download proto files using HTTP
Expand All @@ -315,6 +353,10 @@ func (r appInitRunner) setupRequiredProtos() error {
url: "https://raw.githubusercontent.com/protocolbuffers/protobuf/main/src/google/protobuf/descriptor.proto",
path: filepath.Join(googleApiDirRoot, "google/protobuf/descriptor.proto"),
},
{
url: "https://raw.githubusercontent.com/protocolbuffers/protobuf/refs/heads/main/src/google/protobuf/any.proto",
path: filepath.Join(googleApiDirRoot, "google/protobuf/any.proto"),
},
{
url: "https://raw.githubusercontent.com/protocolbuffers/protobuf/refs/heads/main/src/google/protobuf/empty.proto",
path: filepath.Join(googleApiDirRoot, "google/protobuf/empty.proto"),
Expand All @@ -335,6 +377,14 @@ func (r appInitRunner) setupRequiredProtos() error {
url: "https://raw.githubusercontent.com/googleapis/googleapis/refs/heads/master/google/api/field_behavior.proto",
path: filepath.Join(googleApiDirRoot, "google/api/field_behaviour.proto"),
},
{
"https://raw.githubusercontent.com/google/gnostic/refs/heads/main/openapiv3/OpenAPIv3.proto",
filepath.Join(gnosticDirRoot, "openapiv3/OpenAPIv3.proto"),
},
{
"https://raw.githubusercontent.com/google/gnostic/refs/heads/main/openapiv3/annotations.proto",
filepath.Join(gnosticDirRoot, "openapiv3/annotations.proto"),
},
}

var wg = sync.WaitGroup{}
Expand Down
6 changes: 6 additions & 0 deletions cmd/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,9 @@ func Execute(name string, args ...string) error {

return c.Run()
}

var DefaultProtocArgs = []string{
"-I", "protos/includes/googleapis",
"-I", "protos/includes/grpc_ecosystem",
"-I", "protos/includes/gnostic",
}
Loading

0 comments on commit 7b22752

Please sign in to comment.