diff --git a/.codecov.yml b/.codecov.yml new file mode 100644 index 00000000..0ac10dcf --- /dev/null +++ b/.codecov.yml @@ -0,0 +1,10 @@ +coverage: + status: + project: + default: + threshold: 1% + server: + threshold: 1% + paths: + - pkg/grapiserver/* + patch: off diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..3d1589ad --- /dev/null +++ b/.dockerignore @@ -0,0 +1,7 @@ +.travis.yml +.reviewdog.yml +_tests +bin +vendor +README.md +LICENSE diff --git a/.github/workflows/review.yml b/.github/workflows/review.yml new file mode 100644 index 00000000..dab76d35 --- /dev/null +++ b/.github/workflows/review.yml @@ -0,0 +1,11 @@ +name: Review +on: [pull_request] +jobs: + + golangci-lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - uses: reviewdog/action-golangci-lint@v1 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..fbf805e9 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,72 @@ +name: CI +on: [push, pull_request] +jobs: + + test: + runs-on: ubuntu-latest + + strategy: + matrix: + go-version: ['1.11.x', '1.12.x', '1.13.x'] + test-task: ['test', 'test-e2e'] + + steps: + - uses: actions/setup-go@v1 + with: + go-version: ${{ matrix.go-version }} + + - uses: actions/checkout@v1 + + - name: Install protobuf + run: | + version=3.10.0 + archive=protoc-${version}-linux-x86_64 + curl -O -L https://github.com/protocolbuffers/protobuf/releases/download/v${version}/${archive}.zip + sudo unzip -d '/usr/local' ${archive}.zip 'bin/*' 'include/*' + rm -rf $archive.zip + protoc --version + if: matrix.test-task == 'test-e2e' + + - run: go mod download + + - run: echo "TARGET_REVISION=${{ github.sha }}" >> $GITHUB_ENV + if: "github.event.push" + + - run: echo "GRAPI_URL=$(pwd)" >> $GITHUB_ENV + if: "!github.event.push" + + - run: make ${{ matrix.test-task }} + env: + COVER: ${{ matrix.go-version == '1.13.x' && matrix.test-task == 'test' }} + + - run: curl -s https://codecov.io/bash | bash -s -- -t $CODECOV_TOKEN + if: matrix.go-version == '1.13.x' && matrix.test-task == 'test' + + build: + runs-on: ubuntu-latest + steps: + - uses: actions/setup-go@v1 + with: + go-version: 1.13 + + - uses: actions/checkout@v1 + + - run: go mod download + + - uses: izumin5210/action-go-crossbuild@releases/v1 + with: + package: ./cmd/grapi + + - uses: softprops/action-gh-release@v1 + with: + files: './dist/*' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + if: startsWith(github.ref, 'refs/tags/') + + - uses: izumin5210/action-homebrew@releases/v0 + with: + tap: izumin5210/homebrew-tools + token: ${{ secrets.GITHUB_TOKEN }} + tap-token: ${{ secrets.TAP_GITHUB_TOKEN }} + if: startsWith(github.ref, 'refs/tags/') diff --git a/.gitignore b/.gitignore index fe814633..19d9de15 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ /vendor /coverage.txt /bin +/dist diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 00000000..9ffbe349 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,12 @@ +output: + format: colored-line-number + +linters-settings: + lll: + line-length: 140 + +linters: + enable-all: true + disable: + - gochecknoglobals + - gochecknoinits diff --git a/.reviewdog.yml b/.reviewdog.yml new file mode 100644 index 00000000..a4f5554b --- /dev/null +++ b/.reviewdog.yml @@ -0,0 +1,11 @@ +runner: + golangci: + cmd: golangci-lint run ./... + errorformat: + - '%E%f:%l:%c: %m' + - '%E%f:%l: %m' + - '%C%.%#' + wraperr: + cmd: wraperr ./... + errorformat: + - "%f:%l:%c:%m" diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 88d8b4a9..00000000 --- a/.travis.yml +++ /dev/null @@ -1,69 +0,0 @@ -language: go - -go: "1.10" - -env: - global: - - DEP_VERSION="0.4.1" - - FILE_TO_DEPLOY="dist/*" - - PROTOBUF_VERSION="3.5.1" - - CANARY_TAG="canary" - - # GITHUB_TOKEN - - secure: "MhmvXAAzOA5HY7koCfagX1wJi2mBVQsVF5cCMaNx73l+7uDgNzGYfTn4OGKmckduiGB/mp5bTJ1DeMbPq+TDX1n/RE6kndu/Q/1vw4pbxm9BsmO9b3DizIFoWlnG+EABdAZa9igbCAfv+Jj57a0WjKGaiLazylj1mb7AYj6Vao+1zvm2ufoZvpKJcnKPqcWTsx/enJD3wx0LbqTpN5a/EdynJF9kj9Z97cGk9lS/hQHqmYVUYLYG5ZIvPjkuc6ho6pYaerupZ8aQgwraupRrNAzh70C3QgxnrCK+6RRmBMchhBsHOZq1MGhbN48ttlSMKow2NyVp8mK8+wLUnQgxEvYjVNJBXf5iKMmCTBiTO8IqgAKkkMgLaB3H0UpkeOoUQNTACPxR42+FJcwObmxYRSekTGFPwAAwnZV/1BuPrpxpT7JHa9ELlShz2OVEDz9aK/WC28/oEmtYKN8s9koKr1sx4OT5c0F/XG+er2idgCWwvfK5A0Om7Fudur+bbp1a38QWb00cAu8dPTIONe01vGXQ04d+NyohS2bcvK3iehVpa+WZ4CHkjRRuv6vQGvFMNCtwwQjXopBM99+yAykLm7yqOewbzbxFI7nCHNBc1zHvI13j7yniEoI/vdWk43e2H3Az0OOtdVASNmmp5Avwo/UWzjVACvlyNK1CST4pqYQ=" - -branches: - except: - - canary - -cache: - directories: - - $GOPATH/pkg/dep - - $HOME/include - - $HOME/bin - -before_install: -- export PATH=$PWD/bin:$HOME/bin:$PATH -- ./_script/ci-install -- go get -u github.com/golang/lint/golint - -install: -- make setup - -script: -- make lint -- make cover -- make -- make test-integration - -after_success: -- bash <(curl -s https://codecov.io/bash) - -before_deploy: -- make packages -# for canary build, ref: https://github.com/oliexdev/openScale/pull/121 -- git remote add gh https://${TRAVIS_REPO_SLUG%/*}:${GITHUB_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git -- git tag -f $CANARY_TAG -- git push -f gh $CANARY_TAG -- git remote remove gh - -deploy: -- provider: releases - skip_cleanup: true - api_key: $GITHUB_TOKEN - file_glob: true - file: $FILE_TO_DEPLOY - on: - tags: true -- provider: releases - skip_cleanup: true - api_key: $GITHUB_TOKEN - file_glob: true - file: $FILE_TO_DEPLOY - prerelease: true - overwrite: true - name: canary - body: canary build of $TRAVIS_BRANCH ($TRAVIS_COMMIT) built by Travis CI on $(date +'%F %T %Z'). - target_commitish: $TRAVIS_COMMIT - on: - branch: master diff --git a/Gopkg.lock b/Gopkg.lock deleted file mode 100644 index 9c90d283..00000000 --- a/Gopkg.lock +++ /dev/null @@ -1,381 +0,0 @@ -# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. - - -[[projects]] - name = "github.com/bradleyjkemp/cupaloy" - packages = ["."] - revision = "0d797813a76b4573317a916b0c5816dc55fff62d" - version = "v2.0.0" - -[[projects]] - name = "github.com/davecgh/go-spew" - packages = ["spew"] - revision = "346938d642f2ec3594ed81d874461961cd0faa76" - version = "v1.1.0" - -[[projects]] - name = "github.com/fatih/color" - packages = ["."] - revision = "507f6050b8568533fb3f5504de8e5205fa62a114" - version = "v1.6.0" - -[[projects]] - name = "github.com/fsnotify/fsnotify" - packages = ["."] - revision = "c2828203cd70a50dcccfb2761f8b1f8ceef9a8e9" - version = "v1.4.7" - -[[projects]] - name = "github.com/golang/mock" - packages = [ - "gomock", - "mockgen", - "mockgen/model" - ] - revision = "13f360950a79f5864a972c786a10a50e44b69541" - version = "v1.0.0" - -[[projects]] - name = "github.com/golang/protobuf" - packages = [ - "jsonpb", - "proto", - "protoc-gen-go/descriptor", - "ptypes", - "ptypes/any", - "ptypes/duration", - "ptypes/empty", - "ptypes/struct", - "ptypes/timestamp" - ] - revision = "925541529c1fa6821df4e44ce2723319eb2be768" - version = "v1.0.0" - -[[projects]] - name = "github.com/google/go-cmp" - packages = [ - "cmp", - "cmp/internal/diff", - "cmp/internal/function", - "cmp/internal/value" - ] - revision = "3af367b6b30c263d47e8895973edcca9a49cf029" - version = "v0.2.0" - -[[projects]] - branch = "master" - name = "github.com/grpc-ecosystem/go-grpc-middleware" - packages = ["."] - revision = "d0c54e68681ec7999ac17864470f3bee6521ba2b" - -[[projects]] - name = "github.com/grpc-ecosystem/grpc-gateway" - packages = [ - "runtime", - "runtime/internal", - "utilities" - ] - revision = "07f5e79768022f9a3265235f0db4ac8c3f675fec" - version = "v1.3.1" - -[[projects]] - branch = "master" - name = "github.com/hashicorp/hcl" - packages = [ - ".", - "hcl/ast", - "hcl/parser", - "hcl/scanner", - "hcl/strconv", - "hcl/token", - "json/parser", - "json/scanner", - "json/token" - ] - revision = "23c074d0eceb2b8a5bfdbb271ab780cde70f05a8" - -[[projects]] - name = "github.com/inconshreveable/mousetrap" - packages = ["."] - revision = "76626ae9c91c4f2a10f34cad8ce83ea42c93bb75" - version = "v1.0" - -[[projects]] - name = "github.com/izumin5210/clicontrib" - packages = [ - "pkg/cbuild", - "pkg/ccmd", - "pkg/clog" - ] - revision = "fdfd45e15564e04a472c90a37fb1a5d8035fe050" - version = "v0.1.0" - -[[projects]] - branch = "master" - name = "github.com/jessevdk/go-assets" - packages = ["."] - revision = "4f4301a06e153ff90e17793577ab6bf79f8dc5c5" - -[[projects]] - branch = "master" - name = "github.com/jessevdk/go-assets-builder" - packages = ["."] - revision = "b8483521738fd2198ecfc378067a4e8a6079f8e5" - -[[projects]] - name = "github.com/jessevdk/go-flags" - packages = ["."] - revision = "96dc06278ce32a0e9d957d590bb987c81ee66407" - version = "v1.3.0" - -[[projects]] - branch = "master" - name = "github.com/jinzhu/inflection" - packages = ["."] - revision = "04140366298a54a039076d798123ffa108fff46c" - -[[projects]] - name = "github.com/magiconair/properties" - packages = ["."] - revision = "c3beff4c2358b44d0493c7dda585e7db7ff28ae6" - version = "v1.7.6" - -[[projects]] - name = "github.com/mattn/go-colorable" - packages = ["."] - revision = "167de6bfdfba052fa6b2d3664c8f5272e23c9072" - version = "v0.0.9" - -[[projects]] - name = "github.com/mattn/go-isatty" - packages = ["."] - revision = "0360b2af4f38e8d38c7fce2a9f4e702702d73a39" - version = "v0.0.3" - -[[projects]] - name = "github.com/mitchellh/gox" - packages = ["."] - revision = "c9740af9c6574448fd48eb30a71f964014c7a837" - version = "v0.4.0" - -[[projects]] - branch = "master" - name = "github.com/mitchellh/iochan" - packages = ["."] - revision = "87b45ffd0e9581375c491fef3d32130bb15c5bd7" - -[[projects]] - branch = "master" - name = "github.com/mitchellh/mapstructure" - packages = ["."] - revision = "00c29f56e2386353d58c599509e8dc3801b0d716" - -[[projects]] - name = "github.com/pelletier/go-toml" - packages = ["."] - revision = "acdc4509485b587f5e675510c4f2c63e90ff68a8" - version = "v1.1.0" - -[[projects]] - name = "github.com/pkg/errors" - packages = ["."] - revision = "645ef00459ed84a119197bfb8d8205042c6df63d" - version = "v0.8.0" - -[[projects]] - name = "github.com/pmezard/go-difflib" - packages = ["difflib"] - revision = "792786c7400a136282c1664665ae0a8db921c6c2" - version = "v1.0.0" - -[[projects]] - branch = "master" - name = "github.com/serenize/snaker" - packages = ["."] - revision = "a683aaf2d516deecd70cad0c72e3ca773ecfcef0" - -[[projects]] - name = "github.com/soheilhy/cmux" - packages = ["."] - revision = "e09e9389d85d8492d313d73d1469c029e710623f" - version = "v0.1.4" - -[[projects]] - name = "github.com/spf13/afero" - packages = [ - ".", - "mem" - ] - revision = "bb8f1927f2a9d3ab41c9340aa034f6b803f4359c" - version = "v1.0.2" - -[[projects]] - name = "github.com/spf13/cast" - packages = ["."] - revision = "8965335b8c7107321228e3e3702cab9832751bac" - version = "v1.2.0" - -[[projects]] - name = "github.com/spf13/cobra" - packages = ["."] - revision = "7b2c5ac9fc04fc5efafb60700713d4fa609b777b" - version = "v0.0.1" - -[[projects]] - branch = "master" - name = "github.com/spf13/jwalterweatherman" - packages = ["."] - revision = "7c0cea34c8ece3fbeb2b27ab9b59511d360fb394" - -[[projects]] - name = "github.com/spf13/pflag" - packages = ["."] - revision = "e57e3eeb33f795204c1ca35f56c44f83227c6e66" - version = "v1.0.0" - -[[projects]] - name = "github.com/spf13/viper" - packages = ["."] - revision = "25b30aa063fc18e48662b86996252eabdcf2f0c7" - version = "v1.0.0" - -[[projects]] - branch = "master" - name = "github.com/tcnksm/go-input" - packages = ["."] - revision = "bbe13e92fa7dda71a4e012e3f427df11866c7651" - -[[projects]] - name = "go.uber.org/atomic" - packages = ["."] - revision = "8474b86a5a6f79c443ce4b2992817ff32cf208b8" - version = "v1.3.1" - -[[projects]] - name = "go.uber.org/multierr" - packages = ["."] - revision = "3c4937480c32f4c13a875a1829af76c98ca3d40a" - version = "v1.1.0" - -[[projects]] - name = "go.uber.org/zap" - packages = [ - ".", - "buffer", - "internal/bufferpool", - "internal/color", - "internal/exit", - "zapcore" - ] - revision = "35aad584952c3e7020db7b839f6b102de6271f89" - version = "v1.7.1" - -[[projects]] - branch = "master" - name = "golang.org/x/crypto" - packages = ["ssh/terminal"] - revision = "91a49db82a88618983a78a06c1cbd4e00ab749ab" - -[[projects]] - branch = "master" - name = "golang.org/x/net" - packages = [ - "context", - "http2", - "http2/hpack", - "idna", - "internal/timeseries", - "lex/httplex", - "trace" - ] - revision = "cbe0f9307d0156177f9dd5dc85da1a31abc5f2fb" - -[[projects]] - branch = "master" - name = "golang.org/x/sync" - packages = ["errgroup"] - revision = "fd80eb99c8f653c847d294a001bdf2a3a6f768f5" - -[[projects]] - branch = "master" - name = "golang.org/x/sys" - packages = [ - "unix", - "windows" - ] - revision = "88d2dcc510266da9f7f8c7f34e1940716cab5f5c" - -[[projects]] - name = "golang.org/x/text" - packages = [ - "collate", - "collate/build", - "internal/colltab", - "internal/gen", - "internal/tag", - "internal/triegen", - "internal/ucd", - "language", - "secure/bidirule", - "transform", - "unicode/bidi", - "unicode/cldr", - "unicode/norm", - "unicode/rangetable" - ] - revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" - version = "v0.3.0" - -[[projects]] - branch = "master" - name = "google.golang.org/genproto" - packages = [ - "googleapis/api/annotations", - "googleapis/rpc/status" - ] - revision = "2b5a72b8730b0b16380010cfe5286c42108d88e7" - -[[projects]] - name = "google.golang.org/grpc" - packages = [ - ".", - "balancer", - "balancer/base", - "balancer/roundrobin", - "codes", - "connectivity", - "credentials", - "encoding", - "encoding/proto", - "grpclb/grpc_lb_v1/messages", - "grpclog", - "internal", - "keepalive", - "metadata", - "naming", - "peer", - "reflection", - "reflection/grpc_reflection_v1alpha", - "resolver", - "resolver/dns", - "resolver/passthrough", - "stats", - "status", - "tap", - "transport" - ] - revision = "8e4536a86ab602859c20df5ebfd0bd4228d08655" - version = "v1.10.0" - -[[projects]] - name = "gopkg.in/yaml.v2" - packages = ["."] - revision = "7f97868eec74b32b0982dd158a51a446d1da7eb5" - version = "v2.1.1" - -[solve-meta] - analyzer-name = "dep" - analyzer-version = 1 - inputs-digest = "2c857d3a14b94944969786dc15eae617a0804401018097555db7b338471d5328" - solver-name = "gps-cdcl" - solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml deleted file mode 100644 index f052fd54..00000000 --- a/Gopkg.toml +++ /dev/null @@ -1,91 +0,0 @@ -# Gopkg.toml example -# -# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html -# for detailed Gopkg.toml documentation. -# -# required = ["github.com/user/thing/cmd/thing"] -# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] -# -# [[constraint]] -# name = "github.com/user/project" -# version = "1.0.0" -# -# [[constraint]] -# name = "github.com/user/project2" -# branch = "dev" -# source = "github.com/myfork/project2" -# -# [[override]] -# name = "github.com/x/y" -# version = "2.4.0" -# -# [prune] -# non-go = false -# go-tests = true -# unused-packages = true - -required = [ - "github.com/golang/mock/mockgen", - "github.com/jessevdk/go-assets-builder", - "github.com/mitchellh/gox", -] - -[[constraint]] - branch = "master" - name = "github.com/grpc-ecosystem/go-grpc-middleware" - -[[constraint]] - name = "github.com/grpc-ecosystem/grpc-gateway" - version = "1.3.1" - -[[constraint]] - name = "github.com/pkg/errors" - version = "0.8.0" - -[[constraint]] - name = "google.golang.org/grpc" - version = "1.10.0" - -[prune] - go-tests = true - unused-packages = true - -[[constraint]] - name = "github.com/spf13/cobra" - version = "0.0.1" - -[[constraint]] - name = "github.com/fatih/color" - version = "1.6.0" - -[[constraint]] - name = "github.com/izumin5210/clicontrib" - version = "0.1.0" - -[[constraint]] - branch = "master" - name = "golang.org/x/sync" - -[[constraint]] - branch = "master" - name = "github.com/tcnksm/go-input" - -[[constraint]] - name = "github.com/golang/mock" - version = "1.0.0" - -[[constraint]] - name = "github.com/google/go-cmp" - version = "0.2.0" - -[[constraint]] - name = "github.com/bradleyjkemp/cupaloy" - version = "2.0.0" - -[[constraint]] - branch = "master" - name = "github.com/jinzhu/inflection" - -[[constraint]] - name = "github.com/soheilhy/cmux" - version = "0.1.4" diff --git a/Makefile b/Makefile index 849b68bf..7dea79ad 100644 --- a/Makefile +++ b/Makefile @@ -1,143 +1,43 @@ -.DEFAULT_GOAL := all +PATH := ${PWD}/bin:${PATH} +export PATH -VERSION_MAJOR ?= 0 -VERSION_MINOR ?= 2 -VERSION_BUILD ?= 2 +.DEFAULT_GOAL := build -VERSION ?= v$(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_BUILD) -REVISION ?= $(shell git describe --always) -BUILD_DATE ?= $(shell date +'%Y-%m-%dT%H:%M:%SZ') -RELEASE_TYPE ?= $(if $(shell git tag --contains $(REVISION) | grep $(VERSION)),stable,canary) - -ORG := github.com/izumin5210 -PROJECT := grapi -ROOT_PKG ?= $(ORG)/$(PROJECT) - -TEMPLATE_PKG := pkg/grapicmd/internal/module/generator/template -MOCK_PKG := pkg/grapicmd/internal/module/testing -GENERATED_PKGS := $(TEMPLATE_PKG) -GENERATED_PKGS += $(MOCK_PKG) - -SRC_FILES := $(shell git ls-files --cached --others --exclude-standard | grep -E "\.go$$" | grep -v ".snapshot") -GOFMT_TARGET := $(SRC_FILES) -$(foreach pkg,$(GENERATED_PKGS),$(eval GOFMT_TARGET := $(filter-out $(pkg)/%,$(GOFMT_TARGET)))) -GOLINT_TARGET := $(shell go list ./... | grep -v "$(pkg)/testing") -$(foreach pkg,$(GENERATED_PKGS),$(eval GOLINT_TARGET := $(filter-out $(ROOT_PKG)/$(pkg),$(GOLINT_TARGET)))) - -GO_BUILD_FLAGS := -v -GO_TEST_FLAGS := -v -GO_COVER_FLAGS := -coverpkg $(shell echo $(GOLINT_TARGET) | tr ' ' ',') -coverprofile coverage.txt -covermode atomic - -XC_ARCH := 386 amd64 -XC_OS := darwin linux windows - -# Utils -#---------------------------------------------------------------- -define section - @printf "\e[34m--> $1\e[0m\n" -endef - -# dep -#---------------------------------------------------------------- -DEP_BIN_DIR := ./vendor/.bin/ -DEP_SRCS := \ - github.com/golang/mock/mockgen \ - github.com/jessevdk/go-assets-builder \ - github.com/mitchellh/gox - -DEP_BINS := $(addprefix $(DEP_BIN_DIR),$(notdir $(DEP_SRCS))) - -define dep-bin-tmpl -$(eval OUT := $(addprefix $(DEP_BIN_DIR),$(notdir $(1)))) -$(OUT): dep - $(call section,Installing $(OUT)) - @cd vendor/$(1) && GOBIN="$(shell pwd)/$(DEP_BIN_DIR)" go install . -endef - -$(foreach src,$(DEP_SRCS),$(eval $(call dep-bin-tmpl,$(src)))) - - -# App -#---------------------------------------------------------------- -BIN_DIR := ./bin/ -OUT_DIR := ./dist -GENERATED_BINS := -PACKAGES := -CMDS := $(wildcard ./cmd/*) - -define cmd-tmpl - -$(eval NAME := $(notdir $(1))) -$(eval OUT := $(addprefix $(BIN_DIR),$(NAME))) -$(eval LDFLAGS := -ldflags "-X main.name=$(NAME) -X main.version=$(VERSION) -X main.revision=$(REVISION) -X main.buildDate=$(BUILD_DATE) -X main.releaseType=$(RELEASE_TYPE)") -$(eval GENERATED_BINS += $(OUT)) -$(OUT): $(SRC_FILES) - $(call section,Building $(OUT)) - @go build $(GO_BUILD_FLAGS) $(LDFLAGS) -o $(OUT) $(1) - -.PHONY: $(NAME) -$(NAME): $(OUT) - -$(eval PACKAGES += $(NAME)-package) - -.PHONY: $(NAME)-package -$(NAME)-package: $(NAME) - @PATH=$(shell pwd)/$(DEP_BIN_DIR):$$$$PATH gox \ - $(LDFLAGS) \ - -os="$(XC_OS)" \ - -arch="$(XC_ARCH)" \ - -output="$(OUT_DIR)/$(NAME)_{{.OS}}_{{.Arch}}" \ - $(1) -endef - -$(foreach src,$(CMDS),$(eval $(call cmd-tmpl,$(src)))) - -.PHONY: all -all: $(GENERATED_BINS) - - -# Commands -#---------------------------------------------------------------- -.PHONY: setup -setup: dep $(DEP_BINS) +.PHONY: tools +tools: + go generate ./tools.go .PHONY: clean clean: - rm -rf $(BIN_DIR)/* - -.PHONY: clobber -clobber: clean - rm -rf vendor - -.PHONY: dep -dep: Gopkg.toml Gopkg.lock - $(call section,Installing dependencies) - @dep ensure -v -vendor-only + rm -rf ./bin/* .PHONY: gen -gen: - @PATH=$(shell pwd)/$(DEP_BIN_DIR):$$PATH go generate ./... +gen: tools + go generate ./... + +.PHONY: grapi +build: + go build -v -o ./bin/grapi ./cmd/grapi .PHONY: lint -lint: - $(call section,Linting) - @gofmt -e -d -s $(GOFMT_TARGET) | awk '{ e = 1; print $0 } END { if (e) exit(1) }' - @echo $(GOLINT_TARGET) | xargs -n1 golint -set_exit_status +lint: ./bin/reviewdog ./bin/golangci-lint + reviewdog -diff="git diff master" .PHONY: test test: - $(call section,Testing) - @go test $(GO_TEST_FLAGS) ./... - -.PHONY: cover -cover: - $(call section,Testing with coverage) - @go test $(GO_TEST_FLAGS) $(GO_COVER_FLAGS) ./... - -.PHONY: test-integration -test-integration: - $(call section,Integration Testing) - cd _tests && go test $(GO_TEST_FLAGS) ./... - -.PHONY: packages -packages: $(PACKAGES) +ifeq ($(COVER),true) + go test -v -coverprofile coverage.txt -covermode atomic ./... +else + go test -v ./... +endif + +.PHONY: test-e2e +test-e2e: build + go test -v -timeout 4m ./_tests/e2e --grapi=$$PWD/bin/grapi --grapi-url="$(GRAPI_URL)" --revision="$(TARGET_REVISION)" + +# linters +bin/reviewdog: + curl -sfL https://raw.githubusercontent.com/reviewdog/reviewdog/master/install.sh | sh -s -- -b ./bin v0.9.12 + +bin/golangci-lint: + curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b ./bin v1.17.1 diff --git a/README.md b/README.md index 81ef16e5..9b8cc5ff 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ # ![grapi](./grapi.png) -[![Build Status](https://travis-ci.org/izumin5210/grapi.svg?branch=master)](https://travis-ci.org/izumin5210/grapi) +[![CI](https://github.com/izumin5210/grapi/workflows/CI/badge.svg)](https://github.com/izumin5210/grapi/actions?workflow=CI) [![GoDoc](https://godoc.org/github.com/izumin5210/grapi/pkg/grapiserver?status.svg)](https://godoc.org/github.com/izumin5210/grapi/pkg/grapiserver) [![Go Report Card](https://goreportcard.com/badge/github.com/izumin5210/grapi)](https://goreportcard.com/report/github.com/izumin5210/grapi) -[![Go project version](https://badge.fury.io/go/github.com%2Fizumin5210%2Fgrapi.svg)](https://badge.fury.io/go/github.com%2Fizumin5210%2Fgrapi) +[![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/izumin5210/grapi)](http://github.com/izumin5210/grapi/releases/latest) [![license](https://img.shields.io/github/license/izumin5210/grapi.svg)](./LICENSE) :open_mouth: A surprisingly easy API server and generator in gRPC and Go @@ -18,22 +18,172 @@ [![asciicast](https://asciinema.org/a/176280.png)](https://asciinema.org/a/176280) -## Getting Started -### Installation -#### For Homebrew users +## :warning: Migrate 0.4.x -> 0.5.x :warning: +[grapiserver](https://godoc.org/github.com/izumin5210/grapi/pkg/grapiserver) will not handle os signals from v0.5.x. +We recommend to use [`appctx.Global()`](https://godoc.org/github.com/srvc/appctx#Global) if you want to handle them. -``` -$ brew install protobuf -$ brew install izumin5210/tools/grapi -``` +
+:memo: How to migrate + +0. Bump grapi version + - `go get -u github.com/izumin5210/grapi@v0.5' +1. Update `cmd/server/run.go` + - ```diff + // Application context + - ctx := context.Background() + + ctx := appctx.Global() + ``` + - ```diff + - return s.ServeContext(ctx) + + return s.Serve(ctx) + ``` + +
+ + +## :warning: Migrate 0.3.x -> 0.4.x :warning: +Some tools that are depended by grapi are updated. If you have a grapi project <=v0.3.x, you should migrate it. + +
+:memo: How to migrate + +0. Bump grapi version + - If you use [dep](https://golang.github.io/dep/), update `Gopkg.toml` + ```diff + [[constraint]] + name = "github.com/izumin5210/grapi" + - version = "0.3.0" + + version = "0.4.0" + ``` + - and run `dep ensure` +1. Update [gex](https://github.com/izumin5210/gex) and `tools.go` + - ``` + go get -u github.com/izumin5210/gex/cmd/gex + gex --regen + ``` +1. Initialize [Go Modules](https://github.com/golang/go/wiki/Modules) + - ``` + go mod init + go mod tidy + ``` +1. Update `grapi.toml` + - ```diff + package = "yourcompany.yourappname" + + [grapi] + server_dir = "./app/server" + + [protoc] + protos_dir = "./api/protos" + out_dir = "./api" + import_dirs = [ + "./api/protos", + - "./vendor/github.com/grpc-ecosystem/grpc-gateway", + - "./vendor/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis", + + '{{ module "github.com/grpc-ecosystem/grpc-gateway" }}', + + '{{ module "github.com/grpc-ecosystem/grpc-gateway" }}/third_party/googleapis', + ] + + [[protoc.plugins]] + name = "go" + args = { plugins = "grpc", paths = "source_relative" } + + [[protoc.plugins]] + name = "grpc-gateway" + args = { logtostderr = true, paths = "source_relative" } + + [[protoc.plugins]] + name = "swagger" + args = { logtostderr = true } + ``` +1. Drop dep + - ``` + rm Gopkg.* + ``` + + +
-#### Download built binary -You should install `protoc` command from [google/protobuf](https://github.com/google/protobuf). +## :warning: Migrate 0.2.x -> 0.3.x :warning: +grapi v0.3.0 has some breaking changes. If you have a grapi project <=v0.2.x, you should migrate it. -- Linux: - - `curl -Lo grapi https://github.com/izumin5210/grapi/releases/download/v0.2.2/grapi_linux_amd64 && chmod +x grapi && sudo mv grapi /usr/local/bin` -- masOS: - - `curl -Lo grapi https://github.com/izumin5210/grapi/releases/download/v0.2.2/grapi_darwin_amd64 && chmod +x grapi && sudo mv grapi /usr/local/bin` +
+:memo: How to migrate + +0. Bump grapi version + - If you use [dep](https://golang.github.io/dep/), update `Gopkg.toml` + ```diff + [[constraint]] + name = "github.com/izumin5210/grapi" + - version = "0.2.2" + + version = "0.3.0" + ``` + - and run `dep ensure` +1. Introduce [gex](https://github.com/izumin5210/gex) + - ``` + go get github.com/izumin5210/gex/cmd/gex + ``` +1. Add defualt generator plugins: + - ``` + gex \ + --add github.com/izumin5210/grapi/cmd/grapi \ + --add github.com/izumin5210/grapi/cmd/grapi-gen-command \ + --add github.com/izumin5210/grapi/cmd/grapi-gen-service \ + --add github.com/izumin5210/grapi/cmd/grapi-gen-scaffold-service \ + --add github.com/izumin5210/grapi/cmd/grapi-gen-type + ``` +1. Add protoc plugins via gex + - ``` + gex \ + --add github.com/golang/protobuf/protoc-gen-go \ + --add github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway \ + --add github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger + ``` + - Remove protoc plugins from `Gopkg.toml` + ```diff + -required = [ + - "github.com/golang/protobuf/protoc-gen-go", + - "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway", + - "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger", + -] + ``` +1. Update `grapi.toml` + - ```diff + +package = "yourcompany.yourappname" + + + [grapi] + server_dir = "./app/server" + + [protoc] + protos_dir = "./api/protos" + out_dir = "./api" + import_dirs = [ + + "./api/protos", + "./vendor/github.com/grpc-ecosystem/grpc-gateway", + "./vendor/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis", + ] + + [[protoc.plugins]] + - path = "./vendor/github.com/golang/protobuf/protoc-gen-go" + name = "go" + args = { plugins = "grpc", paths = "source_relative" } + + [[protoc.plugins]] + - path = "./vendor/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway" + name = "grpc-gateway" + - args = { logtostderr = true } + + args = { logtostderr = true, paths = "source_relative" } + + [[protoc.plugins]] + - path = "./vendor/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger" + name = "swagger" + args = { logtostderr = true } + ``` + +
+ + +## Getting Started ### Create a new application ``` @@ -95,3 +245,26 @@ $ grapi import-books # run the command $ grapi build ``` +## Installation + +1. **grapi** + - Linux + - `curl -Lo grapi https://github.com/izumin5210/grapi/releases/download/v0.2.2/grapi_linux_amd64 && chmod +x grapi && sudo mv grapi /usr/local/bin` + - macOS + - `brew install izumin5210/tools/grapi` + - others + - `go get github.com/izumin5210/grapi/cmd/grapi` +1. **dep** or **Modules** + - [dep](https://golang.github.io/dep/) + - macOS + - `brew install dep` + - others + - See [Installation ยท dep](https://golang.github.io/dep/docs/installation.html) + - `curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh` + - [Modules](https://github.com/golang/go/wiki/Modules) (experimental) + - Use Go 1.11 and set `GO111MODULE=on` your env vars +1. **protoc** + - macOS + - `brew install protobuf` + - others + - Download and install from [google/protobuf](https://github.com/google/protobuf) diff --git a/_script/ci-install b/_script/ci-install deleted file mode 100755 index e8ec9332..00000000 --- a/_script/ci-install +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env bash - -set -eu -set -o pipefail - -BIN_DIR=$HOME/bin - -section() { - printf "\e[34m--> $1\e[0m\n" -} - -install_protoc() { - section "Installing protoc" - - if [ ! -d "${BIN_DIR}/protoc" ]; then - curl -L -s https://github.com/google/protobuf/releases/download/v$PROTOBUF_VERSION/protoc-$PROTOBUF_VERSION-linux-x86_64.zip -o protoc-$PROTOBUF_VERSION.zip - unzip -o protoc-$PROTOBUF_VERSION.zip -d $HOME - else - echo "skipped" - fi - - protoc --version -} - -install_dep() { - section "Installing dep" - - if [ ! -d "${BIN_DIR}/dep" ]; then - curl -L -s https://github.com/golang/dep/releases/download/v${DEP_VERSION}/dep-linux-amd64 -o ${BIN_DIR}/dep - chmod +x ${BIN_DIR}/dep - else - echo "skipped" - fi - - dep version -} - -main() { - install_protoc - install_dep -} - -main diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ProjectGenerator-cmd-server-run.go b/_tests/e2e/.snapshots/TestE2E_withModules--cmd-server-main.go similarity index 64% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ProjectGenerator-cmd-server-run.go rename to _tests/e2e/.snapshots/TestE2E_withModules--cmd-server-main.go index bd59a657..76322ecc 100644 --- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ProjectGenerator-cmd-server-run.go +++ b/_tests/e2e/.snapshots/TestE2E_withModules--cmd-server-main.go @@ -4,20 +4,13 @@ import ( "os" "google.golang.org/grpc/grpclog" - - "testapp/app" ) func main() { - os.Exit(run()) -} - -func run() int { - err := app.Run() + err := run() if err != nil { grpclog.Errorf("server was shutdown with errors: %v", err) - return 1 + os.Exit(1) } - return 0 } diff --git a/pkg/grapicmd/internal/module/generator/template/init/app/run.go.tmpl b/_tests/e2e/.snapshots/TestE2E_withModules--cmd-server-run.go similarity index 55% rename from pkg/grapicmd/internal/module/generator/template/init/app/run.go.tmpl rename to _tests/e2e/.snapshots/TestE2E_withModules--cmd-server-run.go index c9c90169..485f8be3 100644 --- a/pkg/grapicmd/internal/module/generator/template/init/app/run.go.tmpl +++ b/_tests/e2e/.snapshots/TestE2E_withModules--cmd-server-run.go @@ -1,16 +1,21 @@ -package app +package main import ( + "github.com/srvc/appctx" + "github.com/izumin5210/grapi/pkg/grapiserver" ) -// Run starts the grapiserver. -func Run() error { +func run() error { + // Application context + ctx := appctx.Global() + s := grapiserver.New( grapiserver.WithDefaultLogger(), grapiserver.WithServers( // TODO ), ) - return s.Serve() + return s.Serve(ctx) } + diff --git a/_tests/e2e/.snapshots/TestE2E_withModules--grapi.toml b/_tests/e2e/.snapshots/TestE2E_withModules--grapi.toml new file mode 100644 index 00000000..5b763733 --- /dev/null +++ b/_tests/e2e/.snapshots/TestE2E_withModules--grapi.toml @@ -0,0 +1,26 @@ +package = "testuser.sampleapp" + +[grapi] +server_dir = "./app/server" + +[protoc] +protos_dir = "./api/protos" +out_dir = "./api" +import_dirs = [ + "./api/protos", + '{{ module "github.com/grpc-ecosystem/grpc-gateway" }}', + '{{ module "github.com/grpc-ecosystem/grpc-gateway" }}/third_party/googleapis', +] + + [[protoc.plugins]] + name = "go" + args = { plugins = "grpc", paths = "source_relative" } + + [[protoc.plugins]] + name = "grpc-gateway" + args = { logtostderr = true, paths = "source_relative" } + + [[protoc.plugins]] + name = "swagger" + args = { logtostderr = true } + diff --git a/_tests/e2e/.snapshots/TestE2E_withModules--tools.go b/_tests/e2e/.snapshots/TestE2E_withModules--tools.go new file mode 100644 index 00000000..210f0e62 --- /dev/null +++ b/_tests/e2e/.snapshots/TestE2E_withModules--tools.go @@ -0,0 +1,32 @@ +// Code generated by github.com/izumin5210/gex. DO NOT EDIT. + +// +build tools + +package tools + +// tool dependencies +import ( + _ "github.com/golang/protobuf/protoc-gen-go" + _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway" + _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger" + _ "github.com/izumin5210/gex/cmd/gex" + _ "github.com/izumin5210/grapi/cmd/grapi" + _ "github.com/izumin5210/grapi/cmd/grapi-gen-command" + _ "github.com/izumin5210/grapi/cmd/grapi-gen-scaffold-service" + _ "github.com/izumin5210/grapi/cmd/grapi-gen-service" + _ "github.com/izumin5210/grapi/cmd/grapi-gen-type" +) + +// If you want to use tools, please run the following command: +// go generate ./tools.go +// +//go:generate go build -v -o=./bin/protoc-gen-go github.com/golang/protobuf/protoc-gen-go +//go:generate go build -v -o=./bin/protoc-gen-grpc-gateway github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway +//go:generate go build -v -o=./bin/protoc-gen-swagger github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger +//go:generate go build -v -o=./bin/gex github.com/izumin5210/gex/cmd/gex +//go:generate go build -v -o=./bin/grapi github.com/izumin5210/grapi/cmd/grapi +//go:generate go build -v -o=./bin/grapi-gen-command github.com/izumin5210/grapi/cmd/grapi-gen-command +//go:generate go build -v -o=./bin/grapi-gen-scaffold-service github.com/izumin5210/grapi/cmd/grapi-gen-scaffold-service +//go:generate go build -v -o=./bin/grapi-gen-service github.com/izumin5210/grapi/cmd/grapi-gen-service +//go:generate go build -v -o=./bin/grapi-gen-type github.com/izumin5210/grapi/cmd/grapi-gen-type + diff --git a/_tests/e2e/main_test.go b/_tests/e2e/main_test.go new file mode 100644 index 00000000..cea7139d --- /dev/null +++ b/_tests/e2e/main_test.go @@ -0,0 +1,359 @@ +package main + +import ( + "bytes" + "context" + "flag" + "fmt" + "go/ast" + "go/format" + "go/parser" + "go/token" + "io/ioutil" + "net" + "net/http" + "os" + "os/exec" + "path/filepath" + "strconv" + "strings" + "testing" + "time" + + "github.com/bradleyjkemp/cupaloy/v2" + "golang.org/x/tools/go/packages/packagestest" +) + +var ( + grapiCmd = flag.String("grapi", "grapi", "path of grapi command") + grapiURL = flag.String("grapi-url", "", "url for replacing github.com/izumin5210/grapi") + revision = flag.String("revision", "", "target revision") +) + +func TestE2E_withModules(t *testing.T) { + invokeE2ETest(t, packagestest.Modules) +} + +func TestE2E_withDep(t *testing.T) { + t.SkipNow() + invokeE2ETest(t, packagestest.GOPATH) +} + +func invokeE2ETest(t *testing.T, exporter packagestest.Exporter) { + t.Helper() + + exported := packagestest.Export(t, exporter, []packagestest.Module{ + {Name: "sampleapp", Files: map[string]interface{}{".keep": ""}}, + }) + defer exported.Cleanup() + + rootPath := exported.Config.Dir + exported.Config.Dir = filepath.Dir(rootPath) + checkNoErr(t, os.RemoveAll(rootPath)) + + // init + { + args := []string{"--debug", "init", "--package", "testuser.sampleapp"} + if *grapiURL != "" { + // Add replacements later + args = append(args, "--replace-grapi="+*grapiURL) + } else if *revision != "" { + args = append(args, "--revision="+*revision) + } else { + args = append(args, "--HEAD") + } + if exporter.Name() == "GOPATH" { + args = append(args, "--use-dep") + } + args = append(args, filepath.Base(rootPath)) + invoke(t, exported, exec.Command(*grapiCmd, args...)) + checkExistence(t, rootPath) + t.Log("Initialize a project successfully") + } + + exported.Config.Dir = rootPath + + ignoreFiles := map[string]struct{}{ + "/go.mod": struct{}{}, + "/go.sum": struct{}{}, + } + + err := filepath.Walk(rootPath, func(path string, info os.FileInfo, err error) error { + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if info.IsDir() { + return nil + } + rel := strings.TrimPrefix(path, rootPath) + if _, ok := ignoreFiles[rel]; ok { + return nil + } + if strings.HasPrefix(rel, "/bin/") { + return nil + } + + t.Run(rel, func(t *testing.T) { + data, err := ioutil.ReadFile(path) + if err != nil { + t.Errorf("failed to open %s: %v", path, err) + } + + cupaloy.SnapshotT(t, string(data)) + }) + + return nil + }) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + + // generate service + { + invoke(t, exported, exec.Command(*grapiCmd, "--debug", "g", "service", "book", "list")) + checkExistence(t, filepath.Join(rootPath, "app", "server", "book_server.go")) + t.Log("Generate a service successfully") + } + + port := getFreePort(t) + updateRun(t, rootPath, port) + updateServerImpl(t, rootPath) + + // run server + { + t.Log("Start the server") + cmd := exec.Command(*grapiCmd, "--debug", "server") + sdCh := make(chan struct{}, 1) + go func() { + defer close(sdCh) + invoke(t, exported, cmd) + }() + + startedAt := time.Now() + var ( + resp *http.Response + retryCnt int + err error + ) + + for { + func() { + defer recover() + resp, err = http.Get(fmt.Sprintf("http://localhost:%d/books", port)) + }() + if err != nil && time.Since(startedAt) < 120*time.Second { + time.Sleep(5 * time.Second) + retryCnt++ + } else { + break + } + } + + if err != nil { + t.Fatalf("Unexpected error (retry count: %d): %v", retryCnt, err) + } + + if got, want := resp.StatusCode, 200; got != want { + t.Errorf("Response status is %d, want %d", got, want) + } + + t.Log("HTTP Request successfully") + + sendSignal(t, cmd, os.Interrupt) + toCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + select { + case <-sdCh: + t.Log("Shutdown server successfully") + case <-toCtx.Done(): + t.Log("Deadline exceeded stopping server") + sendSignal(t, cmd, os.Kill) + <-sdCh + } + } +} + +func invoke(t *testing.T, exported *packagestest.Exported, cmd *exec.Cmd) { + t.Helper() + cmd.Dir = exported.Config.Dir + for _, kv := range exported.Config.Env { + if strings.HasPrefix(kv, "GOPROXY=") { + continue + } + cmd.Env = append(cmd.Env, kv) + } + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + err := cmd.Run() + if err != nil { + t.Fatalf("failed to execute command %v: %v", cmd, err) + } +} + +func checkNoErr(t *testing.T, err error) { + t.Helper() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } +} + +func checkExistence(t *testing.T, path string) { + t.Helper() + _, err := os.Stat(path) + if err != nil { + if os.IsNotExist(err) { + t.Fatalf("%s does not exist: %v", path, err) + } + t.Fatalf("failed to check file existence: %v", err) + } +} + +func getFreePort(t *testing.T) int { + t.Helper() + lis, err := net.Listen("tcp", ":0") + checkNoErr(t, err) + defer lis.Close() + + return lis.Addr().(*net.TCPAddr).Port +} + +func sendSignal(t *testing.T, cmd *exec.Cmd, sig os.Signal) { + t.Helper() + checkNoErr(t, cmd.Process.Signal(sig)) +} + +type visitor struct { + VisitFunc func(ast.Visitor, ast.Node) ast.Visitor +} + +func (v *visitor) Visit(node ast.Node) ast.Visitor { + return v.VisitFunc(v, node) +} + +func updateRun(t *testing.T, rootPath string, port int) { + data, err := ioutil.ReadFile(filepath.Join(rootPath, "cmd", "server", "run.go")) + checkNoErr(t, err) + + fset := token.NewFileSet() + f, err := parser.ParseFile(fset, "", data, parser.DeclarationErrors) + checkNoErr(t, err) + + ast.Walk(&visitor{ + VisitFunc: func(v ast.Visitor, n ast.Node) ast.Visitor { + switch n := n.(type) { + case *ast.GenDecl: + if n.Tok == token.IMPORT { + n.Specs = append(n.Specs, &ast.ImportSpec{ + Path: &ast.BasicLit{ + Kind: token.STRING, + Value: strconv.Quote("sampleapp/app/server"), + }, + }) + } + case *ast.CallExpr: + switch fun := n.Fun.(type) { + case *ast.SelectorExpr: + switch fun.Sel.Name { + case "New": + n.Args = append(n.Args, &ast.CallExpr{ + Fun: &ast.SelectorExpr{ + X: ast.NewIdent("grapiserver"), + Sel: ast.NewIdent("WithGatewayAddr"), + }, + Args: []ast.Expr{ + &ast.BasicLit{ + Kind: token.STRING, + Value: strconv.Quote("tcp"), + }, + &ast.BasicLit{ + Kind: token.STRING, + Value: strconv.Quote(fmt.Sprintf(":%d", port)), + }, + }, + }) + case "WithServers": + n.Args = append(n.Args, &ast.CallExpr{ + Fun: &ast.SelectorExpr{ + X: ast.NewIdent("server"), + Sel: ast.NewIdent("NewBookServiceServer"), + }, + }) + } + } + } + return v + }, + }, f) + + buf := new(bytes.Buffer) + err = format.Node(buf, token.NewFileSet(), f) + checkNoErr(t, err) + err = ioutil.WriteFile(filepath.Join(rootPath, "cmd", "server", "run.go"), buf.Bytes(), 0755) + checkNoErr(t, err) +} + +func updateServerImpl(t *testing.T, rootPath string) { + data, err := ioutil.ReadFile(filepath.Join(rootPath, "app", "server", "book_server.go")) + checkNoErr(t, err) + + fset := token.NewFileSet() + f, err := parser.ParseFile(fset, "", data, parser.DeclarationErrors) + checkNoErr(t, err) + + ast.Walk(&visitor{ + VisitFunc: func(v ast.Visitor, n ast.Node) ast.Visitor { + switch n := n.(type) { + case *ast.GenDecl: + if n.Tok == token.IMPORT { + n.Specs = []ast.Spec{ + &ast.ImportSpec{ + Path: &ast.BasicLit{ + Kind: token.STRING, + Value: strconv.Quote("context"), + }, + }, + &ast.ImportSpec{ + Path: &ast.BasicLit{ + Kind: token.STRING, + Value: strconv.Quote("github.com/izumin5210/grapi/pkg/grapiserver"), + }, + }, + &ast.ImportSpec{ + Name: &ast.Ident{Name: "api_pb"}, + Path: &ast.BasicLit{ + Kind: token.STRING, + Value: strconv.Quote("sampleapp/api"), + }, + }, + } + } + case *ast.FuncDecl: + if n.Name.Name == "ListBooks" { + n.Body.List = []ast.Stmt{ + &ast.ReturnStmt{ + Results: []ast.Expr{ + &ast.UnaryExpr{ + X: &ast.CompositeLit{ + Type: &ast.SelectorExpr{ + X: ast.NewIdent("api_pb"), + Sel: ast.NewIdent("ListBooksResponse"), + }, + }, + Op: token.AND, + }, + &ast.Ident{Name: "nil"}, + }, + }, + } + } + } + return v + }, + }, f) + + buf := new(bytes.Buffer) + err = format.Node(buf, token.NewFileSet(), f) + checkNoErr(t, err) + err = ioutil.WriteFile(filepath.Join(rootPath, "app", "server", "book_server.go"), buf.Bytes(), 0755) + checkNoErr(t, err) +} diff --git a/_tests/integration/main_test.go b/_tests/integration/main_test.go deleted file mode 100644 index aa118b01..00000000 --- a/_tests/integration/main_test.go +++ /dev/null @@ -1,275 +0,0 @@ -package main - -import ( - "bytes" - "context" - "fmt" - "go/ast" - "go/format" - "go/parser" - "go/token" - "net/http" - "os" - "os/exec" - "path/filepath" - "runtime" - "strconv" - "testing" - "time" - - "github.com/spf13/afero" -) - -func Test_Integration(t *testing.T) { - _, testfilepath, _, _ := runtime.Caller(0) - wd := filepath.Dir(testfilepath) - bin := filepath.Join(wd, "..", "..", "bin", "grapi") - gopath := filepath.Join(wd, "go") - srcDir := filepath.Join(gopath, "src") - - fs := afero.NewOsFs() - name := "sample" - rootPath := filepath.Join(srcDir, name) - - fs.MkdirAll(srcDir, 0755) - defer fs.RemoveAll(gopath) - - cmd := exec.Command(bin, "--debug", "init", "--HEAD", name) - cmd.Dir = srcDir - cmd.Env = append(os.Environ(), "GOPATH="+gopath) - out, err := cmd.CombinedOutput() - if err != nil { - t.Fatalf("failed to initialize project: %v\n%s", err, string(out)) - } - - if ok, err := afero.DirExists(fs, rootPath); err != nil || !ok { - t.Fatalf("%s does not exist: %v", rootPath, err) - } - t.Log("Initialize a project successfully") - - cmd = exec.Command(bin, "--debug", "g", "service", "book", "list") - cmd.Dir = rootPath - cmd.Env = append(os.Environ(), "GOPATH="+gopath) - out, err = cmd.CombinedOutput() - if err != nil { - t.Fatalf("failed to generate service: %v\n%s", err, string(out)) - } - - svrPath := filepath.Join(rootPath, "app", "server", "book_server.go") - if ok, err := afero.Exists(fs, svrPath); err != nil || !ok { - t.Fatalf("%s does not exist: %v", svrPath, err) - } - t.Log("Generate a service successfully") - - port := 15261 - - updateRun(t, fs, rootPath, port) - updateServerImpl(t, fs, rootPath) - - t.Log("Start the server") - svrCtx, cancel := context.WithCancel(context.Background()) - cmd = exec.CommandContext(svrCtx, bin, "--debug", "server") - cmd.Dir = rootPath - cmd.Env = append(os.Environ(), "GOPATH="+gopath) - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr - err = cmd.Start() - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - defer func() { - if cmd.Process != nil && cmd.ProcessState != nil && !cmd.ProcessState.Exited() { - cmd.Process.Kill() - } - }() - - startedAt := time.Now() - var resp *http.Response - var retryCnt int - - for { - func() { - defer recover() - resp, err = http.Get(fmt.Sprintf("http://localhost:%d/books", port)) - }() - if err != nil && time.Since(startedAt) < 120*time.Second { - time.Sleep(5 * time.Second) - retryCnt++ - } else { - break - } - } - - if err != nil { - t.Fatalf("Unexpected error (retry count: %d): %v", retryCnt, err) - } - - if got, want := resp.StatusCode, 200; got != want { - t.Errorf("Response status is %d, want %d", got, want) - } - - t.Log("HTTP Request successfully") - - cancel() - toCtx, _ := context.WithTimeout(context.Background(), 30*time.Second) - select { - case <-svrCtx.Done(): - t.Log("Shutdown server successfully") - case <-toCtx.Done(): - t.Log("Deadline exceeded stopping server") - cmd.Process.Signal(os.Kill) - } - err = cmd.Wait() -} - -type visitor struct { - VisitFunc func(ast.Visitor, ast.Node) ast.Visitor -} - -func (v *visitor) Visit(node ast.Node) ast.Visitor { - return v.VisitFunc(v, node) -} - -func updateRun(t *testing.T, fs afero.Fs, rootPath string, port int) { - data, err := afero.ReadFile(fs, filepath.Join(rootPath, "app", "run.go")) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - - fset := token.NewFileSet() - f, err := parser.ParseFile(fset, "", data, parser.DeclarationErrors) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - - ast.Walk(&visitor{ - VisitFunc: func(v ast.Visitor, n ast.Node) ast.Visitor { - switch n := n.(type) { - case *ast.GenDecl: - if n.Tok == token.IMPORT { - n.Specs = append(n.Specs, &ast.ImportSpec{ - Path: &ast.BasicLit{ - Kind: token.STRING, - Value: strconv.Quote("sample/app/server"), - }, - }) - } - case *ast.CallExpr: - switch fun := n.Fun.(type) { - case *ast.SelectorExpr: - switch fun.Sel.Name { - case "New": - n.Args = append(n.Args, &ast.CallExpr{ - Fun: &ast.SelectorExpr{ - X: ast.NewIdent("grapiserver"), - Sel: ast.NewIdent("WithGatewayAddr"), - }, - Args: []ast.Expr{ - &ast.BasicLit{ - Kind: token.STRING, - Value: strconv.Quote("tcp"), - }, - &ast.BasicLit{ - Kind: token.STRING, - Value: strconv.Quote(fmt.Sprintf(":%d", port)), - }, - }, - }) - case "WithServers": - n.Args = append(n.Args, &ast.CallExpr{ - Fun: &ast.SelectorExpr{ - X: ast.NewIdent("server"), - Sel: ast.NewIdent("NewBookServiceServer"), - }, - }) - } - } - } - return v - }, - }, f) - - buf := new(bytes.Buffer) - err = format.Node(buf, token.NewFileSet(), f) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - err = afero.WriteFile(fs, filepath.Join(rootPath, "app", "run.go"), buf.Bytes(), 0755) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } -} - -func updateServerImpl(t *testing.T, fs afero.Fs, rootPath string) { - data, err := afero.ReadFile(fs, filepath.Join(rootPath, "app", "server", "book_server.go")) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - - fset := token.NewFileSet() - f, err := parser.ParseFile(fset, "", data, parser.DeclarationErrors) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - - ast.Walk(&visitor{ - VisitFunc: func(v ast.Visitor, n ast.Node) ast.Visitor { - switch n := n.(type) { - case *ast.GenDecl: - if n.Tok == token.IMPORT { - n.Specs = []ast.Spec{ - &ast.ImportSpec{ - Path: &ast.BasicLit{ - Kind: token.STRING, - Value: strconv.Quote("context"), - }, - }, - &ast.ImportSpec{ - Path: &ast.BasicLit{ - Kind: token.STRING, - Value: strconv.Quote("github.com/izumin5210/grapi/pkg/grapiserver"), - }, - }, - &ast.ImportSpec{ - Name: &ast.Ident{Name: "api_pb"}, - Path: &ast.BasicLit{ - Kind: token.STRING, - Value: strconv.Quote("sample/api"), - }, - }, - } - } - case *ast.FuncDecl: - if n.Name.Name == "ListBooks" { - n.Body.List = []ast.Stmt{ - &ast.ReturnStmt{ - Results: []ast.Expr{ - &ast.UnaryExpr{ - X: &ast.CompositeLit{ - Type: &ast.SelectorExpr{ - X: ast.NewIdent("api_pb"), - Sel: ast.NewIdent("ListBooksResponse"), - }, - }, - Op: token.AND, - }, - &ast.Ident{Name: "nil"}, - }, - }, - } - } - } - return v - }, - }, f) - - buf := new(bytes.Buffer) - err = format.Node(buf, token.NewFileSet(), f) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } - err = afero.WriteFile(fs, filepath.Join(rootPath, "app", "server", "book_server.go"), buf.Bytes(), 0755) - if err != nil { - t.Fatalf("Unexpected error: %v", err) - } -} diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_CommandGenerator-Generate-cmd-foo-run.go b/cmd/grapi-gen-command/.snapshots/TestCommand-simple-generate-cmd-foo-run.go similarity index 100% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_CommandGenerator-Generate-cmd-foo-run.go rename to cmd/grapi-gen-command/.snapshots/TestCommand-simple-generate-cmd-foo-run.go diff --git a/cmd/grapi-gen-command/main.go b/cmd/grapi-gen-command/main.go new file mode 100644 index 00000000..2a1d7a2a --- /dev/null +++ b/cmd/grapi-gen-command/main.go @@ -0,0 +1,45 @@ +package main + +import ( + "github.com/spf13/cobra" + + _ "github.com/izumin5210/grapi/cmd/grapi-gen-command/template" + "github.com/izumin5210/grapi/pkg/gencmd" +) + +func main() { + buildCommand().MustExecute() +} + +func buildCommand(opts ...gencmd.Option) gencmd.Executor { + return gencmd.New( + "command", + newGenerateCommand(), + newDestroyCommand(), + opts..., + ) +} + +func newGenerateCommand() *gencmd.Command { + return &gencmd.Command{ + Use: "generate NAME", + Short: "Generate a new command", + Args: cobra.ExactArgs(1), + ShouldInsideApp: true, + BuildParams: func(c *gencmd.Command, args []string) (interface{}, error) { + return map[string]string{"name": args[0]}, nil + }, + } +} + +func newDestroyCommand() *gencmd.Command { + return &gencmd.Command{ + Use: "destroy NAME", + Short: "Destroy a existing command", + Args: cobra.ExactArgs(1), + ShouldInsideApp: true, + BuildParams: func(c *gencmd.Command, args []string) (interface{}, error) { + return map[string]string{"name": args[0]}, nil + }, + } +} diff --git a/cmd/grapi-gen-command/main_test.go b/cmd/grapi-gen-command/main_test.go new file mode 100644 index 00000000..c9884ce8 --- /dev/null +++ b/cmd/grapi-gen-command/main_test.go @@ -0,0 +1,93 @@ +package main + +import ( + "testing" + + "github.com/bradleyjkemp/cupaloy/v2" + "github.com/izumin5210/clig/pkg/clib" + "github.com/izumin5210/grapi/pkg/cli" + "github.com/izumin5210/grapi/pkg/gencmd" + gencmdtesting "github.com/izumin5210/grapi/pkg/gencmd/testing" + "github.com/izumin5210/grapi/pkg/grapicmd" + "github.com/spf13/afero" +) + +func TestCommand(t *testing.T) { + cases := []struct { + test string + args []string + files []string + }{ + { + test: "simple", + args: []string{"foo"}, + files: []string{"cmd/foo/run.go"}, + }, + } + + rootDir := cli.RootDir{clib.Path("/home/src/testapp")} + + createGenApp := func(cmd *gencmd.Command) (*gencmd.App, error) { + return gencmdtesting.NewTestApp(cmd, cli.NopUI) + } + createCmd := func(t *testing.T, fs afero.Fs) gencmd.Executor { + ctx := &grapicmd.Ctx{ + FS: fs, + RootDir: rootDir, + } + return buildCommand(gencmd.WithGrapiCtx(ctx), gencmd.WithCreateAppFunc(createGenApp)) + } + + for _, tc := range cases { + t.Run(tc.test, func(t *testing.T) { + fs := afero.NewMemMapFs() + afero.WriteFile(fs, rootDir.Join("grapi.toml").String(), []byte{}, 0755) + + t.Run("generate", func(t *testing.T) { + cmd := createCmd(t, fs) + cmd.Command().SetArgs(append([]string{"generate"}, tc.args...)) + err := cmd.Execute() + + if err != nil { + t.Errorf("returned an error: %+v", err) + } + + for _, file := range tc.files { + t.Run(file, func(t *testing.T) { + data, err := afero.ReadFile(fs, rootDir.Join(file).String()) + + if err != nil { + t.Errorf("returned an error: %v", err) + } + + cupaloy.SnapshotT(t, string(data)) + }) + } + }) + + t.Run("destroy", func(t *testing.T) { + cmd := createCmd(t, fs) + cmd.Command().SetArgs(append([]string{"destroy"}, tc.args...)) + err := cmd.Execute() + + if err != nil { + t.Errorf("returned an error: %+v", err) + } + + for _, file := range tc.files { + t.Run(file, func(t *testing.T) { + ok, err := afero.Exists(fs, rootDir.Join(file).String()) + + if err != nil { + t.Errorf("Exists(fs, %q) returned an error: %v", file, err) + } + + if ok { + t.Errorf("%q should not exist", file) + } + }) + } + }) + }) + } +} diff --git a/pkg/grapicmd/internal/module/generator/template/init/api/protos/.keep.tmpl b/cmd/grapi-gen-command/template/.gitignore similarity index 100% rename from pkg/grapicmd/internal/module/generator/template/init/api/protos/.keep.tmpl rename to cmd/grapi-gen-command/template/.gitignore diff --git a/pkg/grapicmd/internal/module/generator/template/command/cmd/{{ .name }}/run.go.tmpl b/cmd/grapi-gen-command/template/_data/cmd/{{ .name }}/run.go.tmpl similarity index 100% rename from pkg/grapicmd/internal/module/generator/template/command/cmd/{{ .name }}/run.go.tmpl rename to cmd/grapi-gen-command/template/_data/cmd/{{ .name }}/run.go.tmpl diff --git a/cmd/grapi-gen-command/template/gen.go b/cmd/grapi-gen-command/template/gen.go new file mode 100644 index 00000000..ba18632b --- /dev/null +++ b/cmd/grapi-gen-command/template/gen.go @@ -0,0 +1,3 @@ +//go:generate statik -src ./_data -dest .. -p template -f -m + +package template diff --git a/cmd/grapi-gen-command/template/statik.go b/cmd/grapi-gen-command/template/statik.go new file mode 100644 index 00000000..68e3dd19 --- /dev/null +++ b/cmd/grapi-gen-command/template/statik.go @@ -0,0 +1,13 @@ +// Code generated by statik. DO NOT EDIT. + +// Package statik contains static assets. +package template + +import ( + "github.com/rakyll/statik/fs" +) + +func init() { + data := "PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1b\x00 \x00cmd/{{ .name }}/run.go.tmplUT\x05\x00\x01\x80Cm8,\xcaA\n\xc20\x10\x05\xd0u\xfe)\xc6Y%\x9b\xe2%\\\xb8\xf3\n\xa142\xd4L\xcad\x82\x82\xf4\xeeb\xe9\xf2\xc1\xdb\xf2\xbc\xe6\xe7B5\x8b\x02R\xb7fN\x11\x81KuF\xe0\xd6\x19 (C\xe7\xe3\xc4D_\x84\xd6\xa7\xdbG<\xda\xd0\x98\x12\xf63\x1c$Q\xff\x9fR}z\x98\xa8\xbf4\xf2\xdd\xe9\xddl\xed\x17N\x08\xb6\xf80\xa5+v\xfc\x02\x00\x00\xff\xffPK\x07\x08\xe5<\xbb\x9ao\x00\x00\x00\x7f\x00\x00\x00PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(\xe5<\xbb\x9ao\x00\x00\x00\x7f\x00\x00\x00\x1b\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x00\x00\x00\x00cmd/{{ .name }}/run.go.tmplUT\x05\x00\x01\x80Cm8PK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x00R\x00\x00\x00\xc1\x00\x00\x00\x00\x00" + fs.Register(data) +} diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz-Generate-api-protos-foo-bar_baz.proto b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-kebab-case_name-generate-api-protos-foo-bar_baz.proto similarity index 69% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz-Generate-api-protos-foo-bar_baz.proto rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-kebab-case_name-generate-api-protos-foo-bar_baz.proto index d19d50bd..2590d59c 100644 --- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz-Generate-api-protos-foo-bar_baz.proto +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-kebab-case_name-generate-api-protos-foo-bar_baz.proto @@ -1,7 +1,10 @@ syntax = "proto3"; -option go_package = "foo_pb"; + package testapp.api.foo; +option go_package = "testapp/api/foo;foo_pb"; + + import "google/api/annotations.proto"; service BarBazService { diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz-Generate-app-server-foo-bar_baz_server.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-kebab-case_name-generate-app-server-foo-bar_baz_server.go similarity index 65% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz-Generate-app-server-foo-bar_baz_server.go rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-kebab-case_name-generate-app-server-foo-bar_baz_server.go index 6b5bba6d..fa385d3b 100644 --- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz-Generate-app-server-foo-bar_baz_server.go +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-kebab-case_name-generate-app-server-foo-bar_baz_server.go @@ -10,11 +10,14 @@ import ( foo_pb "testapp/api/foo" ) -// NewBarBazServiceServer creates a new BarBazServiceServer instance. -func NewBarBazServiceServer() interface { +// BarBazServiceServer is a composite interface of foo_pb.BarBazServiceServer and grapiserver.Server. +type BarBazServiceServer interface { foo_pb.BarBazServiceServer grapiserver.Server -} { +} + +// NewBarBazServiceServer creates a new BarBazServiceServer instance. +func NewBarBazServiceServer() BarBazServiceServer { return &barBazServiceServerImpl{} } diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar_baz-Generate-app-server-foo-bar_baz_server_register_funcs.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-kebab-case_name-generate-app-server-foo-bar_baz_server_register_funcs.go similarity index 90% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar_baz-Generate-app-server-foo-bar_baz_server_register_funcs.go rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-kebab-case_name-generate-app-server-foo-bar_baz_server_register_funcs.go index 9bd7e8ae..1c820532 100644 --- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar_baz-Generate-app-server-foo-bar_baz_server_register_funcs.go +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-kebab-case_name-generate-app-server-foo-bar_baz_server_register_funcs.go @@ -1,3 +1,5 @@ +// Code generated by github.com/izumin5210/grapi. DO NOT EDIT. + package foo import ( diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-Generate-app-server-foo-bar_server_test.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-kebab-case_name-generate-app-server-foo-bar_baz_server_test.go similarity index 100% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-Generate-app-server-foo-bar_server_test.go rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-kebab-case_name-generate-app-server-foo-bar_baz_server_test.go diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-Generate-api-protos-foo-bar.proto b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested-generate-api-protos-foo-bar.proto similarity index 68% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-Generate-api-protos-foo-bar.proto rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested-generate-api-protos-foo-bar.proto index 80a6b442..230cdcd3 100644 --- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-Generate-api-protos-foo-bar.proto +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested-generate-api-protos-foo-bar.proto @@ -1,7 +1,10 @@ syntax = "proto3"; -option go_package = "foo_pb"; + package testapp.api.foo; +option go_package = "testapp/api/foo;foo_pb"; + + import "google/api/annotations.proto"; service BarService { diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-Generate-app-server-foo-bar_server.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested-generate-app-server-foo-bar_server.go similarity index 66% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-Generate-app-server-foo-bar_server.go rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested-generate-app-server-foo-bar_server.go index b35236b8..68b67795 100644 --- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-Generate-app-server-foo-bar_server.go +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested-generate-app-server-foo-bar_server.go @@ -10,11 +10,14 @@ import ( foo_pb "testapp/api/foo" ) -// NewBarServiceServer creates a new BarServiceServer instance. -func NewBarServiceServer() interface { +// BarServiceServer is a composite interface of foo_pb.BarServiceServer and grapiserver.Server. +type BarServiceServer interface { foo_pb.BarServiceServer grapiserver.Server -} { +} + +// NewBarServiceServer creates a new BarServiceServer instance. +func NewBarServiceServer() BarServiceServer { return &barServiceServerImpl{} } diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-Generate-app-server-foo-bar_server_register_funcs.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested-generate-app-server-foo-bar_server_register_funcs.go similarity index 90% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-Generate-app-server-foo-bar_server_register_funcs.go rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested-generate-app-server-foo-bar_server_register_funcs.go index 6d73d59d..27421437 100644 --- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-Generate-app-server-foo-bar_server_register_funcs.go +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested-generate-app-server-foo-bar_server_register_funcs.go @@ -1,3 +1,5 @@ +// Code generated by github.com/izumin5210/grapi. DO NOT EDIT. + package foo import ( diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz-Generate-app-server-foo-bar_baz_server_test.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested-generate-app-server-foo-bar_server_test.go similarity index 100% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz-Generate-app-server-foo-bar_baz_server_test.go rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested-generate-app-server-foo-bar_server_test.go diff --git a/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-api-protos-foo-bar.proto b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-api-protos-foo-bar.proto new file mode 100644 index 00000000..2775531f --- /dev/null +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-api-protos-foo-bar.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +package testcompany.testapp.foo; + +option go_package = "testapp/api/foo;foo_pb"; + + +import "google/api/annotations.proto"; + +service BarService { +} + diff --git a/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-app-server-foo-bar_server.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-app-server-foo-bar_server.go new file mode 100644 index 00000000..68b67795 --- /dev/null +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-app-server-foo-bar_server.go @@ -0,0 +1,26 @@ +package foo + +import ( + "context" + + "github.com/izumin5210/grapi/pkg/grapiserver" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + foo_pb "testapp/api/foo" +) + +// BarServiceServer is a composite interface of foo_pb.BarServiceServer and grapiserver.Server. +type BarServiceServer interface { + foo_pb.BarServiceServer + grapiserver.Server +} + +// NewBarServiceServer creates a new BarServiceServer instance. +func NewBarServiceServer() BarServiceServer { + return &barServiceServerImpl{} +} + +type barServiceServerImpl struct { +} + diff --git a/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-app-server-foo-bar_server_register_funcs.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-app-server-foo-bar_server_register_funcs.go new file mode 100644 index 00000000..27421437 --- /dev/null +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-app-server-foo-bar_server_register_funcs.go @@ -0,0 +1,23 @@ +// Code generated by github.com/izumin5210/grapi. DO NOT EDIT. + +package foo + +import ( + "context" + + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "google.golang.org/grpc" + + foo_pb "testapp/api/foo" +) + +// RegisterWithServer implements grapiserver.Server.RegisterWithServer. +func (s *barServiceServerImpl) RegisterWithServer(grpcSvr *grpc.Server) { + foo_pb.RegisterBarServiceServer(grpcSvr, s) +} + +// RegisterWithHandler implements grapiserver.Server.RegisterWithHandler. +func (s *barServiceServerImpl) RegisterWithHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return foo_pb.RegisterBarServiceHandler(ctx, mux, conn) +} + diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar_baz-Generate-app-server-foo-bar_baz_server_test.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-app-server-foo-bar_server_test.go similarity index 100% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar_baz-Generate-app-server-foo-bar_baz_server_test.go rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-app-server-foo-bar_server_test.go diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-book-Scaffold-api-protos-book.proto b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-api-protos-book.proto similarity index 96% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-book-Scaffold-api-protos-book.proto rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-api-protos-book.proto index 2f046f00..767d59bc 100644 --- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-book-Scaffold-api-protos-book.proto +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-api-protos-book.proto @@ -1,7 +1,10 @@ syntax = "proto3"; -option go_package = "api_pb"; + package testapp.api; +option go_package = "testapp/api;api_pb"; + + import "google/api/annotations.proto"; import "google/protobuf/empty.proto"; diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-Generate-api-protos-foo.proto b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-api-protos-foo.proto similarity index 70% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-Generate-api-protos-foo.proto rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-api-protos-foo.proto index dedcc441..e94131cc 100644 --- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-Generate-api-protos-foo.proto +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-api-protos-foo.proto @@ -1,7 +1,10 @@ syntax = "proto3"; -option go_package = "api_pb"; + package testapp.api; +option go_package = "testapp/api;api_pb"; + + import "google/api/annotations.proto"; service FooService { diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-book-Scaffold-app-server-book_server.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-app-server-book_server.go similarity index 89% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-book-Scaffold-app-server-book_server.go rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-app-server-book_server.go index 1080bbe6..09152b99 100644 --- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-book-Scaffold-app-server-book_server.go +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-app-server-book_server.go @@ -11,11 +11,14 @@ import ( api_pb "testapp/api" ) -// NewBookServiceServer creates a new BookServiceServer instance. -func NewBookServiceServer() interface { +// BookServiceServer is a composite interface of api_pb.BookServiceServer and grapiserver.Server. +type BookServiceServer interface { api_pb.BookServiceServer grapiserver.Server -} { +} + +// NewBookServiceServer creates a new BookServiceServer instance. +func NewBookServiceServer() BookServiceServer { return &bookServiceServerImpl{} } diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-book-Scaffold-app-server-book_server_register_funcs.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-app-server-book_server_register_funcs.go similarity index 90% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-book-Scaffold-app-server-book_server_register_funcs.go rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-app-server-book_server_register_funcs.go index eb34b1e1..0c8e7832 100644 --- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-book-Scaffold-app-server-book_server_register_funcs.go +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-app-server-book_server_register_funcs.go @@ -1,3 +1,5 @@ +// Code generated by github.com/izumin5210/grapi. DO NOT EDIT. + package server import ( diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-book-Scaffold-app-server-book_server_test.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-app-server-book_server_test.go similarity index 95% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-book-Scaffold-app-server-book_server_test.go rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-app-server-book_server_test.go index 5bda729f..2423f050 100644 --- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-book-Scaffold-app-server-book_server_test.go +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-app-server-book_server_test.go @@ -4,8 +4,6 @@ import ( "context" "testing" - "github.com/golang/protobuf/ptypes/empty" - api_pb "testapp/api" ) @@ -17,6 +15,8 @@ func Test_BookServiceServer_ListBooks(t *testing.T) { resp, err := svr.ListBooks(ctx, req) + t.SkipNow() + if err != nil { t.Errorf("returned an error %v", err) } @@ -34,6 +34,8 @@ func Test_BookServiceServer_GetBook(t *testing.T) { resp, err := svr.GetBook(ctx, req) + t.SkipNow() + if err != nil { t.Errorf("returned an error %v", err) } @@ -51,6 +53,8 @@ func Test_BookServiceServer_CreateBook(t *testing.T) { resp, err := svr.CreateBook(ctx, req) + t.SkipNow() + if err != nil { t.Errorf("returned an error %v", err) } @@ -68,6 +72,8 @@ func Test_BookServiceServer_UpdateBook(t *testing.T) { resp, err := svr.UpdateBook(ctx, req) + t.SkipNow() + if err != nil { t.Errorf("returned an error %v", err) } @@ -85,6 +91,8 @@ func Test_BookServiceServer_DeleteBook(t *testing.T) { resp, err := svr.DeleteBook(ctx, req) + t.SkipNow() + if err != nil { t.Errorf("returned an error %v", err) } diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-Generate-app-server-foo_server.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-app-server-foo_server.go similarity index 66% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-Generate-app-server-foo_server.go rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-app-server-foo_server.go index 9d028c3a..04d3cf0b 100644 --- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-Generate-app-server-foo_server.go +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-app-server-foo_server.go @@ -10,11 +10,14 @@ import ( api_pb "testapp/api" ) -// NewFooServiceServer creates a new FooServiceServer instance. -func NewFooServiceServer() interface { +// FooServiceServer is a composite interface of api_pb.FooServiceServer and grapiserver.Server. +type FooServiceServer interface { api_pb.FooServiceServer grapiserver.Server -} { +} + +// NewFooServiceServer creates a new FooServiceServer instance. +func NewFooServiceServer() FooServiceServer { return &fooServiceServerImpl{} } diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-Generate-app-server-foo_server_register_funcs.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-app-server-foo_server_register_funcs.go similarity index 90% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-Generate-app-server-foo_server_register_funcs.go rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-app-server-foo_server_register_funcs.go index 78c52d0d..7b576996 100644 --- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-Generate-app-server-foo_server_register_funcs.go +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-app-server-foo_server_register_funcs.go @@ -1,3 +1,5 @@ +// Code generated by github.com/izumin5210/grapi. DO NOT EDIT. + package server import ( diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-corge-Generate-pkg-foo-server-corge_server_test.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-app-server-foo_server_test.go similarity index 100% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-corge-Generate-pkg-foo-server-corge_server_test.go rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-simple-generate-app-server-foo_server_test.go diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-book#01-Generate_without_test-api-protos-book.proto b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-skip_tests-generate-api-protos-book.proto similarity index 70% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-book#01-Generate_without_test-api-protos-book.proto rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-skip_tests-generate-api-protos-book.proto index 28f4ec58..5fe20ee3 100644 --- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-book#01-Generate_without_test-api-protos-book.proto +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-skip_tests-generate-api-protos-book.proto @@ -1,7 +1,10 @@ syntax = "proto3"; -option go_package = "api_pb"; + package testapp.api; +option go_package = "testapp/api;api_pb"; + + import "google/api/annotations.proto"; service BookService { diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-book#01-Generate_without_test-app-server-book_server.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-skip_tests-generate-app-server-book_server.go similarity index 66% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-book#01-Generate_without_test-app-server-book_server.go rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-skip_tests-generate-app-server-book_server.go index 9bc99a11..9c739963 100644 --- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-book#01-Generate_without_test-app-server-book_server.go +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-skip_tests-generate-app-server-book_server.go @@ -10,11 +10,14 @@ import ( api_pb "testapp/api" ) -// NewBookServiceServer creates a new BookServiceServer instance. -func NewBookServiceServer() interface { +// BookServiceServer is a composite interface of api_pb.BookServiceServer and grapiserver.Server. +type BookServiceServer interface { api_pb.BookServiceServer grapiserver.Server -} { +} + +// NewBookServiceServer creates a new BookServiceServer instance. +func NewBookServiceServer() BookServiceServer { return &bookServiceServerImpl{} } diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-book#01-Generate_without_test-app-server-book_server_register_funcs.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-skip_tests-generate-app-server-book_server_register_funcs.go similarity index 90% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-book#01-Generate_without_test-app-server-book_server_register_funcs.go rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-skip_tests-generate-app-server-book_server_register_funcs.go index eb34b1e1..0c8e7832 100644 --- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-book#01-Generate_without_test-app-server-book_server_register_funcs.go +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-skip_tests-generate-app-server-book_server_register_funcs.go @@ -1,3 +1,5 @@ +// Code generated by github.com/izumin5210/grapi. DO NOT EDIT. + package server import ( diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar_baz-Generate-api-protos-foo-bar_baz.proto b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-snake_case_name-generate-api-protos-foo-bar_baz.proto similarity index 69% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar_baz-Generate-api-protos-foo-bar_baz.proto rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-snake_case_name-generate-api-protos-foo-bar_baz.proto index d19d50bd..2590d59c 100644 --- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar_baz-Generate-api-protos-foo-bar_baz.proto +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-snake_case_name-generate-api-protos-foo-bar_baz.proto @@ -1,7 +1,10 @@ syntax = "proto3"; -option go_package = "foo_pb"; + package testapp.api.foo; +option go_package = "testapp/api/foo;foo_pb"; + + import "google/api/annotations.proto"; service BarBazService { diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar_baz-Generate-app-server-foo-bar_baz_server.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-snake_case_name-generate-app-server-foo-bar_baz_server.go similarity index 65% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar_baz-Generate-app-server-foo-bar_baz_server.go rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-snake_case_name-generate-app-server-foo-bar_baz_server.go index 6b5bba6d..fa385d3b 100644 --- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar_baz-Generate-app-server-foo-bar_baz_server.go +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-snake_case_name-generate-app-server-foo-bar_baz_server.go @@ -10,11 +10,14 @@ import ( foo_pb "testapp/api/foo" ) -// NewBarBazServiceServer creates a new BarBazServiceServer instance. -func NewBarBazServiceServer() interface { +// BarBazServiceServer is a composite interface of foo_pb.BarBazServiceServer and grapiserver.Server. +type BarBazServiceServer interface { foo_pb.BarBazServiceServer grapiserver.Server -} { +} + +// NewBarBazServiceServer creates a new BarBazServiceServer instance. +func NewBarBazServiceServer() BarBazServiceServer { return &barBazServiceServerImpl{} } diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz-Generate-app-server-foo-bar_baz_server_register_funcs.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-snake_case_name-generate-app-server-foo-bar_baz_server_register_funcs.go similarity index 90% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz-Generate-app-server-foo-bar_baz_server_register_funcs.go rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-snake_case_name-generate-app-server-foo-bar_baz_server_register_funcs.go index 9bd7e8ae..1c820532 100644 --- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz-Generate-app-server-foo-bar_baz_server_register_funcs.go +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-snake_case_name-generate-app-server-foo-bar_baz_server_register_funcs.go @@ -1,3 +1,5 @@ +// Code generated by github.com/izumin5210/grapi. DO NOT EDIT. + package foo import ( diff --git a/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-snake_case_name-generate-app-server-foo-bar_baz_server_test.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-snake_case_name-generate-app-server-foo-bar_baz_server_test.go new file mode 100644 index 00000000..d1b1429c --- /dev/null +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-snake_case_name-generate-app-server-foo-bar_baz_server_test.go @@ -0,0 +1,2 @@ +package foo + diff --git a/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_package-generate-api-protos-foo.proto b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_package-generate-api-protos-foo.proto new file mode 100644 index 00000000..447b4721 --- /dev/null +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_package-generate-api-protos-foo.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +package testcompany.testapp; + +option go_package = "testapp/api;api_pb"; + + +import "google/api/annotations.proto"; + +service FooService { +} + diff --git a/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_package-generate-app-server-foo_server.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_package-generate-app-server-foo_server.go new file mode 100644 index 00000000..04d3cf0b --- /dev/null +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_package-generate-app-server-foo_server.go @@ -0,0 +1,26 @@ +package server + +import ( + "context" + + "github.com/izumin5210/grapi/pkg/grapiserver" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + api_pb "testapp/api" +) + +// FooServiceServer is a composite interface of api_pb.FooServiceServer and grapiserver.Server. +type FooServiceServer interface { + api_pb.FooServiceServer + grapiserver.Server +} + +// NewFooServiceServer creates a new FooServiceServer instance. +func NewFooServiceServer() FooServiceServer { + return &fooServiceServerImpl{} +} + +type fooServiceServerImpl struct { +} + diff --git a/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_package-generate-app-server-foo_server_register_funcs.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_package-generate-app-server-foo_server_register_funcs.go new file mode 100644 index 00000000..7b576996 --- /dev/null +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_package-generate-app-server-foo_server_register_funcs.go @@ -0,0 +1,23 @@ +// Code generated by github.com/izumin5210/grapi. DO NOT EDIT. + +package server + +import ( + "context" + + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "google.golang.org/grpc" + + api_pb "testapp/api" +) + +// RegisterWithServer implements grapiserver.Server.RegisterWithServer. +func (s *fooServiceServerImpl) RegisterWithServer(grpcSvr *grpc.Server) { + api_pb.RegisterFooServiceServer(grpcSvr, s) +} + +// RegisterWithHandler implements grapiserver.Server.RegisterWithHandler. +func (s *fooServiceServerImpl) RegisterWithHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return api_pb.RegisterFooServiceHandler(ctx, mux, conn) +} + diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-Generate-app-server-foo_server_test.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_package-generate-app-server-foo_server_test.go similarity index 100% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-Generate-app-server-foo_server_test.go rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_package-generate-app-server-foo_server_test.go diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-qux-Generate-app-server-qux_server.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_dir-generate-app-server-qux_server.go similarity index 66% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-qux-Generate-app-server-qux_server.go rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_dir-generate-app-server-qux_server.go index 891963dd..e0d87ee0 100644 --- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-qux-Generate-app-server-qux_server.go +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_dir-generate-app-server-qux_server.go @@ -10,11 +10,14 @@ import ( api_pb "testapp/api" ) -// NewQuxServiceServer creates a new QuxServiceServer instance. -func NewQuxServiceServer() interface { +// QuxServiceServer is a composite interface of api_pb.QuxServiceServer and grapiserver.Server. +type QuxServiceServer interface { api_pb.QuxServiceServer grapiserver.Server -} { +} + +// NewQuxServiceServer creates a new QuxServiceServer instance. +func NewQuxServiceServer() QuxServiceServer { return &quxServiceServerImpl{} } diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-qux-Generate-app-server-qux_server_register_funcs.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_dir-generate-app-server-qux_server_register_funcs.go similarity index 90% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-qux-Generate-app-server-qux_server_register_funcs.go rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_dir-generate-app-server-qux_server_register_funcs.go index 1546a4c3..a23d82ec 100644 --- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-qux-Generate-app-server-qux_server_register_funcs.go +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_dir-generate-app-server-qux_server_register_funcs.go @@ -1,3 +1,5 @@ +// Code generated by github.com/izumin5210/grapi. DO NOT EDIT. + package server import ( diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-quux-Generate-app-server-quux_server_test.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_dir-generate-app-server-qux_server_test.go similarity index 100% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-quux-Generate-app-server-quux_server_test.go rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_dir-generate-app-server-qux_server_test.go diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-qux-Generate-pkg-foo-protos-qux.proto b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_dir-generate-pkg-foo-protos-qux.proto similarity index 70% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-qux-Generate-pkg-foo-protos-qux.proto rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_dir-generate-pkg-foo-protos-qux.proto index a8c3c8a0..3ea614e4 100644 --- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-qux-Generate-pkg-foo-protos-qux.proto +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_dir-generate-pkg-foo-protos-qux.proto @@ -1,7 +1,10 @@ syntax = "proto3"; -option go_package = "api_pb"; + package testapp.api; +option go_package = "testapp/api;api_pb"; + + import "google/api/annotations.proto"; service QuxService { diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-quux-Generate-api-protos-quux.proto b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_out_dir-generate-api-protos-quux.proto similarity index 69% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-quux-Generate-api-protos-quux.proto rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_out_dir-generate-api-protos-quux.proto index 4f591316..e97ef74d 100644 --- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-quux-Generate-api-protos-quux.proto +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_out_dir-generate-api-protos-quux.proto @@ -1,7 +1,10 @@ syntax = "proto3"; -option go_package = "out_pb"; + package testapp.api.out; +option go_package = "testapp/api/out;out_pb"; + + import "google/api/annotations.proto"; service QuuxService { diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-quux-Generate-app-server-quux_server.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_out_dir-generate-app-server-quux_server.go similarity index 66% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-quux-Generate-app-server-quux_server.go rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_out_dir-generate-app-server-quux_server.go index 303df91f..2aa3e521 100644 --- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-quux-Generate-app-server-quux_server.go +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_out_dir-generate-app-server-quux_server.go @@ -10,11 +10,14 @@ import ( out_pb "testapp/api/out" ) -// NewQuuxServiceServer creates a new QuuxServiceServer instance. -func NewQuuxServiceServer() interface { +// QuuxServiceServer is a composite interface of out_pb.QuuxServiceServer and grapiserver.Server. +type QuuxServiceServer interface { out_pb.QuuxServiceServer grapiserver.Server -} { +} + +// NewQuuxServiceServer creates a new QuuxServiceServer instance. +func NewQuuxServiceServer() QuuxServiceServer { return &quuxServiceServerImpl{} } diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-quux-Generate-app-server-quux_server_register_funcs.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_out_dir-generate-app-server-quux_server_register_funcs.go similarity index 90% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-quux-Generate-app-server-quux_server_register_funcs.go rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_out_dir-generate-app-server-quux_server_register_funcs.go index 61c22589..26cfa4ea 100644 --- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-quux-Generate-app-server-quux_server_register_funcs.go +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_out_dir-generate-app-server-quux_server_register_funcs.go @@ -1,3 +1,5 @@ +// Code generated by github.com/izumin5210/grapi. DO NOT EDIT. + package server import ( diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-qux-Generate-app-server-qux_server_test.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_out_dir-generate-app-server-quux_server_test.go similarity index 100% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-qux-Generate-app-server-qux_server_test.go rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_proto_out_dir-generate-app-server-quux_server_test.go diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-library-Scaffold-api-protos-library.proto b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_resource_name-generate-api-protos-library.proto similarity index 96% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-library-Scaffold-api-protos-library.proto rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_resource_name-generate-api-protos-library.proto index ca2a1c90..3b82cfe6 100644 --- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-library-Scaffold-api-protos-library.proto +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_resource_name-generate-api-protos-library.proto @@ -1,7 +1,10 @@ syntax = "proto3"; -option go_package = "api_pb"; + package testapp.api; +option go_package = "testapp/api;api_pb"; + + import "google/api/annotations.proto"; import "google/protobuf/empty.proto"; diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-library-Scaffold-app-server-library_server.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_resource_name-generate-app-server-library_server.go similarity index 88% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-library-Scaffold-app-server-library_server.go rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_resource_name-generate-app-server-library_server.go index 59795854..ff6de0c2 100644 --- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-library-Scaffold-app-server-library_server.go +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_resource_name-generate-app-server-library_server.go @@ -11,11 +11,14 @@ import ( api_pb "testapp/api" ) -// NewLibraryServiceServer creates a new LibraryServiceServer instance. -func NewLibraryServiceServer() interface { +// LibraryServiceServer is a composite interface of api_pb.LibraryServiceServer and grapiserver.Server. +type LibraryServiceServer interface { api_pb.LibraryServiceServer grapiserver.Server -} { +} + +// NewLibraryServiceServer creates a new LibraryServiceServer instance. +func NewLibraryServiceServer() LibraryServiceServer { return &libraryServiceServerImpl{} } diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-library-Scaffold-app-server-library_server_register_funcs.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_resource_name-generate-app-server-library_server_register_funcs.go similarity index 90% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-library-Scaffold-app-server-library_server_register_funcs.go rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_resource_name-generate-app-server-library_server_register_funcs.go index 7e654933..71a96737 100644 --- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-library-Scaffold-app-server-library_server_register_funcs.go +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_resource_name-generate-app-server-library_server_register_funcs.go @@ -1,3 +1,5 @@ +// Code generated by github.com/izumin5210/grapi. DO NOT EDIT. + package server import ( diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-library-Scaffold-app-server-library_server_test.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_resource_name-generate-app-server-library_server_test.go similarity index 96% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-library-Scaffold-app-server-library_server_test.go rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_resource_name-generate-app-server-library_server_test.go index ad4c2505..1cd46a69 100644 --- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-library-Scaffold-app-server-library_server_test.go +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_resource_name-generate-app-server-library_server_test.go @@ -4,8 +4,6 @@ import ( "context" "testing" - "github.com/golang/protobuf/ptypes/empty" - api_pb "testapp/api" ) @@ -17,6 +15,8 @@ func Test_LibraryServiceServer_ListBooks(t *testing.T) { resp, err := svr.ListBooks(ctx, req) + t.SkipNow() + if err != nil { t.Errorf("returned an error %v", err) } @@ -34,6 +34,8 @@ func Test_LibraryServiceServer_GetBook(t *testing.T) { resp, err := svr.GetBook(ctx, req) + t.SkipNow() + if err != nil { t.Errorf("returned an error %v", err) } @@ -51,6 +53,8 @@ func Test_LibraryServiceServer_CreateBook(t *testing.T) { resp, err := svr.CreateBook(ctx, req) + t.SkipNow() + if err != nil { t.Errorf("returned an error %v", err) } @@ -68,6 +72,8 @@ func Test_LibraryServiceServer_UpdateBook(t *testing.T) { resp, err := svr.UpdateBook(ctx, req) + t.SkipNow() + if err != nil { t.Errorf("returned an error %v", err) } @@ -85,6 +91,8 @@ func Test_LibraryServiceServer_DeleteBook(t *testing.T) { resp, err := svr.DeleteBook(ctx, req) + t.SkipNow() + if err != nil { t.Errorf("returned an error %v", err) } diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-corge-Generate-api-protos-corge.proto b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_server_dir-generate-api-protos-corge.proto similarity index 70% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-corge-Generate-api-protos-corge.proto rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_server_dir-generate-api-protos-corge.proto index ee56d372..ee9ce2a1 100644 --- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-corge-Generate-api-protos-corge.proto +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_server_dir-generate-api-protos-corge.proto @@ -1,7 +1,10 @@ syntax = "proto3"; -option go_package = "api_pb"; + package testapp.api; +option go_package = "testapp/api;api_pb"; + + import "google/api/annotations.proto"; service CorgeService { diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-corge-Generate-pkg-foo-server-corge_server.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_server_dir-generate-pkg-foo-server-corge_server.go similarity index 66% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-corge-Generate-pkg-foo-server-corge_server.go rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_server_dir-generate-pkg-foo-server-corge_server.go index fffc343f..13334764 100644 --- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-corge-Generate-pkg-foo-server-corge_server.go +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_server_dir-generate-pkg-foo-server-corge_server.go @@ -10,11 +10,14 @@ import ( api_pb "testapp/api" ) -// NewCorgeServiceServer creates a new CorgeServiceServer instance. -func NewCorgeServiceServer() interface { +// CorgeServiceServer is a composite interface of api_pb.CorgeServiceServer and grapiserver.Server. +type CorgeServiceServer interface { api_pb.CorgeServiceServer grapiserver.Server -} { +} + +// NewCorgeServiceServer creates a new CorgeServiceServer instance. +func NewCorgeServiceServer() CorgeServiceServer { return &corgeServiceServerImpl{} } diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-corge-Generate-pkg-foo-server-corge_server_register_funcs.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_server_dir-generate-pkg-foo-server-corge_server_register_funcs.go similarity index 90% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-corge-Generate-pkg-foo-server-corge_server_register_funcs.go rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_server_dir-generate-pkg-foo-server-corge_server_register_funcs.go index 7bc5772a..93d5b5b8 100644 --- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-corge-Generate-pkg-foo-server-corge_server_register_funcs.go +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_server_dir-generate-pkg-foo-server-corge_server_register_funcs.go @@ -1,3 +1,5 @@ +// Code generated by github.com/izumin5210/grapi. DO NOT EDIT. + package server import ( diff --git a/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_server_dir-generate-pkg-foo-server-corge_server_test.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_server_dir-generate-pkg-foo-server-corge_server_test.go new file mode 100644 index 00000000..a86bc127 --- /dev/null +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-specify_server_dir-generate-pkg-foo-server-corge_server_test.go @@ -0,0 +1,2 @@ +package server + diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,rename,delete,move_move-Generate-api-protos-foo-bar_baz.proto b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_non-standard_methods-generate-api-protos-foo-bar_baz.proto similarity index 96% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,rename,delete,move_move-Generate-api-protos-foo-bar_baz.proto rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_non-standard_methods-generate-api-protos-foo-bar_baz.proto index e2141f9e..5ea823df 100644 --- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,rename,delete,move_move-Generate-api-protos-foo-bar_baz.proto +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_non-standard_methods-generate-api-protos-foo-bar_baz.proto @@ -1,7 +1,10 @@ syntax = "proto3"; -option go_package = "foo_pb"; + package testapp.api.foo; +option go_package = "testapp/api/foo;foo_pb"; + + import "google/api/annotations.proto"; import "google/protobuf/empty.proto"; diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,rename,delete,move_move-Generate-app-server-foo-bar_baz_server.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_non-standard_methods-generate-app-server-foo-bar_baz_server.go similarity index 89% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,rename,delete,move_move-Generate-app-server-foo-bar_baz_server.go rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_non-standard_methods-generate-app-server-foo-bar_baz_server.go index 34997019..0fb60647 100644 --- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,rename,delete,move_move-Generate-app-server-foo-bar_baz_server.go +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_non-standard_methods-generate-app-server-foo-bar_baz_server.go @@ -11,11 +11,14 @@ import ( foo_pb "testapp/api/foo" ) -// NewBarBazServiceServer creates a new BarBazServiceServer instance. -func NewBarBazServiceServer() interface { +// BarBazServiceServer is a composite interface of foo_pb.BarBazServiceServer and grapiserver.Server. +type BarBazServiceServer interface { foo_pb.BarBazServiceServer grapiserver.Server -} { +} + +// NewBarBazServiceServer creates a new BarBazServiceServer instance. +func NewBarBazServiceServer() BarBazServiceServer { return &barBazServiceServerImpl{} } diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,delete-Generate-app-server-foo-bar_baz_server_register_funcs.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_non-standard_methods-generate-app-server-foo-bar_baz_server_register_funcs.go similarity index 90% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,delete-Generate-app-server-foo-bar_baz_server_register_funcs.go rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_non-standard_methods-generate-app-server-foo-bar_baz_server_register_funcs.go index 9bd7e8ae..1c820532 100644 --- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,delete-Generate-app-server-foo-bar_baz_server_register_funcs.go +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_non-standard_methods-generate-app-server-foo-bar_baz_server_register_funcs.go @@ -1,3 +1,5 @@ +// Code generated by github.com/izumin5210/grapi. DO NOT EDIT. + package foo import ( diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,rename,delete,move_move-Generate-app-server-foo-bar_baz_server_test.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_non-standard_methods-generate-app-server-foo-bar_baz_server_test.go similarity index 100% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,rename,delete,move_move-Generate-app-server-foo-bar_baz_server_test.go rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_non-standard_methods-generate-app-server-foo-bar_baz_server_test.go diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,delete-Generate-api-protos-foo-bar_baz.proto b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_some_standard_methods-generate-api-protos-foo-bar_baz.proto similarity index 94% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,delete-Generate-api-protos-foo-bar_baz.proto rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_some_standard_methods-generate-api-protos-foo-bar_baz.proto index a124d7f4..44b449c3 100644 --- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,delete-Generate-api-protos-foo-bar_baz.proto +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_some_standard_methods-generate-api-protos-foo-bar_baz.proto @@ -1,7 +1,10 @@ syntax = "proto3"; -option go_package = "foo_pb"; + package testapp.api.foo; +option go_package = "testapp/api/foo;foo_pb"; + + import "google/api/annotations.proto"; import "google/protobuf/empty.proto"; diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,delete-Generate-app-server-foo-bar_baz_server.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_some_standard_methods-generate-app-server-foo-bar_baz_server.go similarity index 85% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,delete-Generate-app-server-foo-bar_baz_server.go rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_some_standard_methods-generate-app-server-foo-bar_baz_server.go index 20e98ce6..fc85ca3d 100644 --- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,delete-Generate-app-server-foo-bar_baz_server.go +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_some_standard_methods-generate-app-server-foo-bar_baz_server.go @@ -11,11 +11,14 @@ import ( foo_pb "testapp/api/foo" ) -// NewBarBazServiceServer creates a new BarBazServiceServer instance. -func NewBarBazServiceServer() interface { +// BarBazServiceServer is a composite interface of foo_pb.BarBazServiceServer and grapiserver.Server. +type BarBazServiceServer interface { foo_pb.BarBazServiceServer grapiserver.Server -} { +} + +// NewBarBazServiceServer creates a new BarBazServiceServer instance. +func NewBarBazServiceServer() BarBazServiceServer { return &barBazServiceServerImpl{} } diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,rename,delete,move_move-Generate-app-server-foo-bar_baz_server_register_funcs.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_some_standard_methods-generate-app-server-foo-bar_baz_server_register_funcs.go similarity index 90% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,rename,delete,move_move-Generate-app-server-foo-bar_baz_server_register_funcs.go rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_some_standard_methods-generate-app-server-foo-bar_baz_server_register_funcs.go index 9bd7e8ae..1c820532 100644 --- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,rename,delete,move_move-Generate-app-server-foo-bar_baz_server_register_funcs.go +++ b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_some_standard_methods-generate-app-server-foo-bar_baz_server_register_funcs.go @@ -1,3 +1,5 @@ +// Code generated by github.com/izumin5210/grapi. DO NOT EDIT. + package foo import ( diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,delete-Generate-app-server-foo-bar_baz_server_test.go b/cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_some_standard_methods-generate-app-server-foo-bar_baz_server_test.go similarity index 100% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ServiceGenerator-foo-bar-baz_with_list,create,delete-Generate-app-server-foo-bar_baz_server_test.go rename to cmd/grapi-gen-scaffold-service/.snapshots/TestRun-with_some_standard_methods-generate-app-server-foo-bar_baz_server_test.go diff --git a/cmd/grapi-gen-scaffold-service/main.go b/cmd/grapi-gen-scaffold-service/main.go new file mode 100644 index 00000000..e99776b1 --- /dev/null +++ b/cmd/grapi-gen-scaffold-service/main.go @@ -0,0 +1,83 @@ +package main + +import ( + "context" + + "github.com/pkg/errors" + "github.com/spf13/cobra" + + "github.com/izumin5210/grapi/pkg/gencmd" + "github.com/izumin5210/grapi/pkg/svcgen" +) + +func main() { + buildCommand(svcgen.NewApp).MustExecute() +} + +func buildCommand(createAppFunc svcgen.CreateAppFunc, opts ...gencmd.Option) gencmd.Executor { + return gencmd.New( + "scaffold-service", + newGenerateCommand(createAppFunc), + newDestroyCommand(createAppFunc), + opts..., + ) +} + +func newGenerateCommand(createApp svcgen.CreateAppFunc) *gencmd.Command { + var ( + skipTest bool + resName string + app *svcgen.App + ) + + cmd := &gencmd.Command{ + Use: "generate NAME [flags]", + Short: "Generate a new service with standard methods", + Args: cobra.ExactArgs(1), + ShouldInsideApp: true, + PreRun: func(c *gencmd.Command, args []string) error { + var err error + app, err = createApp(c) + return errors.WithStack(err) + }, + BuildParams: func(c *gencmd.Command, args []string) (interface{}, error) { + svcName := args[0] + methods := []string{"list", "get", "create", "update", "delete"} + + params, err := app.ParamsBuilder.Build(svcName, resName, methods) + return params, errors.WithStack(err) + }, + PostRun: func(c *gencmd.Command, args []string) error { + return errors.WithStack(app.ProtocWrapper.Exec(context.TODO())) + }, + } + + cmd.Flags().BoolVarP(&skipTest, "skip-test", "T", false, "Skip test files") + cmd.Flags().StringVar(&resName, "resource-name", "", "ResourceName to be used") + + return cmd +} + +func newDestroyCommand(createApp svcgen.CreateAppFunc) *gencmd.Command { + var ( + app *svcgen.App + ) + + cmd := &gencmd.Command{ + Use: "destroy NAME", + Short: "Destroy an existing service", + Args: cobra.ExactArgs(1), + ShouldInsideApp: true, + PreRun: func(c *gencmd.Command, args []string) error { + var err error + app, err = createApp(c) + return errors.WithStack(err) + }, + BuildParams: func(c *gencmd.Command, args []string) (interface{}, error) { + params, err := app.ParamsBuilder.Build(args[0], "", nil) + return params, errors.WithStack(err) + }, + } + + return cmd +} diff --git a/cmd/grapi-gen-scaffold-service/main_test.go b/cmd/grapi-gen-scaffold-service/main_test.go new file mode 100644 index 00000000..257629bd --- /dev/null +++ b/cmd/grapi-gen-scaffold-service/main_test.go @@ -0,0 +1,81 @@ +package main + +import ( + "context" + "testing" + + "github.com/spf13/afero" + + "github.com/izumin5210/clig/pkg/clib" + "github.com/izumin5210/grapi/pkg/cli" + "github.com/izumin5210/grapi/pkg/gencmd" + gencmdtesting "github.com/izumin5210/grapi/pkg/gencmd/testing" + "github.com/izumin5210/grapi/pkg/grapicmd" + "github.com/izumin5210/grapi/pkg/protoc" + "github.com/izumin5210/grapi/pkg/svcgen" + svcgentesting "github.com/izumin5210/grapi/pkg/svcgen/testing" +) + +func TestRun(t *testing.T) { + cases := []svcgentesting.Case{ + { + Test: "simple", + GArgs: []string{"book"}, + DArgs: []string{"book"}, + Files: []string{ + "api/protos/book.proto", + "app/server/book_server.go", + "app/server/book_server_register_funcs.go", + "app/server/book_server_test.go", + }, + }, + { + Test: "specify resource name", + GArgs: []string{"library", "--resource-name=book"}, + DArgs: []string{"library"}, + Files: []string{ + "api/protos/library.proto", + "app/server/library_server.go", + "app/server/library_server_register_funcs.go", + "app/server/library_server_test.go", + }, + }, + } + + rootDir := cli.RootDir{clib.Path("/home/src/testapp")} + + createSvcApp := func(cmd *gencmd.Command) (*svcgen.App, error) { + return svcgentesting.NewTestApp(cmd, &fakeProtocWrapper{}, cli.NopUI) + } + createGenApp := func(cmd *gencmd.Command) (*gencmd.App, error) { + return gencmdtesting.NewTestApp(cmd, cli.NopUI) + } + createCmd := func(t *testing.T, fs afero.Fs, tc svcgentesting.Case) gencmd.Executor { + ctx := &grapicmd.Ctx{ + FS: fs, + RootDir: rootDir, + Config: grapicmd.Config{ + Package: tc.PkgName, + }, + ProtocConfig: protoc.Config{ + ProtosDir: tc.ProtoDir, + OutDir: tc.ProtoOutDir, + }, + } + ctx.Config.Grapi.ServerDir = tc.ServerDir + return buildCommand(createSvcApp, gencmd.WithGrapiCtx(ctx), gencmd.WithCreateAppFunc(createGenApp)) + } + + ctx := &svcgentesting.Ctx{ + GOPATH: "/home", + RootDir: rootDir, + CreateCmd: createCmd, + Cases: cases, + } + + svcgentesting.Run(t, ctx) +} + +type fakeProtocWrapper struct{} + +func (*fakeProtocWrapper) Exec(context.Context) error { return nil } diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-kebab-case_name-generate-api-protos-foo-bar_baz.proto b/cmd/grapi-gen-service/.snapshots/TestRun-kebab-case_name-generate-api-protos-foo-bar_baz.proto new file mode 100644 index 00000000..2590d59c --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-kebab-case_name-generate-api-protos-foo-bar_baz.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +package testapp.api.foo; + +option go_package = "testapp/api/foo;foo_pb"; + + +import "google/api/annotations.proto"; + +service BarBazService { +} + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-kebab-case_name-generate-app-server-foo-bar_baz_server.go b/cmd/grapi-gen-service/.snapshots/TestRun-kebab-case_name-generate-app-server-foo-bar_baz_server.go new file mode 100644 index 00000000..fa385d3b --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-kebab-case_name-generate-app-server-foo-bar_baz_server.go @@ -0,0 +1,26 @@ +package foo + +import ( + "context" + + "github.com/izumin5210/grapi/pkg/grapiserver" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + foo_pb "testapp/api/foo" +) + +// BarBazServiceServer is a composite interface of foo_pb.BarBazServiceServer and grapiserver.Server. +type BarBazServiceServer interface { + foo_pb.BarBazServiceServer + grapiserver.Server +} + +// NewBarBazServiceServer creates a new BarBazServiceServer instance. +func NewBarBazServiceServer() BarBazServiceServer { + return &barBazServiceServerImpl{} +} + +type barBazServiceServerImpl struct { +} + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-kebab-case_name-generate-app-server-foo-bar_baz_server_register_funcs.go b/cmd/grapi-gen-service/.snapshots/TestRun-kebab-case_name-generate-app-server-foo-bar_baz_server_register_funcs.go new file mode 100644 index 00000000..1c820532 --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-kebab-case_name-generate-app-server-foo-bar_baz_server_register_funcs.go @@ -0,0 +1,23 @@ +// Code generated by github.com/izumin5210/grapi. DO NOT EDIT. + +package foo + +import ( + "context" + + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "google.golang.org/grpc" + + foo_pb "testapp/api/foo" +) + +// RegisterWithServer implements grapiserver.Server.RegisterWithServer. +func (s *barBazServiceServerImpl) RegisterWithServer(grpcSvr *grpc.Server) { + foo_pb.RegisterBarBazServiceServer(grpcSvr, s) +} + +// RegisterWithHandler implements grapiserver.Server.RegisterWithHandler. +func (s *barBazServiceServerImpl) RegisterWithHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return foo_pb.RegisterBarBazServiceHandler(ctx, mux, conn) +} + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-kebab-case_name-generate-app-server-foo-bar_baz_server_test.go b/cmd/grapi-gen-service/.snapshots/TestRun-kebab-case_name-generate-app-server-foo-bar_baz_server_test.go new file mode 100644 index 00000000..d1b1429c --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-kebab-case_name-generate-app-server-foo-bar_baz_server_test.go @@ -0,0 +1,2 @@ +package foo + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-nested-generate-api-protos-foo-bar.proto b/cmd/grapi-gen-service/.snapshots/TestRun-nested-generate-api-protos-foo-bar.proto new file mode 100644 index 00000000..230cdcd3 --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-nested-generate-api-protos-foo-bar.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +package testapp.api.foo; + +option go_package = "testapp/api/foo;foo_pb"; + + +import "google/api/annotations.proto"; + +service BarService { +} + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-nested-generate-app-server-foo-bar_server.go b/cmd/grapi-gen-service/.snapshots/TestRun-nested-generate-app-server-foo-bar_server.go new file mode 100644 index 00000000..68b67795 --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-nested-generate-app-server-foo-bar_server.go @@ -0,0 +1,26 @@ +package foo + +import ( + "context" + + "github.com/izumin5210/grapi/pkg/grapiserver" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + foo_pb "testapp/api/foo" +) + +// BarServiceServer is a composite interface of foo_pb.BarServiceServer and grapiserver.Server. +type BarServiceServer interface { + foo_pb.BarServiceServer + grapiserver.Server +} + +// NewBarServiceServer creates a new BarServiceServer instance. +func NewBarServiceServer() BarServiceServer { + return &barServiceServerImpl{} +} + +type barServiceServerImpl struct { +} + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-nested-generate-app-server-foo-bar_server_register_funcs.go b/cmd/grapi-gen-service/.snapshots/TestRun-nested-generate-app-server-foo-bar_server_register_funcs.go new file mode 100644 index 00000000..27421437 --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-nested-generate-app-server-foo-bar_server_register_funcs.go @@ -0,0 +1,23 @@ +// Code generated by github.com/izumin5210/grapi. DO NOT EDIT. + +package foo + +import ( + "context" + + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "google.golang.org/grpc" + + foo_pb "testapp/api/foo" +) + +// RegisterWithServer implements grapiserver.Server.RegisterWithServer. +func (s *barServiceServerImpl) RegisterWithServer(grpcSvr *grpc.Server) { + foo_pb.RegisterBarServiceServer(grpcSvr, s) +} + +// RegisterWithHandler implements grapiserver.Server.RegisterWithHandler. +func (s *barServiceServerImpl) RegisterWithHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return foo_pb.RegisterBarServiceHandler(ctx, mux, conn) +} + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-nested-generate-app-server-foo-bar_server_test.go b/cmd/grapi-gen-service/.snapshots/TestRun-nested-generate-app-server-foo-bar_server_test.go new file mode 100644 index 00000000..d1b1429c --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-nested-generate-app-server-foo-bar_server_test.go @@ -0,0 +1,2 @@ +package foo + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-api-protos-foo-bar.proto b/cmd/grapi-gen-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-api-protos-foo-bar.proto new file mode 100644 index 00000000..2775531f --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-api-protos-foo-bar.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +package testcompany.testapp.foo; + +option go_package = "testapp/api/foo;foo_pb"; + + +import "google/api/annotations.proto"; + +service BarService { +} + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-app-server-foo-bar_server.go b/cmd/grapi-gen-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-app-server-foo-bar_server.go new file mode 100644 index 00000000..68b67795 --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-app-server-foo-bar_server.go @@ -0,0 +1,26 @@ +package foo + +import ( + "context" + + "github.com/izumin5210/grapi/pkg/grapiserver" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + foo_pb "testapp/api/foo" +) + +// BarServiceServer is a composite interface of foo_pb.BarServiceServer and grapiserver.Server. +type BarServiceServer interface { + foo_pb.BarServiceServer + grapiserver.Server +} + +// NewBarServiceServer creates a new BarServiceServer instance. +func NewBarServiceServer() BarServiceServer { + return &barServiceServerImpl{} +} + +type barServiceServerImpl struct { +} + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-app-server-foo-bar_server_register_funcs.go b/cmd/grapi-gen-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-app-server-foo-bar_server_register_funcs.go new file mode 100644 index 00000000..27421437 --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-app-server-foo-bar_server_register_funcs.go @@ -0,0 +1,23 @@ +// Code generated by github.com/izumin5210/grapi. DO NOT EDIT. + +package foo + +import ( + "context" + + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "google.golang.org/grpc" + + foo_pb "testapp/api/foo" +) + +// RegisterWithServer implements grapiserver.Server.RegisterWithServer. +func (s *barServiceServerImpl) RegisterWithServer(grpcSvr *grpc.Server) { + foo_pb.RegisterBarServiceServer(grpcSvr, s) +} + +// RegisterWithHandler implements grapiserver.Server.RegisterWithHandler. +func (s *barServiceServerImpl) RegisterWithHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return foo_pb.RegisterBarServiceHandler(ctx, mux, conn) +} + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-app-server-foo-bar_server_test.go b/cmd/grapi-gen-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-app-server-foo-bar_server_test.go new file mode 100644 index 00000000..d1b1429c --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-nested_with_specify_pacakge-generate-app-server-foo-bar_server_test.go @@ -0,0 +1,2 @@ +package foo + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-simple-generate-api-protos-foo.proto b/cmd/grapi-gen-service/.snapshots/TestRun-simple-generate-api-protos-foo.proto new file mode 100644 index 00000000..e94131cc --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-simple-generate-api-protos-foo.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +package testapp.api; + +option go_package = "testapp/api;api_pb"; + + +import "google/api/annotations.proto"; + +service FooService { +} + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-simple-generate-app-server-foo_server.go b/cmd/grapi-gen-service/.snapshots/TestRun-simple-generate-app-server-foo_server.go new file mode 100644 index 00000000..04d3cf0b --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-simple-generate-app-server-foo_server.go @@ -0,0 +1,26 @@ +package server + +import ( + "context" + + "github.com/izumin5210/grapi/pkg/grapiserver" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + api_pb "testapp/api" +) + +// FooServiceServer is a composite interface of api_pb.FooServiceServer and grapiserver.Server. +type FooServiceServer interface { + api_pb.FooServiceServer + grapiserver.Server +} + +// NewFooServiceServer creates a new FooServiceServer instance. +func NewFooServiceServer() FooServiceServer { + return &fooServiceServerImpl{} +} + +type fooServiceServerImpl struct { +} + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-simple-generate-app-server-foo_server_register_funcs.go b/cmd/grapi-gen-service/.snapshots/TestRun-simple-generate-app-server-foo_server_register_funcs.go new file mode 100644 index 00000000..7b576996 --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-simple-generate-app-server-foo_server_register_funcs.go @@ -0,0 +1,23 @@ +// Code generated by github.com/izumin5210/grapi. DO NOT EDIT. + +package server + +import ( + "context" + + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "google.golang.org/grpc" + + api_pb "testapp/api" +) + +// RegisterWithServer implements grapiserver.Server.RegisterWithServer. +func (s *fooServiceServerImpl) RegisterWithServer(grpcSvr *grpc.Server) { + api_pb.RegisterFooServiceServer(grpcSvr, s) +} + +// RegisterWithHandler implements grapiserver.Server.RegisterWithHandler. +func (s *fooServiceServerImpl) RegisterWithHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return api_pb.RegisterFooServiceHandler(ctx, mux, conn) +} + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-simple-generate-app-server-foo_server_test.go b/cmd/grapi-gen-service/.snapshots/TestRun-simple-generate-app-server-foo_server_test.go new file mode 100644 index 00000000..a86bc127 --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-simple-generate-app-server-foo_server_test.go @@ -0,0 +1,2 @@ +package server + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-skip_tests-generate-api-protos-book.proto b/cmd/grapi-gen-service/.snapshots/TestRun-skip_tests-generate-api-protos-book.proto new file mode 100644 index 00000000..5fe20ee3 --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-skip_tests-generate-api-protos-book.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +package testapp.api; + +option go_package = "testapp/api;api_pb"; + + +import "google/api/annotations.proto"; + +service BookService { +} + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-skip_tests-generate-app-server-book_server.go b/cmd/grapi-gen-service/.snapshots/TestRun-skip_tests-generate-app-server-book_server.go new file mode 100644 index 00000000..9c739963 --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-skip_tests-generate-app-server-book_server.go @@ -0,0 +1,26 @@ +package server + +import ( + "context" + + "github.com/izumin5210/grapi/pkg/grapiserver" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + api_pb "testapp/api" +) + +// BookServiceServer is a composite interface of api_pb.BookServiceServer and grapiserver.Server. +type BookServiceServer interface { + api_pb.BookServiceServer + grapiserver.Server +} + +// NewBookServiceServer creates a new BookServiceServer instance. +func NewBookServiceServer() BookServiceServer { + return &bookServiceServerImpl{} +} + +type bookServiceServerImpl struct { +} + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-skip_tests-generate-app-server-book_server_register_funcs.go b/cmd/grapi-gen-service/.snapshots/TestRun-skip_tests-generate-app-server-book_server_register_funcs.go new file mode 100644 index 00000000..0c8e7832 --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-skip_tests-generate-app-server-book_server_register_funcs.go @@ -0,0 +1,23 @@ +// Code generated by github.com/izumin5210/grapi. DO NOT EDIT. + +package server + +import ( + "context" + + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "google.golang.org/grpc" + + api_pb "testapp/api" +) + +// RegisterWithServer implements grapiserver.Server.RegisterWithServer. +func (s *bookServiceServerImpl) RegisterWithServer(grpcSvr *grpc.Server) { + api_pb.RegisterBookServiceServer(grpcSvr, s) +} + +// RegisterWithHandler implements grapiserver.Server.RegisterWithHandler. +func (s *bookServiceServerImpl) RegisterWithHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return api_pb.RegisterBookServiceHandler(ctx, mux, conn) +} + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-snake_case_name-generate-api-protos-foo-bar_baz.proto b/cmd/grapi-gen-service/.snapshots/TestRun-snake_case_name-generate-api-protos-foo-bar_baz.proto new file mode 100644 index 00000000..2590d59c --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-snake_case_name-generate-api-protos-foo-bar_baz.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +package testapp.api.foo; + +option go_package = "testapp/api/foo;foo_pb"; + + +import "google/api/annotations.proto"; + +service BarBazService { +} + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-snake_case_name-generate-app-server-foo-bar_baz_server.go b/cmd/grapi-gen-service/.snapshots/TestRun-snake_case_name-generate-app-server-foo-bar_baz_server.go new file mode 100644 index 00000000..fa385d3b --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-snake_case_name-generate-app-server-foo-bar_baz_server.go @@ -0,0 +1,26 @@ +package foo + +import ( + "context" + + "github.com/izumin5210/grapi/pkg/grapiserver" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + foo_pb "testapp/api/foo" +) + +// BarBazServiceServer is a composite interface of foo_pb.BarBazServiceServer and grapiserver.Server. +type BarBazServiceServer interface { + foo_pb.BarBazServiceServer + grapiserver.Server +} + +// NewBarBazServiceServer creates a new BarBazServiceServer instance. +func NewBarBazServiceServer() BarBazServiceServer { + return &barBazServiceServerImpl{} +} + +type barBazServiceServerImpl struct { +} + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-snake_case_name-generate-app-server-foo-bar_baz_server_register_funcs.go b/cmd/grapi-gen-service/.snapshots/TestRun-snake_case_name-generate-app-server-foo-bar_baz_server_register_funcs.go new file mode 100644 index 00000000..1c820532 --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-snake_case_name-generate-app-server-foo-bar_baz_server_register_funcs.go @@ -0,0 +1,23 @@ +// Code generated by github.com/izumin5210/grapi. DO NOT EDIT. + +package foo + +import ( + "context" + + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "google.golang.org/grpc" + + foo_pb "testapp/api/foo" +) + +// RegisterWithServer implements grapiserver.Server.RegisterWithServer. +func (s *barBazServiceServerImpl) RegisterWithServer(grpcSvr *grpc.Server) { + foo_pb.RegisterBarBazServiceServer(grpcSvr, s) +} + +// RegisterWithHandler implements grapiserver.Server.RegisterWithHandler. +func (s *barBazServiceServerImpl) RegisterWithHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return foo_pb.RegisterBarBazServiceHandler(ctx, mux, conn) +} + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-snake_case_name-generate-app-server-foo-bar_baz_server_test.go b/cmd/grapi-gen-service/.snapshots/TestRun-snake_case_name-generate-app-server-foo-bar_baz_server_test.go new file mode 100644 index 00000000..d1b1429c --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-snake_case_name-generate-app-server-foo-bar_baz_server_test.go @@ -0,0 +1,2 @@ +package foo + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-specify_package-generate-api-protos-foo.proto b/cmd/grapi-gen-service/.snapshots/TestRun-specify_package-generate-api-protos-foo.proto new file mode 100644 index 00000000..447b4721 --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-specify_package-generate-api-protos-foo.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +package testcompany.testapp; + +option go_package = "testapp/api;api_pb"; + + +import "google/api/annotations.proto"; + +service FooService { +} + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-specify_package-generate-app-server-foo_server.go b/cmd/grapi-gen-service/.snapshots/TestRun-specify_package-generate-app-server-foo_server.go new file mode 100644 index 00000000..04d3cf0b --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-specify_package-generate-app-server-foo_server.go @@ -0,0 +1,26 @@ +package server + +import ( + "context" + + "github.com/izumin5210/grapi/pkg/grapiserver" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + api_pb "testapp/api" +) + +// FooServiceServer is a composite interface of api_pb.FooServiceServer and grapiserver.Server. +type FooServiceServer interface { + api_pb.FooServiceServer + grapiserver.Server +} + +// NewFooServiceServer creates a new FooServiceServer instance. +func NewFooServiceServer() FooServiceServer { + return &fooServiceServerImpl{} +} + +type fooServiceServerImpl struct { +} + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-specify_package-generate-app-server-foo_server_register_funcs.go b/cmd/grapi-gen-service/.snapshots/TestRun-specify_package-generate-app-server-foo_server_register_funcs.go new file mode 100644 index 00000000..7b576996 --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-specify_package-generate-app-server-foo_server_register_funcs.go @@ -0,0 +1,23 @@ +// Code generated by github.com/izumin5210/grapi. DO NOT EDIT. + +package server + +import ( + "context" + + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "google.golang.org/grpc" + + api_pb "testapp/api" +) + +// RegisterWithServer implements grapiserver.Server.RegisterWithServer. +func (s *fooServiceServerImpl) RegisterWithServer(grpcSvr *grpc.Server) { + api_pb.RegisterFooServiceServer(grpcSvr, s) +} + +// RegisterWithHandler implements grapiserver.Server.RegisterWithHandler. +func (s *fooServiceServerImpl) RegisterWithHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return api_pb.RegisterFooServiceHandler(ctx, mux, conn) +} + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-specify_package-generate-app-server-foo_server_test.go b/cmd/grapi-gen-service/.snapshots/TestRun-specify_package-generate-app-server-foo_server_test.go new file mode 100644 index 00000000..a86bc127 --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-specify_package-generate-app-server-foo_server_test.go @@ -0,0 +1,2 @@ +package server + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_dir-generate-app-server-qux_server.go b/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_dir-generate-app-server-qux_server.go new file mode 100644 index 00000000..e0d87ee0 --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_dir-generate-app-server-qux_server.go @@ -0,0 +1,26 @@ +package server + +import ( + "context" + + "github.com/izumin5210/grapi/pkg/grapiserver" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + api_pb "testapp/api" +) + +// QuxServiceServer is a composite interface of api_pb.QuxServiceServer and grapiserver.Server. +type QuxServiceServer interface { + api_pb.QuxServiceServer + grapiserver.Server +} + +// NewQuxServiceServer creates a new QuxServiceServer instance. +func NewQuxServiceServer() QuxServiceServer { + return &quxServiceServerImpl{} +} + +type quxServiceServerImpl struct { +} + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_dir-generate-app-server-qux_server_register_funcs.go b/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_dir-generate-app-server-qux_server_register_funcs.go new file mode 100644 index 00000000..a23d82ec --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_dir-generate-app-server-qux_server_register_funcs.go @@ -0,0 +1,23 @@ +// Code generated by github.com/izumin5210/grapi. DO NOT EDIT. + +package server + +import ( + "context" + + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "google.golang.org/grpc" + + api_pb "testapp/api" +) + +// RegisterWithServer implements grapiserver.Server.RegisterWithServer. +func (s *quxServiceServerImpl) RegisterWithServer(grpcSvr *grpc.Server) { + api_pb.RegisterQuxServiceServer(grpcSvr, s) +} + +// RegisterWithHandler implements grapiserver.Server.RegisterWithHandler. +func (s *quxServiceServerImpl) RegisterWithHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return api_pb.RegisterQuxServiceHandler(ctx, mux, conn) +} + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_dir-generate-app-server-qux_server_test.go b/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_dir-generate-app-server-qux_server_test.go new file mode 100644 index 00000000..a86bc127 --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_dir-generate-app-server-qux_server_test.go @@ -0,0 +1,2 @@ +package server + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_dir-generate-pkg-foo-protos-qux.proto b/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_dir-generate-pkg-foo-protos-qux.proto new file mode 100644 index 00000000..3ea614e4 --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_dir-generate-pkg-foo-protos-qux.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +package testapp.api; + +option go_package = "testapp/api;api_pb"; + + +import "google/api/annotations.proto"; + +service QuxService { +} + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_out_dir-generate-api-protos-quux.proto b/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_out_dir-generate-api-protos-quux.proto new file mode 100644 index 00000000..e97ef74d --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_out_dir-generate-api-protos-quux.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +package testapp.api.out; + +option go_package = "testapp/api/out;out_pb"; + + +import "google/api/annotations.proto"; + +service QuuxService { +} + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_out_dir-generate-app-server-quux_server.go b/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_out_dir-generate-app-server-quux_server.go new file mode 100644 index 00000000..2aa3e521 --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_out_dir-generate-app-server-quux_server.go @@ -0,0 +1,26 @@ +package server + +import ( + "context" + + "github.com/izumin5210/grapi/pkg/grapiserver" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + out_pb "testapp/api/out" +) + +// QuuxServiceServer is a composite interface of out_pb.QuuxServiceServer and grapiserver.Server. +type QuuxServiceServer interface { + out_pb.QuuxServiceServer + grapiserver.Server +} + +// NewQuuxServiceServer creates a new QuuxServiceServer instance. +func NewQuuxServiceServer() QuuxServiceServer { + return &quuxServiceServerImpl{} +} + +type quuxServiceServerImpl struct { +} + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_out_dir-generate-app-server-quux_server_register_funcs.go b/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_out_dir-generate-app-server-quux_server_register_funcs.go new file mode 100644 index 00000000..26cfa4ea --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_out_dir-generate-app-server-quux_server_register_funcs.go @@ -0,0 +1,23 @@ +// Code generated by github.com/izumin5210/grapi. DO NOT EDIT. + +package server + +import ( + "context" + + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "google.golang.org/grpc" + + out_pb "testapp/api/out" +) + +// RegisterWithServer implements grapiserver.Server.RegisterWithServer. +func (s *quuxServiceServerImpl) RegisterWithServer(grpcSvr *grpc.Server) { + out_pb.RegisterQuuxServiceServer(grpcSvr, s) +} + +// RegisterWithHandler implements grapiserver.Server.RegisterWithHandler. +func (s *quuxServiceServerImpl) RegisterWithHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return out_pb.RegisterQuuxServiceHandler(ctx, mux, conn) +} + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_out_dir-generate-app-server-quux_server_test.go b/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_out_dir-generate-app-server-quux_server_test.go new file mode 100644 index 00000000..a86bc127 --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-specify_proto_out_dir-generate-app-server-quux_server_test.go @@ -0,0 +1,2 @@ +package server + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-specify_resource_name-generate-api-protos-library.proto b/cmd/grapi-gen-service/.snapshots/TestRun-specify_resource_name-generate-api-protos-library.proto new file mode 100644 index 00000000..5310394b --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-specify_resource_name-generate-api-protos-library.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +package testapp.api; + +option go_package = "testapp/api;api_pb"; + + +import "google/api/annotations.proto"; + +service LibraryService { +} + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-specify_resource_name-generate-app-server-library_server.go b/cmd/grapi-gen-service/.snapshots/TestRun-specify_resource_name-generate-app-server-library_server.go new file mode 100644 index 00000000..445f9247 --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-specify_resource_name-generate-app-server-library_server.go @@ -0,0 +1,26 @@ +package server + +import ( + "context" + + "github.com/izumin5210/grapi/pkg/grapiserver" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + api_pb "testapp/api" +) + +// LibraryServiceServer is a composite interface of api_pb.LibraryServiceServer and grapiserver.Server. +type LibraryServiceServer interface { + api_pb.LibraryServiceServer + grapiserver.Server +} + +// NewLibraryServiceServer creates a new LibraryServiceServer instance. +func NewLibraryServiceServer() LibraryServiceServer { + return &libraryServiceServerImpl{} +} + +type libraryServiceServerImpl struct { +} + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-specify_resource_name-generate-app-server-library_server_register_funcs.go b/cmd/grapi-gen-service/.snapshots/TestRun-specify_resource_name-generate-app-server-library_server_register_funcs.go new file mode 100644 index 00000000..71a96737 --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-specify_resource_name-generate-app-server-library_server_register_funcs.go @@ -0,0 +1,23 @@ +// Code generated by github.com/izumin5210/grapi. DO NOT EDIT. + +package server + +import ( + "context" + + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "google.golang.org/grpc" + + api_pb "testapp/api" +) + +// RegisterWithServer implements grapiserver.Server.RegisterWithServer. +func (s *libraryServiceServerImpl) RegisterWithServer(grpcSvr *grpc.Server) { + api_pb.RegisterLibraryServiceServer(grpcSvr, s) +} + +// RegisterWithHandler implements grapiserver.Server.RegisterWithHandler. +func (s *libraryServiceServerImpl) RegisterWithHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return api_pb.RegisterLibraryServiceHandler(ctx, mux, conn) +} + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-specify_resource_name-generate-app-server-library_server_test.go b/cmd/grapi-gen-service/.snapshots/TestRun-specify_resource_name-generate-app-server-library_server_test.go new file mode 100644 index 00000000..a86bc127 --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-specify_resource_name-generate-app-server-library_server_test.go @@ -0,0 +1,2 @@ +package server + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-specify_server_dir-generate-api-protos-corge.proto b/cmd/grapi-gen-service/.snapshots/TestRun-specify_server_dir-generate-api-protos-corge.proto new file mode 100644 index 00000000..ee9ce2a1 --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-specify_server_dir-generate-api-protos-corge.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; + +package testapp.api; + +option go_package = "testapp/api;api_pb"; + + +import "google/api/annotations.proto"; + +service CorgeService { +} + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-specify_server_dir-generate-pkg-foo-server-corge_server.go b/cmd/grapi-gen-service/.snapshots/TestRun-specify_server_dir-generate-pkg-foo-server-corge_server.go new file mode 100644 index 00000000..13334764 --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-specify_server_dir-generate-pkg-foo-server-corge_server.go @@ -0,0 +1,26 @@ +package server + +import ( + "context" + + "github.com/izumin5210/grapi/pkg/grapiserver" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + api_pb "testapp/api" +) + +// CorgeServiceServer is a composite interface of api_pb.CorgeServiceServer and grapiserver.Server. +type CorgeServiceServer interface { + api_pb.CorgeServiceServer + grapiserver.Server +} + +// NewCorgeServiceServer creates a new CorgeServiceServer instance. +func NewCorgeServiceServer() CorgeServiceServer { + return &corgeServiceServerImpl{} +} + +type corgeServiceServerImpl struct { +} + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-specify_server_dir-generate-pkg-foo-server-corge_server_register_funcs.go b/cmd/grapi-gen-service/.snapshots/TestRun-specify_server_dir-generate-pkg-foo-server-corge_server_register_funcs.go new file mode 100644 index 00000000..93d5b5b8 --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-specify_server_dir-generate-pkg-foo-server-corge_server_register_funcs.go @@ -0,0 +1,23 @@ +// Code generated by github.com/izumin5210/grapi. DO NOT EDIT. + +package server + +import ( + "context" + + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "google.golang.org/grpc" + + api_pb "testapp/api" +) + +// RegisterWithServer implements grapiserver.Server.RegisterWithServer. +func (s *corgeServiceServerImpl) RegisterWithServer(grpcSvr *grpc.Server) { + api_pb.RegisterCorgeServiceServer(grpcSvr, s) +} + +// RegisterWithHandler implements grapiserver.Server.RegisterWithHandler. +func (s *corgeServiceServerImpl) RegisterWithHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return api_pb.RegisterCorgeServiceHandler(ctx, mux, conn) +} + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-specify_server_dir-generate-pkg-foo-server-corge_server_test.go b/cmd/grapi-gen-service/.snapshots/TestRun-specify_server_dir-generate-pkg-foo-server-corge_server_test.go new file mode 100644 index 00000000..a86bc127 --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-specify_server_dir-generate-pkg-foo-server-corge_server_test.go @@ -0,0 +1,2 @@ +package server + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-with_non-standard_methods-generate-api-protos-foo-bar_baz.proto b/cmd/grapi-gen-service/.snapshots/TestRun-with_non-standard_methods-generate-api-protos-foo-bar_baz.proto new file mode 100644 index 00000000..5ea823df --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-with_non-standard_methods-generate-api-protos-foo-bar_baz.proto @@ -0,0 +1,70 @@ +syntax = "proto3"; + +package testapp.api.foo; + +option go_package = "testapp/api/foo;foo_pb"; + + +import "google/api/annotations.proto"; +import "google/protobuf/empty.proto"; + +service BarBazService { + rpc ListBarBazs (ListBarBazsRequest) returns (ListBarBazsResponse) { + option (google.api.http) = { + get: "/bar_bazs" + }; + } + rpc CreateBarBaz (CreateBarBazRequest) returns (BarBaz) { + option (google.api.http) = { + post: "/bar_bazs" + body: "bar_baz" + }; + } + rpc DeleteBarBaz (DeleteBarBazRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/bar_bazs/{bar_baz_id}" + }; + } + rpc Rename (RenameRequest) returns (RenameResponse) { + option (google.api.http) = { + get: "/bar_bazs/rename" + }; + } + rpc MoveMove (MoveMoveRequest) returns (MoveMoveResponse) { + option (google.api.http) = { + get: "/bar_bazs/move_move" + }; + } +} + +message BarBaz { + string bar_baz_id = 1; +} + +message ListBarBazsRequest { +} + +message ListBarBazsResponse { + repeated BarBaz bar_bazs = 1; +} + +message CreateBarBazRequest { + BarBaz bar_baz = 1; +} + +message DeleteBarBazRequest { + string bar_baz_id = 1; +} + +message RenameRequest { +} + +message RenameResponse { +} + +message MoveMoveRequest { +} + +message MoveMoveResponse { +} + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-with_non-standard_methods-generate-app-server-foo-bar_baz_server.go b/cmd/grapi-gen-service/.snapshots/TestRun-with_non-standard_methods-generate-app-server-foo-bar_baz_server.go new file mode 100644 index 00000000..0fb60647 --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-with_non-standard_methods-generate-app-server-foo-bar_baz_server.go @@ -0,0 +1,52 @@ +package foo + +import ( + "context" + + "github.com/golang/protobuf/ptypes/empty" + "github.com/izumin5210/grapi/pkg/grapiserver" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + foo_pb "testapp/api/foo" +) + +// BarBazServiceServer is a composite interface of foo_pb.BarBazServiceServer and grapiserver.Server. +type BarBazServiceServer interface { + foo_pb.BarBazServiceServer + grapiserver.Server +} + +// NewBarBazServiceServer creates a new BarBazServiceServer instance. +func NewBarBazServiceServer() BarBazServiceServer { + return &barBazServiceServerImpl{} +} + +type barBazServiceServerImpl struct { +} + +func (s *barBazServiceServerImpl) ListBarBazs(ctx context.Context, req *foo_pb.ListBarBazsRequest) (*foo_pb.ListBarBazsResponse, error) { + // TODO: Not yet implemented. + return nil, status.Error(codes.Unimplemented, "TODO: You should implement it!") +} + +func (s *barBazServiceServerImpl) CreateBarBaz(ctx context.Context, req *foo_pb.CreateBarBazRequest) (*foo_pb.BarBaz, error) { + // TODO: Not yet implemented. + return nil, status.Error(codes.Unimplemented, "TODO: You should implement it!") +} + +func (s *barBazServiceServerImpl) DeleteBarBaz(ctx context.Context, req *foo_pb.DeleteBarBazRequest) (*empty.Empty, error) { + // TODO: Not yet implemented. + return nil, status.Error(codes.Unimplemented, "TODO: You should implement it!") +} + +func (s *barBazServiceServerImpl) Rename(ctx context.Context, req *foo_pb.RenameRequest) (*foo_pb.RenameResponse, error) { + // TODO: Not yet implemented. + return nil, status.Error(codes.Unimplemented, "TODO: You should implement it!") +} + +func (s *barBazServiceServerImpl) MoveMove(ctx context.Context, req *foo_pb.MoveMoveRequest) (*foo_pb.MoveMoveResponse, error) { + // TODO: Not yet implemented. + return nil, status.Error(codes.Unimplemented, "TODO: You should implement it!") +} + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-with_non-standard_methods-generate-app-server-foo-bar_baz_server_register_funcs.go b/cmd/grapi-gen-service/.snapshots/TestRun-with_non-standard_methods-generate-app-server-foo-bar_baz_server_register_funcs.go new file mode 100644 index 00000000..1c820532 --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-with_non-standard_methods-generate-app-server-foo-bar_baz_server_register_funcs.go @@ -0,0 +1,23 @@ +// Code generated by github.com/izumin5210/grapi. DO NOT EDIT. + +package foo + +import ( + "context" + + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "google.golang.org/grpc" + + foo_pb "testapp/api/foo" +) + +// RegisterWithServer implements grapiserver.Server.RegisterWithServer. +func (s *barBazServiceServerImpl) RegisterWithServer(grpcSvr *grpc.Server) { + foo_pb.RegisterBarBazServiceServer(grpcSvr, s) +} + +// RegisterWithHandler implements grapiserver.Server.RegisterWithHandler. +func (s *barBazServiceServerImpl) RegisterWithHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return foo_pb.RegisterBarBazServiceHandler(ctx, mux, conn) +} + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-with_non-standard_methods-generate-app-server-foo-bar_baz_server_test.go b/cmd/grapi-gen-service/.snapshots/TestRun-with_non-standard_methods-generate-app-server-foo-bar_baz_server_test.go new file mode 100644 index 00000000..f1edda88 --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-with_non-standard_methods-generate-app-server-foo-bar_baz_server_test.go @@ -0,0 +1,104 @@ +package foo + +import ( + "context" + "testing" + + foo_pb "testapp/api/foo" +) + +func Test_BarBazServiceServer_ListBarBazs(t *testing.T) { + svr := NewBarBazServiceServer() + + ctx := context.Background() + req := &foo_pb.ListBarBazsRequest{} + + resp, err := svr.ListBarBazs(ctx, req) + + t.SkipNow() + + if err != nil { + t.Errorf("returned an error %v", err) + } + + if resp == nil { + t.Error("response should not nil") + } +} + +func Test_BarBazServiceServer_CreateBarBaz(t *testing.T) { + svr := NewBarBazServiceServer() + + ctx := context.Background() + req := &foo_pb.CreateBarBazRequest{} + + resp, err := svr.CreateBarBaz(ctx, req) + + t.SkipNow() + + if err != nil { + t.Errorf("returned an error %v", err) + } + + if resp == nil { + t.Error("response should not nil") + } +} + +func Test_BarBazServiceServer_DeleteBarBaz(t *testing.T) { + svr := NewBarBazServiceServer() + + ctx := context.Background() + req := &foo_pb.DeleteBarBazRequest{} + + resp, err := svr.DeleteBarBaz(ctx, req) + + t.SkipNow() + + if err != nil { + t.Errorf("returned an error %v", err) + } + + if resp == nil { + t.Error("response should not nil") + } +} + +func Test_BarBazServiceServer_Rename(t *testing.T) { + svr := NewBarBazServiceServer() + + ctx := context.Background() + req := &foo_pb.RenameRequest{} + + resp, err := svr.Rename(ctx, req) + + t.SkipNow() + + if err != nil { + t.Errorf("returned an error %v", err) + } + + if resp == nil { + t.Error("response should not nil") + } +} + +func Test_BarBazServiceServer_MoveMove(t *testing.T) { + svr := NewBarBazServiceServer() + + ctx := context.Background() + req := &foo_pb.MoveMoveRequest{} + + resp, err := svr.MoveMove(ctx, req) + + t.SkipNow() + + if err != nil { + t.Errorf("returned an error %v", err) + } + + if resp == nil { + t.Error("response should not nil") + } +} + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-with_some_standard_methods-generate-api-protos-foo-bar_baz.proto b/cmd/grapi-gen-service/.snapshots/TestRun-with_some_standard_methods-generate-api-protos-foo-bar_baz.proto new file mode 100644 index 00000000..44b449c3 --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-with_some_standard_methods-generate-api-protos-foo-bar_baz.proto @@ -0,0 +1,48 @@ +syntax = "proto3"; + +package testapp.api.foo; + +option go_package = "testapp/api/foo;foo_pb"; + + +import "google/api/annotations.proto"; +import "google/protobuf/empty.proto"; + +service BarBazService { + rpc ListBarBazs (ListBarBazsRequest) returns (ListBarBazsResponse) { + option (google.api.http) = { + get: "/bar_bazs" + }; + } + rpc CreateBarBaz (CreateBarBazRequest) returns (BarBaz) { + option (google.api.http) = { + post: "/bar_bazs" + body: "bar_baz" + }; + } + rpc DeleteBarBaz (DeleteBarBazRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/bar_bazs/{bar_baz_id}" + }; + } +} + +message BarBaz { + string bar_baz_id = 1; +} + +message ListBarBazsRequest { +} + +message ListBarBazsResponse { + repeated BarBaz bar_bazs = 1; +} + +message CreateBarBazRequest { + BarBaz bar_baz = 1; +} + +message DeleteBarBazRequest { + string bar_baz_id = 1; +} + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-with_some_standard_methods-generate-app-server-foo-bar_baz_server.go b/cmd/grapi-gen-service/.snapshots/TestRun-with_some_standard_methods-generate-app-server-foo-bar_baz_server.go new file mode 100644 index 00000000..fc85ca3d --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-with_some_standard_methods-generate-app-server-foo-bar_baz_server.go @@ -0,0 +1,42 @@ +package foo + +import ( + "context" + + "github.com/golang/protobuf/ptypes/empty" + "github.com/izumin5210/grapi/pkg/grapiserver" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + + foo_pb "testapp/api/foo" +) + +// BarBazServiceServer is a composite interface of foo_pb.BarBazServiceServer and grapiserver.Server. +type BarBazServiceServer interface { + foo_pb.BarBazServiceServer + grapiserver.Server +} + +// NewBarBazServiceServer creates a new BarBazServiceServer instance. +func NewBarBazServiceServer() BarBazServiceServer { + return &barBazServiceServerImpl{} +} + +type barBazServiceServerImpl struct { +} + +func (s *barBazServiceServerImpl) ListBarBazs(ctx context.Context, req *foo_pb.ListBarBazsRequest) (*foo_pb.ListBarBazsResponse, error) { + // TODO: Not yet implemented. + return nil, status.Error(codes.Unimplemented, "TODO: You should implement it!") +} + +func (s *barBazServiceServerImpl) CreateBarBaz(ctx context.Context, req *foo_pb.CreateBarBazRequest) (*foo_pb.BarBaz, error) { + // TODO: Not yet implemented. + return nil, status.Error(codes.Unimplemented, "TODO: You should implement it!") +} + +func (s *barBazServiceServerImpl) DeleteBarBaz(ctx context.Context, req *foo_pb.DeleteBarBazRequest) (*empty.Empty, error) { + // TODO: Not yet implemented. + return nil, status.Error(codes.Unimplemented, "TODO: You should implement it!") +} + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-with_some_standard_methods-generate-app-server-foo-bar_baz_server_register_funcs.go b/cmd/grapi-gen-service/.snapshots/TestRun-with_some_standard_methods-generate-app-server-foo-bar_baz_server_register_funcs.go new file mode 100644 index 00000000..1c820532 --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-with_some_standard_methods-generate-app-server-foo-bar_baz_server_register_funcs.go @@ -0,0 +1,23 @@ +// Code generated by github.com/izumin5210/grapi. DO NOT EDIT. + +package foo + +import ( + "context" + + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "google.golang.org/grpc" + + foo_pb "testapp/api/foo" +) + +// RegisterWithServer implements grapiserver.Server.RegisterWithServer. +func (s *barBazServiceServerImpl) RegisterWithServer(grpcSvr *grpc.Server) { + foo_pb.RegisterBarBazServiceServer(grpcSvr, s) +} + +// RegisterWithHandler implements grapiserver.Server.RegisterWithHandler. +func (s *barBazServiceServerImpl) RegisterWithHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return foo_pb.RegisterBarBazServiceHandler(ctx, mux, conn) +} + diff --git a/cmd/grapi-gen-service/.snapshots/TestRun-with_some_standard_methods-generate-app-server-foo-bar_baz_server_test.go b/cmd/grapi-gen-service/.snapshots/TestRun-with_some_standard_methods-generate-app-server-foo-bar_baz_server_test.go new file mode 100644 index 00000000..f2414a33 --- /dev/null +++ b/cmd/grapi-gen-service/.snapshots/TestRun-with_some_standard_methods-generate-app-server-foo-bar_baz_server_test.go @@ -0,0 +1,66 @@ +package foo + +import ( + "context" + "testing" + + foo_pb "testapp/api/foo" +) + +func Test_BarBazServiceServer_ListBarBazs(t *testing.T) { + svr := NewBarBazServiceServer() + + ctx := context.Background() + req := &foo_pb.ListBarBazsRequest{} + + resp, err := svr.ListBarBazs(ctx, req) + + t.SkipNow() + + if err != nil { + t.Errorf("returned an error %v", err) + } + + if resp == nil { + t.Error("response should not nil") + } +} + +func Test_BarBazServiceServer_CreateBarBaz(t *testing.T) { + svr := NewBarBazServiceServer() + + ctx := context.Background() + req := &foo_pb.CreateBarBazRequest{} + + resp, err := svr.CreateBarBaz(ctx, req) + + t.SkipNow() + + if err != nil { + t.Errorf("returned an error %v", err) + } + + if resp == nil { + t.Error("response should not nil") + } +} + +func Test_BarBazServiceServer_DeleteBarBaz(t *testing.T) { + svr := NewBarBazServiceServer() + + ctx := context.Background() + req := &foo_pb.DeleteBarBazRequest{} + + resp, err := svr.DeleteBarBaz(ctx, req) + + t.SkipNow() + + if err != nil { + t.Errorf("returned an error %v", err) + } + + if resp == nil { + t.Error("response should not nil") + } +} + diff --git a/cmd/grapi-gen-service/main.go b/cmd/grapi-gen-service/main.go new file mode 100644 index 00000000..704d2299 --- /dev/null +++ b/cmd/grapi-gen-service/main.go @@ -0,0 +1,83 @@ +package main + +import ( + "context" + + "github.com/pkg/errors" + "github.com/spf13/cobra" + + "github.com/izumin5210/grapi/pkg/gencmd" + "github.com/izumin5210/grapi/pkg/svcgen" +) + +func main() { + buildCommand(svcgen.NewApp).MustExecute() +} + +func buildCommand(createAppFunc svcgen.CreateAppFunc, opts ...gencmd.Option) gencmd.Executor { + return gencmd.New( + "service", + newGenerateCommand(createAppFunc), + newDestroyCommand(createAppFunc), + opts..., + ) +} + +func newGenerateCommand(createApp svcgen.CreateAppFunc) *gencmd.Command { + var ( + skipTest bool + resName string + app *svcgen.App + ) + + cmd := &gencmd.Command{ + Use: "generate NAME [flags] [METHODS...]", + Short: "Generate a new service", + Args: cobra.MinimumNArgs(1), + ShouldInsideApp: true, + PreRun: func(c *gencmd.Command, args []string) error { + var err error + app, err = createApp(c) + return errors.WithStack(err) + }, + BuildParams: func(c *gencmd.Command, args []string) (interface{}, error) { + svcName := args[0] + methods := args[1:] + + params, err := app.ParamsBuilder.Build(svcName, resName, methods) + return params, errors.WithStack(err) + }, + PostRun: func(c *gencmd.Command, args []string) error { + return errors.WithStack(app.ProtocWrapper.Exec(context.TODO())) + }, + } + + cmd.Flags().BoolVarP(&skipTest, "skip-test", "T", false, "Skip test files") + cmd.Flags().StringVar(&resName, "resource-name", "", "ResourceName to be used") + + return cmd +} + +func newDestroyCommand(createApp svcgen.CreateAppFunc) *gencmd.Command { + var ( + app *svcgen.App + ) + + cmd := &gencmd.Command{ + Use: "destroy NAME", + Short: "Destroy an existing service", + Args: cobra.MinimumNArgs(1), + ShouldInsideApp: true, + PreRun: func(c *gencmd.Command, args []string) error { + var err error + app, err = createApp(c) + return errors.WithStack(err) + }, + BuildParams: func(c *gencmd.Command, args []string) (interface{}, error) { + params, err := app.ParamsBuilder.Build(args[0], "", nil) + return params, errors.WithStack(err) + }, + } + + return cmd +} diff --git a/cmd/grapi-gen-service/main_test.go b/cmd/grapi-gen-service/main_test.go new file mode 100644 index 00000000..666e92c3 --- /dev/null +++ b/cmd/grapi-gen-service/main_test.go @@ -0,0 +1,209 @@ +package main + +import ( + "context" + "testing" + + "github.com/spf13/afero" + + "github.com/izumin5210/clig/pkg/clib" + "github.com/izumin5210/grapi/pkg/cli" + "github.com/izumin5210/grapi/pkg/gencmd" + gencmdtesting "github.com/izumin5210/grapi/pkg/gencmd/testing" + "github.com/izumin5210/grapi/pkg/grapicmd" + "github.com/izumin5210/grapi/pkg/protoc" + "github.com/izumin5210/grapi/pkg/svcgen" + svcgentesting "github.com/izumin5210/grapi/pkg/svcgen/testing" +) + +func TestRun(t *testing.T) { + cases := []svcgentesting.Case{ + { + Test: "simple", + GArgs: []string{"foo"}, + DArgs: []string{"foo"}, + Files: []string{ + "api/protos/foo.proto", + "app/server/foo_server.go", + "app/server/foo_server_register_funcs.go", + "app/server/foo_server_test.go", + }, + }, + { + Test: "specify package", + GArgs: []string{"foo"}, + DArgs: []string{"foo"}, + Files: []string{ + "api/protos/foo.proto", + "app/server/foo_server.go", + "app/server/foo_server_register_funcs.go", + "app/server/foo_server_test.go", + }, + PkgName: "testcompany.testapp", + }, + { + Test: "nested", + GArgs: []string{"foo/bar"}, + DArgs: []string{"foo/bar"}, + Files: []string{ + "api/protos/foo/bar.proto", + "app/server/foo/bar_server.go", + "app/server/foo/bar_server_register_funcs.go", + "app/server/foo/bar_server_test.go", + }, + }, + { + Test: "nested with specify pacakge", + GArgs: []string{"foo/bar"}, + DArgs: []string{"foo/bar"}, + Files: []string{ + "api/protos/foo/bar.proto", + "app/server/foo/bar_server.go", + "app/server/foo/bar_server_register_funcs.go", + "app/server/foo/bar_server_test.go", + }, + PkgName: "testcompany.testapp", + }, + { + Test: "snake_case name", + GArgs: []string{"foo/bar_baz"}, + DArgs: []string{"foo/bar_baz"}, + Files: []string{ + "api/protos/foo/bar_baz.proto", + "app/server/foo/bar_baz_server.go", + "app/server/foo/bar_baz_server_register_funcs.go", + "app/server/foo/bar_baz_server_test.go", + }, + }, + { + Test: "kebab-case name", + GArgs: []string{"foo/bar-baz"}, + DArgs: []string{"foo/bar-baz"}, + Files: []string{ + "api/protos/foo/bar_baz.proto", + "app/server/foo/bar_baz_server.go", + "app/server/foo/bar_baz_server_register_funcs.go", + "app/server/foo/bar_baz_server_test.go", + }, + }, + { + Test: "with some standard methods", + GArgs: []string{"foo/bar-baz", "list", "create", "delete"}, + DArgs: []string{"foo/bar-baz"}, + Files: []string{ + "api/protos/foo/bar_baz.proto", + "app/server/foo/bar_baz_server.go", + "app/server/foo/bar_baz_server_register_funcs.go", + "app/server/foo/bar_baz_server_test.go", + }, + }, + { + Test: "with non-standard methods", + GArgs: []string{"foo/bar-baz", "list", "create", "rename", "delete", "move_move"}, + DArgs: []string{"foo/bar-baz"}, + Files: []string{ + "api/protos/foo/bar_baz.proto", + "app/server/foo/bar_baz_server.go", + "app/server/foo/bar_baz_server_register_funcs.go", + "app/server/foo/bar_baz_server_test.go", + }, + }, + { + Test: "specify proto dir", + GArgs: []string{"qux"}, + DArgs: []string{"qux"}, + Files: []string{ + "pkg/foo/protos/qux.proto", + "app/server/qux_server.go", + "app/server/qux_server_register_funcs.go", + "app/server/qux_server_test.go", + }, + ProtoDir: "pkg/foo/protos", + }, + { + Test: "specify proto out dir", + GArgs: []string{"quux"}, + DArgs: []string{"quux"}, + Files: []string{ + "api/protos/quux.proto", + "app/server/quux_server.go", + "app/server/quux_server_register_funcs.go", + "app/server/quux_server_test.go", + }, + ProtoOutDir: "api/out", + }, + { + Test: "specify server dir", + GArgs: []string{"corge"}, + DArgs: []string{"corge"}, + Files: []string{ + "api/protos/corge.proto", + "pkg/foo/server/corge_server.go", + "pkg/foo/server/corge_server_register_funcs.go", + "pkg/foo/server/corge_server_test.go", + }, + ServerDir: "pkg/foo/server", + }, + { + Test: "skip tests", + GArgs: []string{"--skip-test", "book"}, + DArgs: []string{"book"}, + Files: []string{ + "api/protos/book.proto", + "app/server/book_server.go", + "app/server/book_server_register_funcs.go", + }, + SkippedFiles: map[string]struct{}{ + "app/server/book_server_test.go": {}, + }, + }, + { + Test: "specify resource name", + GArgs: []string{"library", "--resource-name=book"}, + DArgs: []string{"library"}, + Files: []string{ + "api/protos/library.proto", + "app/server/library_server.go", + "app/server/library_server_register_funcs.go", + "app/server/library_server_test.go", + }, + }, + } + + rootDir := cli.RootDir{clib.Path("/home/src/testapp")} + + createSvcApp := func(cmd *gencmd.Command) (*svcgen.App, error) { + return svcgentesting.NewTestApp(cmd, &fakeProtocWrapper{}, cli.NopUI) + } + createGenApp := func(cmd *gencmd.Command) (*gencmd.App, error) { + return gencmdtesting.NewTestApp(cmd, cli.NopUI) + } + createCmd := func(t *testing.T, fs afero.Fs, tc svcgentesting.Case) gencmd.Executor { + ctx := &grapicmd.Ctx{ + FS: fs, + RootDir: rootDir, + Config: grapicmd.Config{ + Package: tc.PkgName, + }, + ProtocConfig: protoc.Config{ + ProtosDir: tc.ProtoDir, + OutDir: tc.ProtoOutDir, + }, + } + ctx.Config.Grapi.ServerDir = tc.ServerDir + return buildCommand(createSvcApp, gencmd.WithGrapiCtx(ctx), gencmd.WithCreateAppFunc(createGenApp)) + } + + ctx := &svcgentesting.Ctx{ + GOPATH: "/home", + RootDir: rootDir, + CreateCmd: createCmd, + Cases: cases, + } + + svcgentesting.Run(t, ctx) +} + +type fakeProtocWrapper struct{} + +func (*fakeProtocWrapper) Exec(context.Context) error { return nil } diff --git a/cmd/grapi-gen-type/.snapshots/TestType-camel-generate-api-protos-type-foo-bar_user.proto b/cmd/grapi-gen-type/.snapshots/TestType-camel-generate-api-protos-type-foo-bar_user.proto new file mode 100644 index 00000000..9590f46e --- /dev/null +++ b/cmd/grapi-gen-type/.snapshots/TestType-camel-generate-api-protos-type-foo-bar_user.proto @@ -0,0 +1,10 @@ +syntax = "proto3"; + +package testapp.api.type.foo; + +option go_package = "testapp/api/type/foo;foo_pb"; + +message BarUser { + string id = 1; +} + diff --git a/cmd/grapi-gen-type/.snapshots/TestType-kebab-generate-api-protos-type-foo-bar_user.proto b/cmd/grapi-gen-type/.snapshots/TestType-kebab-generate-api-protos-type-foo-bar_user.proto new file mode 100644 index 00000000..9590f46e --- /dev/null +++ b/cmd/grapi-gen-type/.snapshots/TestType-kebab-generate-api-protos-type-foo-bar_user.proto @@ -0,0 +1,10 @@ +syntax = "proto3"; + +package testapp.api.type.foo; + +option go_package = "testapp/api/type/foo;foo_pb"; + +message BarUser { + string id = 1; +} + diff --git a/cmd/grapi-gen-type/.snapshots/TestType-nested-generate-api-protos-type-foo-user.proto b/cmd/grapi-gen-type/.snapshots/TestType-nested-generate-api-protos-type-foo-user.proto new file mode 100644 index 00000000..260291f9 --- /dev/null +++ b/cmd/grapi-gen-type/.snapshots/TestType-nested-generate-api-protos-type-foo-user.proto @@ -0,0 +1,10 @@ +syntax = "proto3"; + +package testapp.api.type.foo; + +option go_package = "testapp/api/type/foo;foo_pb"; + +message User { + string id = 1; +} + diff --git a/cmd/grapi-gen-type/.snapshots/TestType-nested_with_specified_package-generate-api-protos-type-foo-user.proto b/cmd/grapi-gen-type/.snapshots/TestType-nested_with_specified_package-generate-api-protos-type-foo-user.proto new file mode 100644 index 00000000..260291f9 --- /dev/null +++ b/cmd/grapi-gen-type/.snapshots/TestType-nested_with_specified_package-generate-api-protos-type-foo-user.proto @@ -0,0 +1,10 @@ +syntax = "proto3"; + +package testapp.api.type.foo; + +option go_package = "testapp/api/type/foo;foo_pb"; + +message User { + string id = 1; +} + diff --git a/cmd/grapi-gen-type/.snapshots/TestType-simple-generate-api-protos-type-book.proto b/cmd/grapi-gen-type/.snapshots/TestType-simple-generate-api-protos-type-book.proto new file mode 100644 index 00000000..ec954fee --- /dev/null +++ b/cmd/grapi-gen-type/.snapshots/TestType-simple-generate-api-protos-type-book.proto @@ -0,0 +1,10 @@ +syntax = "proto3"; + +package testapp.api.type; + +option go_package = "testapp/api/type;type_pb"; + +message Book { + string id = 1; +} + diff --git a/cmd/grapi-gen-type/.snapshots/TestType-simple_with_specified_package-generate-api-protos-type-book.proto b/cmd/grapi-gen-type/.snapshots/TestType-simple_with_specified_package-generate-api-protos-type-book.proto new file mode 100644 index 00000000..ec954fee --- /dev/null +++ b/cmd/grapi-gen-type/.snapshots/TestType-simple_with_specified_package-generate-api-protos-type-book.proto @@ -0,0 +1,10 @@ +syntax = "proto3"; + +package testapp.api.type; + +option go_package = "testapp/api/type;type_pb"; + +message Book { + string id = 1; +} + diff --git a/cmd/grapi-gen-type/.snapshots/TestType-snake-generate-api-protos-type-foo-bar_user.proto b/cmd/grapi-gen-type/.snapshots/TestType-snake-generate-api-protos-type-foo-bar_user.proto new file mode 100644 index 00000000..9590f46e --- /dev/null +++ b/cmd/grapi-gen-type/.snapshots/TestType-snake-generate-api-protos-type-foo-bar_user.proto @@ -0,0 +1,10 @@ +syntax = "proto3"; + +package testapp.api.type.foo; + +option go_package = "testapp/api/type/foo;foo_pb"; + +message BarUser { + string id = 1; +} + diff --git a/cmd/grapi-gen-type/di/app.go b/cmd/grapi-gen-type/di/app.go new file mode 100644 index 00000000..f61c910d --- /dev/null +++ b/cmd/grapi-gen-type/di/app.go @@ -0,0 +1,12 @@ +package di + +import ( + "github.com/izumin5210/grapi/pkg/gencmd" + "github.com/izumin5210/grapi/pkg/protoc" +) + +type CreateAppFunc func(*gencmd.Command) (*App, error) + +type App struct { + Protoc protoc.Wrapper +} diff --git a/cmd/grapi-gen-type/di/wire.go b/cmd/grapi-gen-type/di/wire.go new file mode 100644 index 00000000..038985b6 --- /dev/null +++ b/cmd/grapi-gen-type/di/wire.go @@ -0,0 +1,21 @@ +//+build wireinject + +package di + +import ( + "github.com/google/wire" + + "github.com/izumin5210/grapi/pkg/cli" + "github.com/izumin5210/grapi/pkg/gencmd" + "github.com/izumin5210/grapi/pkg/protoc" +) + +func NewApp(*gencmd.Command) (*App, error) { + wire.Build( + App{}, + gencmd.Set, + cli.UIInstance, + protoc.WrapperSet, + ) + return nil, nil +} diff --git a/cmd/grapi-gen-type/di/wire_gen.go b/cmd/grapi-gen-type/di/wire_gen.go new file mode 100644 index 00000000..d0f33a49 --- /dev/null +++ b/cmd/grapi-gen-type/di/wire_gen.go @@ -0,0 +1,36 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate wire +//+build !wireinject + +package di + +import ( + "github.com/izumin5210/grapi/pkg/cli" + "github.com/izumin5210/grapi/pkg/gencmd" + "github.com/izumin5210/grapi/pkg/grapicmd" + "github.com/izumin5210/grapi/pkg/protoc" +) + +// Injectors from wire.go: + +func NewApp(command *gencmd.Command) (*App, error) { + ctx := gencmd.ProvideCtx(command) + grapicmdCtx := gencmd.ProvideGrapiCtx(ctx) + config := grapicmd.ProvideProtocConfig(grapicmdCtx) + fs := grapicmd.ProvideFS(grapicmdCtx) + executor := grapicmd.ProvideExec(grapicmdCtx) + io := grapicmd.ProvideIO(grapicmdCtx) + ui := cli.UIInstance(io) + rootDir := grapicmd.ProvideRootDir(grapicmdCtx) + gexConfig := protoc.ProvideGexConfig(fs, executor, io, rootDir) + repository, err := protoc.ProvideToolRepository(gexConfig) + if err != nil { + return nil, err + } + wrapper := protoc.NewWrapper(config, fs, executor, ui, repository, rootDir) + app := &App{ + Protoc: wrapper, + } + return app, nil +} diff --git a/cmd/grapi-gen-type/main.go b/cmd/grapi-gen-type/main.go new file mode 100644 index 00000000..a0c4ab64 --- /dev/null +++ b/cmd/grapi-gen-type/main.go @@ -0,0 +1,101 @@ +package main + +import ( + "context" + "path/filepath" + + "github.com/pkg/errors" + "github.com/serenize/snaker" + "github.com/spf13/cobra" + + "github.com/izumin5210/grapi/cmd/grapi-gen-type/di" + _ "github.com/izumin5210/grapi/cmd/grapi-gen-type/template" + "github.com/izumin5210/grapi/pkg/gencmd" + gencmdutil "github.com/izumin5210/grapi/pkg/gencmd/util" + "github.com/izumin5210/grapi/pkg/grapicmd" +) + +func main() { + buildCommand(di.NewApp).MustExecute() +} + +func buildCommand(createApp di.CreateAppFunc, opts ...gencmd.Option) gencmd.Executor { + return gencmd.New( + "type", + newGenerateCommand(createApp), + newDestroyCommand(), + opts..., + ) +} + +func newGenerateCommand(createApp di.CreateAppFunc) *gencmd.Command { + var ( + app *di.App + ) + + return &gencmd.Command{ + Use: "generate NAME", + Short: "Generate a new type", + Args: cobra.ExactArgs(1), + ShouldInsideApp: true, + PreRun: func(c *gencmd.Command, args []string) error { + var err error + app, err = createApp(c) + return errors.WithStack(err) + }, + BuildParams: func(c *gencmd.Command, args []string) (interface{}, error) { + return buildParams(args[0], c.Ctx().Ctx) + }, + PostRun: func(c *gencmd.Command, args []string) error { + return errors.WithStack(app.Protoc.Exec(context.TODO())) + }, + } +} + +func newDestroyCommand() *gencmd.Command { + return &gencmd.Command{ + Use: "destroy NAME", + Short: "Destroy a existing type", + Args: cobra.ExactArgs(1), + ShouldInsideApp: true, + BuildParams: func(c *gencmd.Command, args []string) (interface{}, error) { + return buildParams(args[0], c.Ctx().Ctx) + }, + } +} + +type params struct { + Proto struct { + Package string + } + PbGo struct { + PackagePath string + PackageName string + } + Name string + Path string +} + +func buildParams(path string, ctx *grapicmd.Ctx) (*params, error) { + protoOutDir := ctx.ProtocConfig.OutDir + if protoOutDir == "" { + protoOutDir = filepath.Join("api") + } + path = filepath.Join("type", path) + + protoParams, err := gencmdutil.BuildProtoParams(path, ctx.RootDir, protoOutDir, ctx.Config.Package) + if err != nil { + return nil, errors.WithStack(err) + } + + // path => baz/qux/quux + path = protoParams.Proto.Path + + params := new(params) + params.Proto.Package = protoParams.Proto.Package + params.PbGo.PackageName = protoParams.PbGo.ImportName + params.PbGo.PackagePath = protoParams.PbGo.Package + params.Name = snaker.SnakeToCamel(filepath.Base(path)) + params.Path = path + return params, nil +} diff --git a/cmd/grapi-gen-type/main_test.go b/cmd/grapi-gen-type/main_test.go new file mode 100644 index 00000000..3de1a416 --- /dev/null +++ b/cmd/grapi-gen-type/main_test.go @@ -0,0 +1,140 @@ +package main + +import ( + "context" + "go/build" + "testing" + + "github.com/bradleyjkemp/cupaloy/v2" + "github.com/spf13/afero" + + "github.com/izumin5210/clig/pkg/clib" + "github.com/izumin5210/grapi/cmd/grapi-gen-type/di" + "github.com/izumin5210/grapi/pkg/cli" + "github.com/izumin5210/grapi/pkg/gencmd" + gencmdtesting "github.com/izumin5210/grapi/pkg/gencmd/testing" + "github.com/izumin5210/grapi/pkg/grapicmd" + "github.com/izumin5210/grapi/pkg/grapicmd/util/fs" +) + +func TestType(t *testing.T) { + cases := []struct { + test string + args []string + files []string + config string + }{ + { + test: "simple", + args: []string{"book"}, + files: []string{"api/protos/type/book.proto"}, + }, + { + test: "nested", + args: []string{"foo/user"}, + files: []string{"api/protos/type/foo/user.proto"}, + }, + { + test: "camel", + args: []string{"foo/barUser"}, + files: []string{"api/protos/type/foo/bar_user.proto"}, + }, + { + test: "snake", + args: []string{"foo/bar_user"}, + files: []string{"api/protos/type/foo/bar_user.proto"}, + }, + { + test: "kebab", + args: []string{"foo/bar-user"}, + files: []string{"api/protos/type/foo/bar_user.proto"}, + }, + { + test: "simple with specified package", + args: []string{"book"}, + files: []string{"api/protos/type/book.proto"}, + config: `package = "yourcompany.yourapp"`, + }, + { + test: "nested with specified package", + args: []string{"foo/user"}, + files: []string{"api/protos/type/foo/user.proto"}, + config: `package = "yourcompany.yourapp"`, + }, + } + + defer func(c build.Context) { fs.BuildContext = c }(fs.BuildContext) + fs.BuildContext = build.Context{GOPATH: "/go"} + rootDir := cli.RootDir{clib.Path("/go/src/testapp")} + + createApp := func(cmd *gencmd.Command) (*di.App, error) { + return &di.App{Protoc: &fakeProtocWrapper{}}, nil + } + createGenApp := func(cmd *gencmd.Command) (*gencmd.App, error) { + return gencmdtesting.NewTestApp(cmd, cli.NopUI) + } + createCmd := func(t *testing.T, fs afero.Fs) gencmd.Executor { + ctx := &grapicmd.Ctx{ + FS: fs, + RootDir: rootDir, + } + return buildCommand(createApp, gencmd.WithGrapiCtx(ctx), gencmd.WithCreateAppFunc(createGenApp)) + } + + for _, tc := range cases { + t.Run(tc.test, func(t *testing.T) { + fs := afero.NewMemMapFs() + afero.WriteFile(fs, rootDir.Join("grapi.toml").String(), []byte{}, 0755) + + t.Run("generate", func(t *testing.T) { + cmd := createCmd(t, fs) + cmd.Command().SetArgs(append([]string{"generate"}, tc.args...)) + err := cmd.Execute() + + if err != nil { + t.Errorf("returned an error: %+v", err) + } + + for _, file := range tc.files { + t.Run(file, func(t *testing.T) { + data, err := afero.ReadFile(fs, rootDir.Join(file).String()) + + if err != nil { + t.Errorf("returned an error: %v", err) + } + + cupaloy.SnapshotT(t, string(data)) + }) + } + }) + + t.Run("destroy", func(t *testing.T) { + cmd := createCmd(t, fs) + cmd.Command().SetArgs(append([]string{"destroy"}, tc.args...)) + err := cmd.Execute() + + if err != nil { + t.Errorf("returned an error: %+v", err) + } + + for _, file := range tc.files { + t.Run(file, func(t *testing.T) { + ok, err := afero.Exists(fs, rootDir.Join(file).String()) + + if err != nil { + t.Errorf("Exists(fs, %q) returned an error: %v", file, err) + } + + if ok { + t.Errorf("%q should not exist", file) + } + }) + } + }) + }) + } +} + +type fakeProtocWrapper struct{} + +func (*fakeProtocWrapper) Exec(context.Context) error { return nil } diff --git a/cmd/grapi-gen-type/template/_data/api/protos/{{.Path}}.proto.tmpl b/cmd/grapi-gen-type/template/_data/api/protos/{{.Path}}.proto.tmpl new file mode 100644 index 00000000..a9bb09bd --- /dev/null +++ b/cmd/grapi-gen-type/template/_data/api/protos/{{.Path}}.proto.tmpl @@ -0,0 +1,9 @@ +syntax = "proto3"; + +package {{ .Proto.Package }}; + +option go_package = "{{ .PbGo.PackagePath }};{{ .PbGo.PackageName }}"; + +message {{.Name}} { + string id = 1; +} diff --git a/cmd/grapi-gen-type/template/gen.go b/cmd/grapi-gen-type/template/gen.go new file mode 100644 index 00000000..ba18632b --- /dev/null +++ b/cmd/grapi-gen-type/template/gen.go @@ -0,0 +1,3 @@ +//go:generate statik -src ./_data -dest .. -p template -f -m + +package template diff --git a/cmd/grapi-gen-type/template/statik.go b/cmd/grapi-gen-type/template/statik.go new file mode 100644 index 00000000..02b3003a --- /dev/null +++ b/cmd/grapi-gen-type/template/statik.go @@ -0,0 +1,13 @@ +// Code generated by statik. DO NOT EDIT. + +// Package statik contains static assets. +package template + +import ( + "github.com/rakyll/statik/fs" +) + +func init() { + data := "PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1f\x00 \x00api/protos/{{.Path}}.proto.tmplUT\x05\x00\x01\x80Cm8\\\xcd=\xaaB1\x10\xc5\xf1~Vq\xc8\x02\x02\x8fW\x86\xd4v\x92\x1dH\xd4K\x0cr3\xe1f\n%\xcc\xdee\xfc*l\xff\xfc8g\xdc\x9b\xe4\x1b\"\\\xdfX\xf8\xdf\x05\xa2\x9eO\xd7\\\x16\xcc \x9f\xac\xfa\xf4.\xaa\x81\x88\xbbTn(|\xf8\xc0\x08\xf7\xb4\xc7\xdd\x97\xa6,\x17\xe3\xbf}\x9fW\x9b\xb1\x9bu\x19\xe3u\xe3\xad\xaab\x120d\xab\xad\xa0\x9e\x11\xf1\x17H\xe9\x11\x00\x00\xff\xffPK\x07\x08O\x08A4v\x00\x00\x00\xa2\x00\x00\x00PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(O\x08A4v\x00\x00\x00\xa2\x00\x00\x00\x1f\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x00\x00\x00\x00api/protos/{{.Path}}.proto.tmplUT\x05\x00\x01\x80Cm8PK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x00V\x00\x00\x00\xcc\x00\x00\x00\x00\x00" + fs.Register(data) +} diff --git a/cmd/grapi/consts.go b/cmd/grapi/consts.go new file mode 100644 index 00000000..fceaecf8 --- /dev/null +++ b/cmd/grapi/consts.go @@ -0,0 +1,12 @@ +package main + +const ( + name = "grapi" + version = "v0.5.0" +) + +var ( + // set via ldflags + revision string + buildDate string +) diff --git a/cmd/grapi/main.go b/cmd/grapi/main.go index 5bd9c466..6315dbdd 100644 --- a/cmd/grapi/main.go +++ b/cmd/grapi/main.go @@ -2,55 +2,32 @@ package main import ( "fmt" - "io" "os" - "runtime" + "github.com/izumin5210/clig/pkg/clib" + + "github.com/izumin5210/grapi/pkg/cli" "github.com/izumin5210/grapi/pkg/grapicmd" "github.com/izumin5210/grapi/pkg/grapicmd/cmd" - "github.com/mattn/go-colorable" -) - -var ( - name string - version string - revision string - buildDate string - - releaseType string - - inReader = os.Stdin - outWriter io.Writer = os.Stdout - errWriter io.Writer = os.Stderr ) -func init() { - if runtime.GOOS == "windows" { - outWriter = colorable.NewColorableStdout() - errWriter = colorable.NewColorableStderr() - } - if name == "" { - name = "grapi" - } -} - func main() { cwd, err := os.Getwd() if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } - err = cmd.NewGrapiCommand(grapicmd.NewConfig( - cwd, - name, - version, - revision, - buildDate, - releaseType, - inReader, - outWriter, - errWriter, - )).Execute() + + err = cmd.NewGrapiCommand(&grapicmd.Ctx{ + IO: clib.Stdio(), + RootDir: cli.RootDir{clib.Path(cwd)}, + Build: clib.Build{ + AppName: name, + Version: version, + Revision: revision, + BuildDate: buildDate, + }, + }).Execute() if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) diff --git a/go.mod b/go.mod new file mode 100644 index 00000000..2db7d1ab --- /dev/null +++ b/go.mod @@ -0,0 +1,38 @@ +module github.com/izumin5210/grapi + +go 1.12 + +require ( + github.com/bradleyjkemp/cupaloy/v2 v2.5.0 + github.com/fatih/color v1.9.0 + github.com/golang/mock v1.4.3 + github.com/golang/protobuf v1.3.2 + github.com/google/go-cmp v0.5.2 + github.com/google/wire v0.2.1 + github.com/grpc-ecosystem/go-grpc-middleware v1.2.0 + github.com/grpc-ecosystem/grpc-gateway v1.9.0 + github.com/izumin5210/clig v0.3.1 + github.com/izumin5210/execx v0.1.0 + github.com/izumin5210/gex v0.6.0 + github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a + github.com/onsi/gomega v1.4.2 // indirect + github.com/pkg/errors v0.9.1 + github.com/rakyll/statik v0.1.7 + github.com/serenize/snaker v0.0.0-20171204205717-a683aaf2d516 + github.com/soheilhy/cmux v0.1.4 + github.com/spf13/afero v1.2.2 + github.com/spf13/cobra v0.0.6 + github.com/spf13/pflag v1.0.5 + github.com/spf13/viper v1.4.0 + github.com/srvc/appctx v0.1.0 + github.com/tcnksm/go-input v0.0.0-20180404061846-548a7d7a8ee8 + go.uber.org/zap v1.10.0 + golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586 // indirect + golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 + golang.org/x/sync v0.0.0-20190423024810-112230192c58 + golang.org/x/sys v0.0.0-20191118133127-cf1e2d577169 // indirect + golang.org/x/text v0.3.2 // indirect + golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74 // indirect + google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 + google.golang.org/grpc v1.21.0 +) diff --git a/go.sum b/go.sum new file mode 100644 index 00000000..f804d195 --- /dev/null +++ b/go.sum @@ -0,0 +1,305 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/Songmu/wrapcommander v0.1.0 h1:y8/yk9/PHT983weH+ehZIOJ7JtwAlI1AkfUpUNCj1SY= +github.com/Songmu/wrapcommander v0.1.0/go.mod h1:EC2y4OnN8PkdMnaCwcSzItewq+f0yqUvS30kcS4vmn0= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/bradleyjkemp/cupaloy/v2 v2.5.0 h1:XI37Pqyl+msFaJDYL3JuPFKGUgnVxyJp+gQZQGiz2nA= +github.com/bradleyjkemp/cupaloy/v2 v2.5.0/go.mod h1:TD5UU0rdYTbu/TtuwFuWrtiRARuN7mtRipvs/bsShSE= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.9.0 h1:8xPHl4/q1VyqGIPif1F+1V3Y3lSmrq01EabUW3CoW5s= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.1 h1:Xye71clBPdm5HgqGwUkwhbynsUJZhDbS20FvLhQ2izg= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/wire v0.2.1 h1:TYj4Z2qjqxa2ufb34UJqVeO9aznL+i0fLO6TqThKZ7Y= +github.com/google/wire v0.2.1/go.mod h1:ptBl5bWD3nzmJHVNwYHV3v4wdtKzBMlU2YbtKQCG9GI= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 h1:Iju5GlWwrvL6UBg4zJJt3btmonfrMlCDdsejg4CZE7c= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.2.0 h1:0IKlLyQ3Hs9nDaiK5cSHAGmcQEIC8l2Ts1u6x5Dfrqg= +github.com/grpc-ecosystem/go-grpc-middleware v1.2.0/go.mod h1:mJzapYve32yjrKlk9GbyCZHuPgZsrbyIbyKhSzOpg6s= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.8.5 h1:2+KSC78XiO6Qy0hIjfc1OD9H+hsaJdJlb8Kqsd41CTE= +github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/iancoleman/strcase v0.0.0-20180726023541-3605ed457bf7 h1:ux/56T2xqZO/3cP1I2F86qpeoYPCOzk+KF/UH/Ar+lk= +github.com/iancoleman/strcase v0.0.0-20180726023541-3605ed457bf7/go.mod h1:SK73tn/9oHe+/Y0h39VT4UCxmurVJkR5NA7kMEAOgSE= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/izumin5210/clig v0.3.1 h1:96ZR57e/ejdvZ9PmB2a3hFNxRNIiuZizspqSuOfsEYM= +github.com/izumin5210/clig v0.3.1/go.mod h1:mrmoiGnWeMPZjnlRYF1MJTPCWc/aEXyAIzdLE9udh+Y= +github.com/izumin5210/execx v0.1.0 h1:QLtjHrG2nZmb/kvJ8/fXAlGcp+ECAfsWha+Yx2R6Czs= +github.com/izumin5210/execx v0.1.0/go.mod h1:qrIQVE0XTXWXoNpeA+OWZgAjpBxiztOKHfHpPIzc5t0= +github.com/izumin5210/gex v0.6.0 h1:p79JHia3LRwC1M4sheTBZxSOJXubsW8jyWIhI60NMMs= +github.com/izumin5210/gex v0.6.0/go.mod h1:nDH4zt3QvsIJipyYDoedHcNrNzawwkseKfpqGTUDN2Q= +github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a h1:eeaG9XMUvRBYXJi4pg1ZKM7nxc5AfXfojeLLW7O5J3k= +github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU= +github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg= +github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= +github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.2 h1:3mYCb7aPxS/RU7TI1y4rkEn1oKmPRjNJLNEXgw7MH2I= +github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rakyll/statik v0.1.6 h1:uICcfUXpgqtw2VopbIncslhAmE5hwc4g20TEyEENBNs= +github.com/rakyll/statik v0.1.6/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs= +github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ= +github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/serenize/snaker v0.0.0-20171204205717-a683aaf2d516 h1:ofR1ZdrNSkiWcMsRrubK9tb2/SlZVWttAfqUjJi6QYc= +github.com/serenize/snaker v0.0.0-20171204205717-a683aaf2d516/go.mod h1:Yow6lPLSAXx2ifx470yD/nUe22Dv5vBvxK/UK9UUTVs= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.1/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s= +github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= +github.com/spf13/cobra v0.0.6 h1:breEStsVwemnKh2/s6gMvSdMEkwW0sK8vGStnlVBMCs= +github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.2 h1:Fy0orTDgHdbnzHcsOgfCN4LtHf0ec3wwtiwJqwvf3Gc= +github.com/spf13/pflag v1.0.2/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/srvc/appctx v0.1.0 h1:x44Sw4dU567bIWMl5U70XxBrSxxJGzu2DI2zcaljdCA= +github.com/srvc/appctx v0.1.0/go.mod h1:CrplkHjXk2n2wl7wKlDIX5mZdrch9feYZlu32ubJvPA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/tcnksm/go-input v0.0.0-20180404061846-548a7d7a8ee8 h1:RB0v+/pc8oMzPsN97aZYEwNuJ6ouRJ2uhjxemJ9zvrY= +github.com/tcnksm/go-input v0.0.0-20180404061846-548a7d7a8ee8/go.mod h1:IlWNj9v/13q7xFbaK4mbyzMNwrZLaWSHx/aibKIZuIg= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.uber.org/atomic v1.2.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.9.1 h1:XCJQEf3W6eZaVwhRBof6ImoYGJSITeKWsyeh3HFu/5o= +go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586 h1:7KByu05hhLed2MO29w7p1XfZvZ13m8mub3shuVftRs0= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3 h1:x/bBzNauLQAlE3fLku/xy92Y8QwKX5HZymrMz2IiKFc= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3 h1:eH6Eip3UpmR+yM/qI9Ijluzb1bNv/cAU/n+6l8tRSis= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8 h1:YoY1wS6JYVRpIfFngRf2HHo9R9dAne3xbkGOQ5rJXjU= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191018095205-727590c5006e h1:ZtoklVMHQy6BFRHkbG6JzK+S6rX82//Yeok1vMlizfQ= +golang.org/x/sys v0.0.0-20191018095205-727590c5006e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191118133127-cf1e2d577169 h1:LPLFLulk2vyM7yI3CwNW64O6e8AxBmr9opfv14yI7HI= +golang.org/x/sys v0.0.0-20191118133127-cf1e2d577169/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181017214349-06f26fdaaa28/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74 h1:4cFkmztxtMslUX2SctSl+blCyXfpzhGOy9LhKAqSMA4= +golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.21.0 h1:G+97AoqBnmZIT91cLG/EkCoK9NSelj64P8bOHHNmGn0= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099 h1:XJP7lxbSxWLOMNdBE4B/STaqVy6L73o0knwj2vIlxnw= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +k8s.io/utils v0.0.0-20181115163542-0d26856f57b3/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0= +rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/pkg/cli/nop_ui.go b/pkg/cli/nop_ui.go new file mode 100644 index 00000000..cc90c51a --- /dev/null +++ b/pkg/cli/nop_ui.go @@ -0,0 +1,12 @@ +package cli + +var NopUI = &nopUIImpl{} + +type nopUIImpl struct{} + +func (*nopUIImpl) Section(msg string) {} +func (*nopUIImpl) Subsection(msg string) {} +func (*nopUIImpl) ItemSuccess(msg string) {} +func (*nopUIImpl) ItemSkipped(msg string) {} +func (*nopUIImpl) ItemFailure(msg string, errs ...error) {} +func (*nopUIImpl) Confirm(msg string) (bool, error) { return true, nil } diff --git a/pkg/cli/types.go b/pkg/cli/types.go new file mode 100644 index 00000000..ab4fe66d --- /dev/null +++ b/pkg/cli/types.go @@ -0,0 +1,15 @@ +package cli + +import ( + "github.com/izumin5210/clig/pkg/clib" +) + +// RootDir represents a project root directory. +type RootDir struct { + clib.Path +} + +// BinDir returns the directory path contains executable binaries. +func (d *RootDir) BinDir() clib.Path { + return d.Join("bin") +} diff --git a/pkg/cli/ui.go b/pkg/cli/ui.go new file mode 100644 index 00000000..e7210b51 --- /dev/null +++ b/pkg/cli/ui.go @@ -0,0 +1,194 @@ +package cli + +import ( + "fmt" + "io" + "strconv" + "strings" + "sync" + + "github.com/fatih/color" + "github.com/izumin5210/clig/pkg/clib" + "github.com/pkg/errors" + "github.com/tcnksm/go-input" + "go.uber.org/zap" +) + +// UI is an interface for intaracting with the terminal. +type UI interface { + Section(msg string) + Subsection(msg string) + ItemSuccess(msg string) + ItemSkipped(msg string) + ItemFailure(msg string, errs ...error) + Confirm(msg string) (bool, error) +} + +var ( + ui UI + uiMu sync.Mutex +) + +// UIInstance retuens a singleton UI instance. +func UIInstance(io *clib.IO) UI { + uiMu.Lock() + defer uiMu.Unlock() + if ui == nil { + ui = NewUI(io) + } + return ui +} + +// NewUI creates a new UI instance. +func NewUI(io *clib.IO) UI { + return &uiImpl{ + out: io.Out, + inputUI: &input.UI{ + Reader: io.In, + Writer: io.Out, + }, + } +} + +type uiImpl struct { + out io.Writer + inSection bool + inputUI *input.UI +} + +func (u *uiImpl) Section(msg string) { + if u.inSection { + fmt.Fprintln(u.out) + u.inSection = false + } + printTypeSection.Fprintln(u.out, msg) +} + +func (u *uiImpl) Subsection(msg string) { + if u.inSection { + fmt.Fprintln(u.out) + u.inSection = false + } + printTypeSubsection.Fprintln(u.out, msg) +} + +func (u *uiImpl) ItemSuccess(msg string) { + u.inSection = true + printTypeItemSuccess.Fprintln(u.out, msg) +} + +func (u *uiImpl) ItemSkipped(msg string) { + u.inSection = true + printTypeItemSkipped.Fprintln(u.out, msg) +} + +func (u *uiImpl) ItemFailure(msg string, errs ...error) { + u.inSection = true + printTypeItemFailure.Fprintln(u.out, msg) + cfg := configByPrintType[printTypeItemFailure] + pad := strings.Repeat(" ", cfg.indent+4) + fprintln := color.New(color.FgRed).FprintlnFunc() + for _, err := range errs { + for _, s := range strings.Split(err.Error(), "\n") { + fprintln(u.out, pad+s) + } + } +} + +func (u *uiImpl) Confirm(msg string) (bool, error) { + ans, err := u.inputUI.Ask(fmt.Sprintf("%s [Y/n]", msg), &input.Options{ + HideOrder: true, + Loop: true, + ValidateFunc: func(ans string) error { + zap.L().Debug("receive user input", zap.String("query", msg), zap.String("input", ans)) + if ans != "Y" && ans != "n" { + return fmt.Errorf("input must be Y or n") + } + return nil + }, + }) + if err != nil { + return false, errors.WithStack(err) + } + return ans == "Y", nil +} + +type fprintFunc func(w io.Writer, msg string) + +type printConfig struct { + prefix string + colorAttrs []color.Attribute + indent int + allColor bool +} + +type printType int + +const ( + printTypeUnknown printType = iota + printTypeSection + printTypeSubsection + printTypeItemSuccess + printTypeItemSkipped + printTypeItemFailure +) + +const ( + indentSizeItem = 4 +) + +var ( + configByPrintType = map[printType]printConfig{ + printTypeSection: { + prefix: "โžœ", + colorAttrs: []color.Attribute{color.FgYellow}, + allColor: true, + }, + printTypeSubsection: { + prefix: "โ–ธ", + colorAttrs: []color.Attribute{color.FgBlue}, + allColor: true, + }, + printTypeItemSuccess: { + prefix: "โœ”", + colorAttrs: []color.Attribute{color.Bold, color.FgGreen}, + indent: indentSizeItem, + }, + printTypeItemSkipped: { + prefix: "โ•Œ", + colorAttrs: []color.Attribute{color.Bold, color.FgBlue}, + indent: indentSizeItem, + }, + printTypeItemFailure: { + prefix: "โœ—", + colorAttrs: []color.Attribute{color.Bold, color.FgRed}, + indent: indentSizeItem, + }, + } + fprintlnFuncByPrintType = map[printType]fprintFunc{} +) + +func init() { + for pt, cfg := range configByPrintType { + cfg := cfg + fmtStr := "%s" + if cfg.indent > 0 { + fmtStr = "%" + strconv.FormatInt(int64(cfg.indent), 10) + "s" + } + if cfg.allColor { + colored := color.New(cfg.colorAttrs...).FprintfFunc() + fprintlnFuncByPrintType[pt] = func(w io.Writer, msg string) { + colored(w, " "+fmtStr+" %s\n", cfg.prefix, msg) + } + } else { + prefix := color.New(cfg.colorAttrs...).Sprintf(fmtStr, cfg.prefix) + fprintlnFuncByPrintType[pt] = func(w io.Writer, msg string) { + fmt.Fprintf(w, " %s %s\n", prefix, msg) + } + } + } +} + +func (pt printType) Fprintln(w io.Writer, msg string) { + fprintlnFuncByPrintType[pt](w, msg) +} diff --git a/pkg/grapicmd/internal/module/ui/ui_test.go b/pkg/cli/ui_test.go similarity index 54% rename from pkg/grapicmd/internal/module/ui/ui_test.go rename to pkg/cli/ui_test.go index a6fd2254..cb6b3970 100644 --- a/pkg/grapicmd/internal/module/ui/ui_test.go +++ b/pkg/cli/ui_test.go @@ -1,4 +1,4 @@ -package ui +package cli_test import ( "bytes" @@ -6,9 +6,49 @@ import ( "strings" "testing" - "github.com/izumin5210/grapi/pkg/grapicmd/internal/module" + "github.com/fatih/color" + "github.com/izumin5210/clig/pkg/clib" + + "github.com/izumin5210/grapi/pkg/cli" ) +func TestUI(t *testing.T) { + defer func(b bool) { color.NoColor = b }(color.NoColor) + color.NoColor = true + + want := ` โžœ section 1 + โ–ธ subsection 1.1 + โœ” created + โ•Œ skipped + โœ” ok + + โ–ธ subsection 1.2 + โœ— failure + foobar + baz + + โžœ section 2 + โœ— fail!!! +` + + io := clib.NewBufferedIO() + ui := cli.NewUI(io) + + ui.Section("section 1") + ui.Subsection("subsection 1.1") + ui.ItemSuccess("created") + ui.ItemSkipped("skipped") + ui.ItemSuccess("ok") + ui.Subsection("subsection 1.2") + ui.ItemFailure("failure", errors.New("foobar"), errors.New("baz")) + ui.Section("section 2") + ui.ItemFailure("fail!!!") + + if got := io.Out.(*bytes.Buffer).String(); got != want { + t.Errorf("got:\n%s\nwant:\n%s", got, want) + } +} + type errReader struct { } @@ -16,19 +56,17 @@ func (r *errReader) Read(p []byte) (n int, err error) { return 0, errors.New("failed to read") } -func Test_UI_Confirm(t *testing.T) { +func TestUI_Confirm(t *testing.T) { type TestContext struct { - in, out, err *bytes.Buffer - ui module.UI + io *clib.IO + ui cli.UI } createTestContext := func() *TestContext { - in := new(bytes.Buffer) - out := new(bytes.Buffer) + io := clib.NewBufferedIO() return &TestContext{ - in: in, - out: out, - ui: New(out, in), + io: io, + ui: cli.NewUI(io), } } @@ -67,16 +105,16 @@ func Test_UI_Confirm(t *testing.T) { for _, c := range cases { t.Run(c.test, func(t *testing.T) { ctx := createTestContext() - ctx.in.WriteString(c.input) + ctx.io.In.(*bytes.Buffer).WriteString(c.input) ok, err := ctx.ui.Confirm(c.test) - if got, want := ctx.out.String(), c.test; !strings.HasPrefix(got, want) { + if got, want := ctx.io.Out.(*bytes.Buffer).String(), c.test; !strings.HasPrefix(got, want) { t.Errorf("Confirm() wrote %q, want %q", got, want) } wantErrMsg := "input must be Y or n\n" - if got, want := strings.Count(ctx.out.String(), wantErrMsg), c.errMsgCnt; got != want { + if got, want := strings.Count(ctx.io.Out.(*bytes.Buffer).String(), wantErrMsg), c.errMsgCnt; got != want { t.Errorf("Confirm() wrote %q as error %d times, want %d times", wantErrMsg, got, want) } @@ -91,7 +129,7 @@ func Test_UI_Confirm(t *testing.T) { } t.Run("when failed to read", func(t *testing.T) { - ui := New(new(bytes.Buffer), &errReader{}) + ui := cli.NewUI(clib.NewIO(&errReader{}, new(bytes.Buffer), new(bytes.Buffer))) ok, err := ui.Confirm("test") diff --git a/pkg/gencmd/app.go b/pkg/gencmd/app.go new file mode 100644 index 00000000..e403a12c --- /dev/null +++ b/pkg/gencmd/app.go @@ -0,0 +1,12 @@ +package gencmd + +import "github.com/izumin5210/grapi/pkg/cli" + +// CreateAppFunc initializes dependencies. +type CreateAppFunc func(*Command) (*App, error) + +// App contains dependencies to execute a generator. +type App struct { + Generator Generator + UI cli.UI +} diff --git a/pkg/gencmd/command.go b/pkg/gencmd/command.go new file mode 100644 index 00000000..15309ae5 --- /dev/null +++ b/pkg/gencmd/command.go @@ -0,0 +1,89 @@ +package gencmd + +import ( + "github.com/pkg/errors" + "github.com/spf13/cobra" + "github.com/spf13/pflag" +) + +// Command represents a subcommand of a generator plugin. It will be converted to a *cobra.Command object internally. +type Command struct { + // Use, Short, Long, Example and Args are pass-through into *cobra.Command object. + Use string + Short string + Long string + Example string + Args cobra.PositionalArgs + + // BuildParams returns parameters to generate/destroy files(required). + BuildParams func(c *Command, args []string) (interface{}, error) + + // PreRun is executed in *cobra.Command.PreRunE. + PreRun func(c *Command, args []string) error + + // PostRun is executed in *cobra.Command.PostRunE. + PostRun func(c *Command, args []string) error + + // ShouldRun is executed for each generated files. When it returns false, the file will be skipped. + ShouldRun ShouldRunFunc + + // ShouldInsideApp will disable the command when a current working directory is not inside of a grapi project. + ShouldInsideApp bool + + flags *pflag.FlagSet + ctx *Ctx +} + +// Flags returns a FlagSet that applies to this commmand. +func (c *Command) Flags() *pflag.FlagSet { + if c.flags == nil { + c.flags = new(pflag.FlagSet) + } + return c.flags +} + +// Ctx returns the context object. +func (c *Command) Ctx() *Ctx { + return c.ctx +} + +func (c *Command) newCobraCommand() *cobra.Command { + cc := &cobra.Command{ + Use: c.Use, + Short: c.Short, + Long: c.Long, + Example: c.Example, + Args: c.Args, + PreRunE: func(_ *cobra.Command, args []string) error { + if c.ShouldInsideApp && !c.Ctx().IsInsideApp() { + return errors.New("should execute inside grapi project") + } + if c.PreRun != nil { + err := c.PreRun(c, args) + if err != nil { + return errors.WithStack(err) + } + } + return nil + }, + } + if c.PostRun != nil { + cc.PostRunE = func(_ *cobra.Command, args []string) error { return c.PostRun(c, args) } + } + cc.PersistentFlags().AddFlagSet(c.Flags()) + return cc +} + +// File represents a file content. +type File struct { + Path string + Body string +} + +// Entry represents a file that will be generated. +type Entry struct { + File + Template File +} + +type ShouldRunFunc func(e *Entry) bool diff --git a/pkg/gencmd/context.go b/pkg/gencmd/context.go new file mode 100644 index 00000000..272701af --- /dev/null +++ b/pkg/gencmd/context.go @@ -0,0 +1,38 @@ +package gencmd + +import ( + "github.com/izumin5210/grapi/pkg/grapicmd" + "github.com/pkg/errors" +) + +func defaultCtx() *Ctx { + return &Ctx{ + Ctx: &grapicmd.Ctx{}, + } +} + +// Ctx defines a context of a generator. +type Ctx struct { + *grapicmd.Ctx + + CreateAppFunc CreateAppFunc +} + +func (c *Ctx) apply(opts []Option) { + for _, f := range opts { + f(c) + } +} + +// CreateApp initializes dependencies. +func (c *Ctx) CreateApp(cmd *Command) (*App, error) { + f := c.CreateAppFunc + if c.CreateAppFunc == nil { + f = newApp + } + app, err := f(cmd) + if err != nil { + return nil, errors.WithStack(err) + } + return app, nil +} diff --git a/pkg/gencmd/executor.go b/pkg/gencmd/executor.go new file mode 100644 index 00000000..5d10017f --- /dev/null +++ b/pkg/gencmd/executor.go @@ -0,0 +1,48 @@ +package gencmd + +import ( + "fmt" + "os" + + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +type Executor interface { + Command() *cobra.Command + Execute() error + MustExecute() +} + +func newExecutor(ctx *Ctx, cmd *cobra.Command) Executor { + return &executorImpl{ctx: ctx, cmd: cmd} +} + +type executorImpl struct { + ctx *Ctx + cmd *cobra.Command +} + +func (c *executorImpl) Command() *cobra.Command { + return c.cmd +} + +func (c *executorImpl) Execute() error { + err := c.ctx.Init() + if err != nil { + return errors.Wrap(err, "failed to initialize context") + } + + return errors.WithStack(c.cmd.Execute()) +} + +func (c *executorImpl) MustExecute() { + var code int + + if err := c.Execute(); err != nil { + fmt.Fprintln(os.Stderr, err) + code = 1 + } + + os.Exit(code) +} diff --git a/pkg/gencmd/generator.go b/pkg/gencmd/generator.go new file mode 100644 index 00000000..fcab9f29 --- /dev/null +++ b/pkg/gencmd/generator.go @@ -0,0 +1,208 @@ +package gencmd + +import ( + "net/http" + "os" + "path/filepath" + "strings" + + "github.com/pkg/errors" + "github.com/rakyll/statik/fs" + "github.com/spf13/afero" + "go.uber.org/zap" + + "github.com/izumin5210/clig/pkg/clib" + "github.com/izumin5210/grapi/pkg/cli" +) + +type Generator interface { + Generate(params interface{}) error + Destroy(params interface{}) error +} + +func NewGenerator( + fs afero.Fs, + ui cli.UI, + outDir clib.Path, + templateFS http.FileSystem, + shouldRunFunc ShouldRunFunc, +) Generator { + return &generatorImpl{ + fs: fs, + ui: ui, + outDir: outDir, + templateFS: templateFS, + shouldRunFunc: shouldRunFunc, + } +} + +type generatorImpl struct { + fs afero.Fs + ui cli.UI + + outDir clib.Path + + templateFS http.FileSystem + shouldRunFunc ShouldRunFunc +} + +func (g *generatorImpl) Generate(params interface{}) error { + entries, err := g.listEntries(params) + if err != nil { + return errors.WithStack(err) + } + + for _, e := range entries { + if ok, err := g.shouldRun(e); err != nil { + g.ui.ItemFailure(e.Path[1:]) + return errors.WithStack(err) + } else if !ok { + g.ui.ItemSkipped(e.Path[1:]) + continue + } + + err := g.writeFile(e) + if err != nil { + g.ui.ItemFailure(e.Path[1:]) + return errors.WithStack(err) + } + g.ui.ItemSuccess(e.Path[1:]) + } + + return nil +} + +func (g *generatorImpl) Destroy(params interface{}) error { + tmplPaths, err := g.listPathTemplates() + if err != nil { + return errors.WithStack(err) + } + + for _, tmplPath := range tmplPaths { + path, err := TemplateString(strings.TrimSuffix(tmplPath, ".tmpl")).Compile(params) + if err != nil { + return errors.Wrapf(err, "failed to parse path: %s", tmplPath) + } + + absPath := g.outDir.Join(path).String() + if ok, err := afero.Exists(g.fs, absPath); err != nil { + g.ui.ItemFailure(path) + return errors.WithStack(err) + } else if !ok { + g.ui.ItemSkipped(path) + continue + } + + err = g.fs.Remove(absPath) + if err != nil { + g.ui.ItemFailure(path) + return errors.WithStack(err) + } + g.ui.ItemSuccess(path) + } + + return nil +} + +func (g *generatorImpl) listEntries(params interface{}) ([]*Entry, error) { + tmplPaths, err := g.listPathTemplates() + if err != nil { + return nil, errors.WithStack(err) + } + + entries := make([]*Entry, 0, len(tmplPaths)) + + for _, tmplPath := range tmplPaths { + path, err := TemplateString(strings.TrimSuffix(tmplPath, ".tmpl")).Compile(params) + if err != nil { + return nil, errors.Wrapf(err, "failed to parse path: %s", tmplPath) + } + + data, err := fs.ReadFile(g.templateFS, tmplPath) + if err != nil { + return nil, errors.Wrapf(err, "failed to read template: %s", tmplPath) + } + + body, err := TemplateString(string(data)).Compile(params) + if err != nil { + return nil, errors.Wrapf(err, "failed to compile temlpate: %s, %v", tmplPath, params) + } + + entries = append(entries, &Entry{File: File{Path: filepath.Clean(path), Body: body}, Template: File{Path: tmplPath, Body: string(data)}}) + } + return entries, nil +} + +func (g *generatorImpl) listPathTemplates() (tmplPaths []string, err error) { + err = fs.Walk(g.templateFS, "/", func(path string, info os.FileInfo, err error) error { + if err != nil { + return errors.WithStack(err) + } + + if info.IsDir() { + return nil + } + + tmplPaths = append(tmplPaths, path) + + return nil + }) + + err = errors.WithStack(err) + + return +} + +func (g *generatorImpl) shouldRun(e *Entry) (bool, error) { + if g.shouldRunFunc != nil && !g.shouldRunFunc(e) { + return false, nil + } + + absPath := g.outDir.Join(e.Path).String() + + if ok, err := afero.Exists(g.fs, absPath); err != nil { + return false, errors.WithStack(err) + } else if !ok { + return true, nil + } + + existed, err := afero.ReadFile(g.fs, absPath) + if err != nil { + return false, errors.WithStack(err) + } + + if string(existed) == e.Body { + return false, nil + } + + g.ui.ItemFailure(e.Path[1:] + " is conflicted.") + if ok, err := g.ui.Confirm("Overwite it?"); err != nil { + zap.L().Error("failed to confirm to apply", zap.Error(err)) + return false, errors.WithStack(err) + } else if ok { + return true, nil + } + + return false, nil +} + +func (g *generatorImpl) writeFile(e *Entry) error { + path := g.outDir.Join(e.Path).String() + dir := filepath.Dir(path) + + if ok, err := afero.DirExists(g.fs, dir); err != nil { + return errors.WithStack(err) + } else if !ok { + err := g.fs.MkdirAll(dir, 0755) + if err != nil { + return errors.Wrapf(err, "failed to create directory") + } + } + + err := afero.WriteFile(g.fs, path, []byte(e.Body), 0644) + if err != nil { + return errors.Wrapf(err, "failed to write %s", e.Path) + } + + return nil +} diff --git a/pkg/gencmd/main.go b/pkg/gencmd/main.go new file mode 100644 index 00000000..4f5d4a5b --- /dev/null +++ b/pkg/gencmd/main.go @@ -0,0 +1,96 @@ +package gencmd + +import ( + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +// New creates an Executor instnace. +func New( + name string, + generateCmd *Command, + destroyCmd *Command, + opts ...Option, +) Executor { + ctx := defaultCtx() + ctx.apply(opts) + + rootCmd := &cobra.Command{ + Use: "grapi-gen-" + name, + } + + setGenerateCommand(name, rootCmd, generateCmd, ctx) + setDestroyCommand(name, rootCmd, destroyCmd, ctx) + + return newExecutor(ctx, rootCmd) +} + +func setGenerateCommand(name string, rootCmd *cobra.Command, cmd *Command, ctx *Ctx) { + if cmd == nil { + return + } + + ccmd := cmd.newCobraCommand() + + ccmd.RunE = func(_ *cobra.Command, args []string) error { + app, err := ctx.CreateApp(cmd) + if err != nil { + return errors.WithStack(err) + } + + app.UI.Section("Generate " + name) + params, err := cmd.BuildParams(cmd, args) + if err != nil { + return errors.WithStack(err) + } + + err = app.Generator.Generate(params) + if err != nil { + return errors.WithStack(err) + } + + return nil + } + + if ccmd.Use == "" { + ccmd.Use = "generate" + } + + cmd.ctx = ctx + rootCmd.AddCommand(ccmd) +} + +func setDestroyCommand(name string, rootCmd *cobra.Command, cmd *Command, ctx *Ctx) { + if cmd == nil { + return + } + + ccmd := cmd.newCobraCommand() + + ccmd.RunE = func(_ *cobra.Command, args []string) error { + app, err := ctx.CreateApp(cmd) + if err != nil { + return errors.WithStack(err) + } + + app.UI.Section("Destroy " + name) + params, err := cmd.BuildParams(cmd, args) + if err != nil { + return errors.WithStack(err) + } + + err = app.Generator.Destroy(params) + if err != nil { + return errors.WithStack(err) + } + + return nil + } + + if ccmd.Use == "" { + ccmd.Use = "destroy" + } + + cmd.ctx = ctx + rootCmd.AddCommand(ccmd) +} diff --git a/pkg/gencmd/options.go b/pkg/gencmd/options.go new file mode 100644 index 00000000..fefdbd6d --- /dev/null +++ b/pkg/gencmd/options.go @@ -0,0 +1,22 @@ +package gencmd + +import ( + "github.com/izumin5210/grapi/pkg/grapicmd" +) + +// Option configures a command context. +type Option func(*Ctx) + +// WithGrapiCtx specifies a grapi command context. +func WithGrapiCtx(gctx *grapicmd.Ctx) Option { + return func(ctx *Ctx) { + ctx.Ctx = gctx + } +} + +// WithCreateAppFunc specifies a dependencies initializer. +func WithCreateAppFunc(f CreateAppFunc) Option { + return func(ctx *Ctx) { + ctx.CreateAppFunc = f + } +} diff --git a/pkg/gencmd/providers.go b/pkg/gencmd/providers.go new file mode 100644 index 00000000..9d08b4bb --- /dev/null +++ b/pkg/gencmd/providers.go @@ -0,0 +1,27 @@ +package gencmd + +import ( + "github.com/google/wire" + "github.com/rakyll/statik/fs" + + "github.com/izumin5210/clig/pkg/clib" + "github.com/izumin5210/grapi/pkg/cli" + "github.com/izumin5210/grapi/pkg/grapicmd" +) + +func ProvideGrapiCtx(ctx *Ctx) *grapicmd.Ctx { return ctx.Ctx } +func ProvideCtx(cmd *Command) *Ctx { return cmd.Ctx() } +func ProvideShouldRun(cmd *Command) ShouldRunFunc { return cmd.ShouldRun } +func ProvidePath(root cli.RootDir) clib.Path { return root.Path } + +// Set contains providers for DI. +var Set = wire.NewSet( + grapicmd.CtxSet, + fs.New, + ProvideGrapiCtx, + ProvideCtx, + ProvideShouldRun, + ProvidePath, + NewGenerator, + App{}, +) diff --git a/pkg/grapicmd/internal/module/generator/template_string.go b/pkg/gencmd/template_string.go similarity index 97% rename from pkg/grapicmd/internal/module/generator/template_string.go rename to pkg/gencmd/template_string.go index d54ca8bf..1fc0eddf 100644 --- a/pkg/grapicmd/internal/module/generator/template_string.go +++ b/pkg/gencmd/template_string.go @@ -1,4 +1,4 @@ -package generator +package gencmd import ( "bytes" diff --git a/pkg/gencmd/testing/wire.go b/pkg/gencmd/testing/wire.go new file mode 100644 index 00000000..ab6bbab5 --- /dev/null +++ b/pkg/gencmd/testing/wire.go @@ -0,0 +1,17 @@ +//+build wireinject + +package testing + +import ( + "github.com/google/wire" + + "github.com/izumin5210/grapi/pkg/cli" + "github.com/izumin5210/grapi/pkg/gencmd" +) + +func NewTestApp(*gencmd.Command, cli.UI) (*gencmd.App, error) { + wire.Build( + gencmd.Set, + ) + return nil, nil +} diff --git a/pkg/gencmd/testing/wire_gen.go b/pkg/gencmd/testing/wire_gen.go new file mode 100644 index 00000000..a803ecc7 --- /dev/null +++ b/pkg/gencmd/testing/wire_gen.go @@ -0,0 +1,34 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate wire +//+build !wireinject + +package testing + +import ( + "github.com/izumin5210/grapi/pkg/cli" + "github.com/izumin5210/grapi/pkg/gencmd" + "github.com/izumin5210/grapi/pkg/grapicmd" + "github.com/rakyll/statik/fs" +) + +// Injectors from wire.go: + +func NewTestApp(command *gencmd.Command, ui cli.UI) (*gencmd.App, error) { + ctx := gencmd.ProvideCtx(command) + grapicmdCtx := gencmd.ProvideGrapiCtx(ctx) + aferoFs := grapicmd.ProvideFS(grapicmdCtx) + rootDir := grapicmd.ProvideRootDir(grapicmdCtx) + path := gencmd.ProvidePath(rootDir) + fileSystem, err := fs.New() + if err != nil { + return nil, err + } + shouldRunFunc := gencmd.ProvideShouldRun(command) + generator := gencmd.NewGenerator(aferoFs, ui, path, fileSystem, shouldRunFunc) + app := &gencmd.App{ + Generator: generator, + UI: ui, + } + return app, nil +} diff --git a/pkg/gencmd/util/package.go b/pkg/gencmd/util/package.go new file mode 100644 index 00000000..7bedf551 --- /dev/null +++ b/pkg/gencmd/util/package.go @@ -0,0 +1,76 @@ +package util + +import ( + "path/filepath" + "strings" + + "github.com/izumin5210/grapi/pkg/cli" + "github.com/izumin5210/grapi/pkg/grapicmd/util/fs" + "github.com/pkg/errors" + "github.com/serenize/snaker" +) + +type ProtoParams struct { + Proto struct { + Path string + Package string + } + PbGo struct { + Package string + ImportName string + } +} + +func BuildProtoParams(path string, rootDir cli.RootDir, protoOutDir string, pkg string) (out ProtoParams, err error) { + if protoOutDir == "" { + err = errors.New("protoOutDir is required") + return + } + + // github.com/foo/bar + importPath, err := fs.GetImportPath(rootDir.String()) + if err != nil { + err = errors.WithStack(err) + return + } + + // path => baz/qux/quux + path = strings.Replace(snaker.CamelToSnake(path), "-", "_", -1) + + // baz/qux + packagePath := filepath.Dir(path) + + // api/baz/qux + pbgoPackagePath := filepath.Join(protoOutDir, packagePath) + // qux_pb + pbgoPackageName := filepath.Base(pbgoPackagePath) + "_pb" + + if packagePath == "." { + pbgoPackagePath = protoOutDir + pbgoPackageName = filepath.Base(pbgoPackagePath) + "_pb" + } + + protoPackage := pkg + if protoPackage == "" { + protoPackageChunks := []string{} + for _, pkg := range strings.Split(filepath.ToSlash(filepath.Join(importPath, protoOutDir)), "/") { + chunks := strings.Split(pkg, ".") + for i := len(chunks) - 1; i >= 0; i-- { + protoPackageChunks = append(protoPackageChunks, chunks[i]) + } + } + // com.github.foo.bar.baz.qux + protoPackage = strings.Join(protoPackageChunks, ".") + } + if dir := filepath.Dir(path); dir != "." { + protoPackage = protoPackage + "." + strings.Replace(dir, string(filepath.Separator), ".", -1) + } + protoPackage = strings.Replace(protoPackage, "-", "_", -1) + + out.Proto.Path = path + out.Proto.Package = strings.Replace(protoPackage, "-", "_", -1) + out.PbGo.Package = filepath.ToSlash(filepath.Join(importPath, pbgoPackagePath)) + out.PbGo.ImportName = pbgoPackageName + + return +} diff --git a/pkg/gencmd/util/string.go b/pkg/gencmd/util/string.go new file mode 100644 index 00000000..97623b2a --- /dev/null +++ b/pkg/gencmd/util/string.go @@ -0,0 +1,33 @@ +package util + +import ( + "strings" + + "github.com/jinzhu/inflection" + "github.com/serenize/snaker" +) + +type String struct { + Camel struct { + Plural string + Singular string + } + CamelLower struct { + Plural string + Singular string + } + Snake struct { + Plural string + Singular string + } +} + +func Inflect(in string) (out String) { + out.Camel.Singular = inflection.Singular(snaker.SnakeToCamel(in)) + out.Camel.Plural = inflection.Plural(out.Camel.Singular) + out.CamelLower.Singular = strings.ToLower(string(out.Camel.Singular[0])) + out.Camel.Singular[1:] + out.CamelLower.Plural = strings.ToLower(string(out.Camel.Plural[0])) + out.Camel.Plural[1:] + out.Snake.Singular = snaker.CamelToSnake(out.Camel.Singular) + out.Snake.Plural = snaker.CamelToSnake(out.Camel.Plural) + return +} diff --git a/pkg/gencmd/wire.go b/pkg/gencmd/wire.go new file mode 100644 index 00000000..612b0384 --- /dev/null +++ b/pkg/gencmd/wire.go @@ -0,0 +1,16 @@ +//+build wireinject + +package gencmd + +import ( + "github.com/google/wire" + "github.com/izumin5210/grapi/pkg/cli" +) + +func newApp(*Command) (*App, error) { + wire.Build( + Set, + cli.UIInstance, + ) + return nil, nil +} diff --git a/pkg/gencmd/wire_gen.go b/pkg/gencmd/wire_gen.go new file mode 100644 index 00000000..bbd8dcb5 --- /dev/null +++ b/pkg/gencmd/wire_gen.go @@ -0,0 +1,35 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate wire +//+build !wireinject + +package gencmd + +import ( + "github.com/izumin5210/grapi/pkg/cli" + "github.com/izumin5210/grapi/pkg/grapicmd" + "github.com/rakyll/statik/fs" +) + +// Injectors from wire.go: + +func newApp(command *Command) (*App, error) { + ctx := ProvideCtx(command) + grapicmdCtx := ProvideGrapiCtx(ctx) + aferoFs := grapicmd.ProvideFS(grapicmdCtx) + io := grapicmd.ProvideIO(grapicmdCtx) + ui := cli.UIInstance(io) + rootDir := grapicmd.ProvideRootDir(grapicmdCtx) + path := ProvidePath(rootDir) + fileSystem, err := fs.New() + if err != nil { + return nil, err + } + shouldRunFunc := ProvideShouldRun(command) + generator := NewGenerator(aferoFs, ui, path, fileSystem, shouldRunFunc) + app := &App{ + Generator: generator, + UI: ui, + } + return app, nil +} diff --git a/pkg/grapicmd/cmd/build.go b/pkg/grapicmd/cmd/build.go index 034428c0..619a7aed 100644 --- a/pkg/grapicmd/cmd/build.go +++ b/pkg/grapicmd/cmd/build.go @@ -1,35 +1,68 @@ package cmd import ( + "strings" + "github.com/pkg/errors" "github.com/spf13/cobra" + "github.com/srvc/appctx" + "github.com/izumin5210/grapi/pkg/cli" "github.com/izumin5210/grapi/pkg/grapicmd" + "github.com/izumin5210/grapi/pkg/grapicmd/di" "github.com/izumin5210/grapi/pkg/grapicmd/internal/module" ) -func newBuildCommand(cfg grapicmd.Config, ui module.UI, scriptLoader module.ScriptLoader) *cobra.Command { +func splitOptions(args []string) ([]string, []string) { + pos := len(args) + for i, arg := range args { + if strings.HasPrefix(arg, "-") { + pos = i + + break + } + } + + return args[:pos], args[pos:] +} + +func newBuildCommand(ctx *grapicmd.Ctx) *cobra.Command { + scriptLoader := di.NewScriptLoader(ctx) + ui := di.NewUI(ctx) + + return newBuildCommandMocked(ctx.IsInsideApp(), ctx, scriptLoader, ui) +} + +func newBuildCommandMocked(isInsideApp bool, ctx *grapicmd.Ctx, scriptLoader module.ScriptLoader, ui cli.UI) *cobra.Command { return &cobra.Command{ - Use: "build [TARGET]... [-- BUILD_OPTIONS]", + Use: "build [flags] [TARGET]... [-- BUILD_OPTIONS]", Short: "Build commands", SilenceErrors: true, SilenceUsage: true, - RunE: func(c *cobra.Command, args []string) (err error) { - if !cfg.IsInsideApp() { + RunE: func(c *cobra.Command, args []string) error { + if !isInsideApp { return errors.New("protoc command should be execute inside a grapi application directory") } - nameSet := make(map[string]bool, len(args)) - for _, n := range args { + names, opt := splitOptions(args) + nameSet := make(map[string]bool, len(names)) + for _, n := range names { nameSet[n] = true } - isAll := len(args) == 0 + isAll := len(names) == 0 + + err := scriptLoader.Load(ctx.RootDir.Join("cmd").String()) + if err != nil { + return errors.WithStack(err) + } + + ctx := appctx.Global() for _, name := range scriptLoader.Names() { script, ok := scriptLoader.Get(name) if ok && (isAll || nameSet[script.Name()]) { ui.Subsection("Building " + script.Name()) - err := script.Build(args...) + err := script.Build(ctx, opt...) if err != nil { return errors.WithStack(err) } diff --git a/pkg/grapicmd/cmd/build_internal_test.go b/pkg/grapicmd/cmd/build_internal_test.go new file mode 100644 index 00000000..e0d54742 --- /dev/null +++ b/pkg/grapicmd/cmd/build_internal_test.go @@ -0,0 +1,185 @@ +package cmd + +import ( + "errors" + "reflect" + "testing" + + "github.com/golang/mock/gomock" + + "github.com/izumin5210/grapi/pkg/cli" + "github.com/izumin5210/grapi/pkg/grapicmd" + moduletesting "github.com/izumin5210/grapi/pkg/grapicmd/internal/module/testing" +) + +func TestSplitOptions(t *testing.T) { + tests := []struct { + input []string + outputArg []string + outputOpt []string + }{ + { + []string{"foo", "bar"}, + []string{"foo", "bar"}, + []string{}, + }, + { + []string{"foo", "-b"}, + []string{"foo"}, + []string{"-b"}, + }, + { + []string{"foo", "-b", "-h"}, + []string{"foo"}, + []string{"-b", "-h"}, + }, + { + []string{"foo", "-b", "-h"}, + []string{"foo"}, + []string{"-b", "-h"}, + }, + { + []string{"-b", "-h"}, + []string{}, + []string{"-b", "-h"}, + }, + { + []string{"foo", "-b", "-h", "ooo"}, + []string{"foo"}, + []string{"-b", "-h", "ooo"}, + }, + { + []string{}, + []string{}, + []string{}, + }, + } + + for i, test := range tests { + gotArg, gotOpt := splitOptions(test.input) + if !reflect.DeepEqual(test.outputArg, gotArg) { + t.Errorf("(%v) Expected: %v gotArg: %v", i, test.outputArg, gotArg) + } + if !reflect.DeepEqual(test.outputOpt, gotOpt) { + t.Errorf("(%v) Expected: %v gotOpt: %v", i, test.outputOpt, gotOpt) + } + } +} + +var errBuildFailed = errors.New("error occur") + +func Test_newBuildCommandMocked(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + type errBuild struct { + err error + } + + testCases := []struct { + args []string + optExpect []string + // ๅ˜ไฝ“ใฎbuildใฎ็ตๆžœ + buildResult map[string]errBuild + // ๅ…จไฝ“ใฎbuildใฎ็ตๆžœ + wantErr bool + }{ + { + // "aaa"ใ ใ‘ๆŒ‡ๅฎšใ—ใŸๅ ดๅˆ๏ผŒbuildใซใฏๆˆๅŠŸใ™ใ‚‹ + // "bbb"ใจ"ccc"ใฏๅ‘ผใฐใ‚Œใชใ„ + args: []string{"aaa"}, + optExpect: []string{}, + buildResult: map[string]errBuild{ + "aaa": {}, + }, + }, + { + // ใชใ‚“ใ‚‰ใ‹ใฎ็†็”ฑใง"bbb"ใฎbuildใŒใงใใšใซๅคฑๆ•—๏ผˆargsๆŒ‡ๅฎšใชใ—๏ผ‰ + // "ccc"ใฏbuildใ•ใ‚Œใชใ„ + args: []string{}, + optExpect: []string{}, + buildResult: map[string]errBuild{ + "aaa": {}, + "bbb": { + err: errBuildFailed, + }, + }, + wantErr: true, + }, + { + // ใชใ‚“ใ‚‰ใ‹ใฎ็†็”ฑใง"bbb"ใงใ‚จใƒฉใƒผใŒ็™บ็”Ÿใ—ใŸๅ ดๅˆ๏ผˆargsๆŒ‡ๅฎšใ‚ใ‚Š๏ผ‰ + // "ccc"ใฏๅฎŸ่กŒใ•ใ‚Œใชใ„ + args: []string{"aaa", "bbb", "ccc"}, + optExpect: []string{}, + buildResult: map[string]errBuild{ + "aaa": {}, + "bbb": { + err: errBuildFailed, + }, + }, + wantErr: true, + }, + { + // ไธŽใˆใŸใ‚ชใƒ—ใ‚ทใƒงใƒณใซใ‚ˆใฃใฆใ‚จใƒฉใƒผใŒ็™บ็”Ÿใ™ใ‚‹ๅ ดๅˆ + // "bbb"ไปฅ้™ใฏๅฎŸ่กŒใ•ใ‚Œใชใ„ + args: []string{"--", "-b", "-c"}, + optExpect: []string{"-b", "-c"}, + buildResult: map[string]errBuild{ + "aaa": { + err: errBuildFailed, + }, + }, + wantErr: true, + }, + { + // buildๅฏพ่ฑกใฏไธŽใˆใšไธŽใˆใŸใ‚ชใƒ—ใ‚ทใƒงใƒณใŒๆญฃๅฝ“ใงใ‚ใ‚Šใ™ในใฆๆˆๅŠŸใ™ใ‚‹ๅ ดๅˆ + args: []string{"--", "-a"}, + optExpect: []string{"-a"}, + buildResult: map[string]errBuild{ + "aaa": {}, + "bbb": {}, + "ccc": {}, + }, + }, + { + // buildๅฏพ่ฑกใจใ‚ชใƒ—ใ‚ทใƒงใƒณใฎไธก่€…ใ‚’ไธŽใˆใš๏ผŒใ™ในใฆๆˆๅŠŸใ™ใ‚‹ๅ ดๅˆ + args: []string{}, + optExpect: []string{}, + buildResult: map[string]errBuild{ + "aaa": {}, + "bbb": {}, + "ccc": {}, + }, + }, + } + + ctx := &grapicmd.Ctx{} + err := ctx.Init() + if err != nil { + t.Fatal(err) + } + + commandNames := []string{"aaa", "bbb", "ccc"} + for _, c := range testCases { + loader := moduletesting.NewMockScriptLoader(ctrl) + + loader.EXPECT().Load(gomock.Any()).Return(nil) + loader.EXPECT().Names().Return(commandNames) + for _, arg := range commandNames { + script := moduletesting.NewMockScript(ctrl) + loader.EXPECT().Get(arg).Return(script, true).AnyTimes() + script.EXPECT().Name().Return(arg).AnyTimes() + if buildResult, ok := c.buildResult[arg]; ok { + script.EXPECT().Build(gomock.Any(), c.optExpect).Return(buildResult.err) + } + } + + cmd := newBuildCommandMocked(true, ctx, loader, cli.NopUI) + cmd.SetArgs(c.args) + err := cmd.Execute() + + if c.wantErr != (err != nil) { + t.Errorf("wantErr: %v, gotErr: %v", c.wantErr, err != nil) + } + } +} diff --git a/pkg/grapicmd/cmd/cmd.go b/pkg/grapicmd/cmd/cmd.go index 869340a6..9edc0d15 100644 --- a/pkg/grapicmd/cmd/cmd.go +++ b/pkg/grapicmd/cmd/cmd.go @@ -1,74 +1,42 @@ package cmd import ( - "path/filepath" - - "github.com/izumin5210/clicontrib/pkg/ccmd" "github.com/pkg/errors" "github.com/spf13/cobra" + "github.com/izumin5210/clig/pkg/clib" "github.com/izumin5210/grapi/pkg/grapicmd" - "github.com/izumin5210/grapi/pkg/grapicmd/internal/module/command" - "github.com/izumin5210/grapi/pkg/grapicmd/internal/module/generator" - "github.com/izumin5210/grapi/pkg/grapicmd/internal/module/script" - "github.com/izumin5210/grapi/pkg/grapicmd/internal/module/ui" ) // NewGrapiCommand creates a new command object. -func NewGrapiCommand(cfg grapicmd.Config) *cobra.Command { - var err error +func NewGrapiCommand(ctx *grapicmd.Ctx) *cobra.Command { + initErr := ctx.Init() cmd := &cobra.Command{ - Use: cfg.AppName(), + Use: ctx.Build.AppName, Short: "JSON API framework implemented with gRPC and Gateway", Long: "", SilenceErrors: true, SilenceUsage: true, - PreRunE: func(cmd *cobra.Command, args []string) error { - return err + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + return errors.WithStack(initErr) + }, + PersistentPostRun: func(cmd *cobra.Command, args []string) { + clib.Close() }, } - var cfgFile string - cobra.OnInitialize(func() { cfg.Init(cfgFile) }) - ccmd.HandleLogFlags(cmd) + clib.AddLoggingFlags(cmd) + clib.SetIO(cmd, ctx.IO) - cmd.PersistentFlags().StringVar(&cfgFile, "config", "./"+cfg.AppName()+".toml", "config file") - - ui := ui.New(cfg.OutWriter(), cfg.InReader()) - generator := generator.New( - cfg.Fs(), - ui, - cfg.RootDir(), - cfg.ProtocConfig().ProtosDir, - cfg.ProtocConfig().OutDir, - cfg.ServerDir(), - cfg.Version(), + cmd.AddCommand( + newInitCommand(ctx), + newProtocCommand(ctx), + newBuildCommand(ctx), + clib.NewVersionCommand(ctx.Build), ) - commandFactory := command.NewFactory(cfg.OutWriter(), cfg.ErrWriter(), cfg.InReader()) - scriptLoader := script.NewLoader(cfg.Fs(), commandFactory, cfg.RootDir()) - - cmd.AddCommand(newInitCommand(cfg, ui, generator, commandFactory)) - cmd.AddCommand(newGenerateCommand(cfg, ui, generator, commandFactory)) - cmd.AddCommand(newDestroyCommand(cfg, generator)) - cmd.AddCommand(newProtocCommand(cfg, ui, commandFactory)) - cmd.AddCommand(newBuildCommand(cfg, ui, scriptLoader)) - cmd.AddCommand(newVersionCommand(cfg)) - - if cfg.IsInsideApp() { - err = scriptLoader.Load(filepath.Join(cfg.RootDir(), "cmd")) - if err != nil { - err = errors.Wrap(err, "failed to load user-defined commands") - } - - udCmds := make([]*cobra.Command, 0) - for _, name := range scriptLoader.Names() { - udCmds = append(udCmds, newUserDefinedCommand(ui, scriptLoader, name)) - } - if len(udCmds) > 0 { - cmd.AddCommand(udCmds...) - } - } + cmd.AddCommand(newGenerateCommands(ctx)...) + cmd.AddCommand(newUserDefinedCommands(ctx)...) return cmd } diff --git a/pkg/grapicmd/cmd/destroy.go b/pkg/grapicmd/cmd/destroy.go deleted file mode 100644 index d4aea963..00000000 --- a/pkg/grapicmd/cmd/destroy.go +++ /dev/null @@ -1,56 +0,0 @@ -package cmd - -import ( - "github.com/pkg/errors" - "github.com/spf13/cobra" - - "github.com/izumin5210/grapi/pkg/grapicmd" - "github.com/izumin5210/grapi/pkg/grapicmd/internal/module" -) - -func newDestroyCommand(cfg grapicmd.Config, generator module.Generator) *cobra.Command { - cmd := &cobra.Command{ - Use: "destroy GENERATOR", - Short: "Destroy codes", - Aliases: []string{"d"}, - } - - cmd.AddCommand(newDestroyServiceCommand(cfg, generator)) - cmd.AddCommand(newDestroyCommandCommand(cfg, generator)) - - return cmd -} - -func newDestroyServiceCommand(cfg grapicmd.Config, generator module.ServiceGenerator) *cobra.Command { - return &cobra.Command{ - Use: "service NAME", - Short: "Destroy a service", - SilenceErrors: true, - SilenceUsage: true, - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - if !cfg.IsInsideApp() { - return errors.New("destroy command should execute inside a grapi application directory") - } - - return errors.WithStack(errors.WithStack(generator.DestroyService(args[0]))) - }, - } -} - -func newDestroyCommandCommand(cfg grapicmd.Config, generator module.CommandGenerator) *cobra.Command { - return &cobra.Command{ - Use: "command NAME", - Short: "Destroy a command", - SilenceErrors: true, - SilenceUsage: true, - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - if !cfg.IsInsideApp() { - return errors.New("destroy command should execute inside a grapi application directory") - } - - return errors.WithStack(generator.DestroyCommand(args[0])) - }, - } -} diff --git a/pkg/grapicmd/cmd/generate.go b/pkg/grapicmd/cmd/generate.go deleted file mode 100644 index ba58a7cf..00000000 --- a/pkg/grapicmd/cmd/generate.go +++ /dev/null @@ -1,111 +0,0 @@ -package cmd - -import ( - "github.com/pkg/errors" - "github.com/spf13/cobra" - - "github.com/izumin5210/grapi/pkg/grapicmd" - "github.com/izumin5210/grapi/pkg/grapicmd/internal/module" - "github.com/izumin5210/grapi/pkg/grapicmd/internal/usecase" -) - -func newGenerateCommand(cfg grapicmd.Config, ui module.UI, generator module.Generator, commandFactory module.CommandFactory) *cobra.Command { - cmd := &cobra.Command{ - Use: "generate GENERATOR", - Short: "Generate new code", - Aliases: []string{"g", "gen"}, - } - - cmd.AddCommand(newGenerateServiceCommand(cfg, ui, generator, commandFactory)) - cmd.AddCommand(newGenerateScaffoldServiceCommand(cfg, ui, generator, commandFactory)) - cmd.AddCommand(newGenerateCommandCommand(cfg, generator)) - - return cmd -} - -func newGenerateServiceCommand(cfg grapicmd.Config, ui module.UI, generator module.ServiceGenerator, commandFactory module.CommandFactory) *cobra.Command { - var ( - skipTest bool - resourceName string - ) - - cmd := &cobra.Command{ - Use: "service NAME [METHODS...]", - Short: "Generate a new service", - SilenceErrors: true, - SilenceUsage: true, - Args: cobra.MinimumNArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - if !cfg.IsInsideApp() { - return errors.New("geneate command should execut inside a grapi application directory") - } - - ui.Section("Generate service") - genCfg := module.ServiceGenerationConfig{ResourceName: resourceName, Methods: args[1:], SkipTest: skipTest} - err := errors.WithStack(generator.GenerateService(args[0], genCfg)) - if err != nil { - return err - } - - protocUsecase := usecase.NewExecuteProtocUsecase(cfg.ProtocConfig(), cfg.Fs(), ui, commandFactory, cfg.RootDir()) - return errors.WithStack(protocUsecase.Perform()) - }, - } - - cmd.PersistentFlags().BoolVarP(&skipTest, "skip-test", "T", false, "Skip test files") - cmd.PersistentFlags().StringVar(&resourceName, "resource-name", "", "ResourceName to be used") - - return cmd -} - -func newGenerateScaffoldServiceCommand(cfg grapicmd.Config, ui module.UI, generator module.ServiceGenerator, commandFactory module.CommandFactory) *cobra.Command { - var ( - skipTest bool - resourceName string - ) - - cmd := &cobra.Command{ - Use: "scaffold-service NAME", - Short: "Generate a new service with standard methods", - SilenceErrors: true, - SilenceUsage: true, - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - if !cfg.IsInsideApp() { - return errors.New("geneate command should execut inside a grapi application directory") - } - - ui.Section("Scaffold service") - genCfg := module.ServiceGenerationConfig{ResourceName: resourceName, SkipTest: skipTest} - err := errors.WithStack(generator.ScaffoldService(args[0], genCfg)) - if err != nil { - return err - } - - protocUsecase := usecase.NewExecuteProtocUsecase(cfg.ProtocConfig(), cfg.Fs(), ui, commandFactory, cfg.RootDir()) - return errors.WithStack(protocUsecase.Perform()) - }, - } - - cmd.PersistentFlags().BoolVarP(&skipTest, "skip-test", "T", false, "Skip test files") - cmd.PersistentFlags().StringVar(&resourceName, "resource-name", "", "ResourceName to be used") - - return cmd -} - -func newGenerateCommandCommand(cfg grapicmd.Config, generator module.CommandGenerator) *cobra.Command { - return &cobra.Command{ - Use: "command NAME", - Short: "Generate a new command", - SilenceErrors: true, - SilenceUsage: true, - Args: cobra.ExactArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - if !cfg.IsInsideApp() { - return errors.New("geneate command should execut inside a grapi application directory") - } - - return errors.WithStack(generator.GenerateCommand(args[0])) - }, - } -} diff --git a/pkg/grapicmd/cmd/generators.go b/pkg/grapicmd/cmd/generators.go new file mode 100644 index 00000000..324baad4 --- /dev/null +++ b/pkg/grapicmd/cmd/generators.go @@ -0,0 +1,122 @@ +package cmd + +import ( + "context" + "strings" + "sync" + + "github.com/spf13/cobra" + "go.uber.org/zap" + + "github.com/izumin5210/clig/pkg/clib" + "github.com/izumin5210/execx" + "github.com/izumin5210/gex/pkg/tool" + "github.com/izumin5210/grapi/pkg/grapicmd" + "github.com/izumin5210/grapi/pkg/grapicmd/di" + "github.com/izumin5210/grapi/pkg/grapicmd/util/fs" +) + +func newGenerateCommands(ctx *grapicmd.Ctx) (cmds []*cobra.Command) { + gCmd := &cobra.Command{ + Use: "generate GENERATOR", + Short: "Generate a new code", + Aliases: []string{"g", "gen"}, + } + dCmd := &cobra.Command{ + Use: "destroy GENERATOR", + Short: "Destroy an existing new code", + Aliases: []string{"d"}, + } + + var ( + execs []string + wg sync.WaitGroup + ) + + wg.Add(2) + cmdNames := make(map[string]struct{}, 100) + + go func() { + defer wg.Done() + execs = fs.ListExecutableWithPrefix(ctx.FS, "grapi-gen-") + }() + + go func() { + defer wg.Done() + + toolRepo, err := di.NewToolRepository(ctx) + if err != nil { + zap.L().Debug("failed to initialize tools repository", zap.Error(err)) + return + } + + tools, err := toolRepo.List(context.TODO()) + if err != nil { + zap.L().Debug("failed to retrieve tools", zap.Error(err)) + return + } + + for _, t := range tools { + if !strings.HasPrefix(t.Name(), "grapi-gen-") { + continue + } + if _, ok := cmdNames[t.Name()]; ok { + continue + } + cmdNames[t.Name()] = struct{}{} + gCmd.AddCommand(newGenerateCommandByTool(toolRepo, t, "generate")) + dCmd.AddCommand(newGenerateCommandByTool(toolRepo, t, "destroy")) + } + }() + + wg.Wait() + + for _, exec := range execs { + if _, ok := cmdNames[exec]; ok { + continue + } + cmdNames[exec] = struct{}{} + gCmd.AddCommand(newGenerateCommandByExec(ctx.IO, ctx.Exec, exec, "generate")) + dCmd.AddCommand(newGenerateCommandByExec(ctx.IO, ctx.Exec, exec, "destroy")) + } + + cmds = append(cmds, gCmd, dCmd) + + return +} + +func newGenerateCommandByTool(repo tool.Repository, t tool.Tool, subCmd string) *cobra.Command { + cmd := &cobra.Command{ + Use: strings.TrimPrefix(t.Name(), "grapi-gen-"), + Args: cobra.ArbitraryArgs, + RunE: func(_ *cobra.Command, args []string) error { + return repo.Run(context.TODO(), t.Name(), append([]string{subCmd}, args...)...) + }, + } + cmd.SetUsageFunc(func(*cobra.Command) error { + return repo.Run(context.TODO(), t.Name(), subCmd, "--help") + }) + return cmd +} + +func newGenerateCommandByExec(io *clib.IO, exec *execx.Executor, path, subCmd string) *cobra.Command { + cmd := &cobra.Command{ + Use: strings.TrimPrefix(path, "grapi-gen-"), + Args: cobra.ArbitraryArgs, + RunE: func(_ *cobra.Command, args []string) error { + cmd := exec.CommandContext(context.TODO(), path, append([]string{subCmd}, args...)...) + cmd.Stdout = io.Out + cmd.Stderr = io.Err + cmd.Stdin = io.In + return cmd.Run() + }, + } + cmd.SetUsageFunc(func(*cobra.Command) error { + cmd := exec.CommandContext(context.TODO(), path, subCmd, "--help") + cmd.Stdout = io.Out + cmd.Stderr = io.Err + cmd.Stdin = io.In + return cmd.Run() + }) + return cmd +} diff --git a/pkg/grapicmd/cmd/init.go b/pkg/grapicmd/cmd/init.go index 738d48bf..19822271 100644 --- a/pkg/grapicmd/cmd/init.go +++ b/pkg/grapicmd/cmd/init.go @@ -3,24 +3,18 @@ package cmd import ( "path/filepath" - "github.com/izumin5210/clicontrib/pkg/clog" "github.com/pkg/errors" "github.com/spf13/cobra" + "go.uber.org/zap" + "github.com/izumin5210/clig/pkg/clib" "github.com/izumin5210/grapi/pkg/grapicmd" - "github.com/izumin5210/grapi/pkg/grapicmd/internal/module" + "github.com/izumin5210/grapi/pkg/grapicmd/di" "github.com/izumin5210/grapi/pkg/grapicmd/internal/usecase" ) -var ( - tmplPaths []string -) - -func newInitCommand(cfg grapicmd.Config, ui module.UI, generator module.ProjectGenerator, commandFactory module.CommandFactory) *cobra.Command { - var ( - depSkipped bool - headUsed bool - ) +func newInitCommand(ctx *grapicmd.Ctx) *cobra.Command { + var cfg usecase.InitConfig cmd := &cobra.Command{ Use: "init [name]", @@ -29,44 +23,47 @@ func newInitCommand(cfg grapicmd.Config, ui module.UI, generator module.ProjectG SilenceUsage: true, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - root, err := parseInitArgs(cfg, args) + root, err := parseInitArgs(ctx, args) if err != nil { return errors.WithStack(err) } - clog.Debug("parseInitArgs", "root", root) + zap.L().Debug("parseInitArgs", zap.String("root", root)) - u := usecase.NewInitializeProjectUsecase( - ui, - generator, - commandFactory, - cfg.Version(), - ) + u, err := di.NewInitializeProjectUsecase(ctx, clib.Path(root)) + if err != nil { + return errors.WithStack(err) + } - return errors.WithStack(u.Perform(root, depSkipped, headUsed)) + return errors.WithStack(u.Perform(root, cfg)) }, } - cmd.PersistentFlags().BoolVarP(&depSkipped, "skip-dep", "D", false, "Don't run dep ensure") - cmd.PersistentFlags().BoolVar(&headUsed, "HEAD", false, "Use HEAD grapi") + cmd.PersistentFlags().StringVar(&cfg.Revision, "revision", "", "Specify grapi revision") + cmd.PersistentFlags().StringVar(&cfg.Branch, "branch", "", "Specify grapi branch") + cmd.PersistentFlags().StringVar(&cfg.Version, "version", "", "Specify grapi version") + cmd.PersistentFlags().BoolVar(&cfg.HEAD, "HEAD", false, "Use HEAD grapi") + cmd.PersistentFlags().StringVar(&cfg.GrapiReplacementURL, "replace-grapi", "", "Specify grapi replacement url") + cmd.PersistentFlags().StringVarP(&cfg.Package, "package", "p", "", `Package name of the application(default: ".")`) + cmd.PersistentFlags().BoolVar(&cfg.Dep, "use-dep", false, "Use dep instead of Modules") return cmd } -func parseInitArgs(cfg grapicmd.Config, args []string) (root string, err error) { +func parseInitArgs(ctx *grapicmd.Ctx, args []string) (root string, err error) { if argCnt := len(args); argCnt != 1 { err = errors.Errorf("invalid argument count: want 1, got %d", argCnt) return } arg := args[0] - root = cfg.CurrentDir() + root = ctx.RootDir.String() if arg == "." { return } root = arg if !filepath.IsAbs(arg) { - root = filepath.Join(cfg.CurrentDir(), arg) + root = ctx.RootDir.Join(arg).String() } return } diff --git a/pkg/grapicmd/cmd/protoc.go b/pkg/grapicmd/cmd/protoc.go index e6fefadb..ddbf5770 100644 --- a/pkg/grapicmd/cmd/protoc.go +++ b/pkg/grapicmd/cmd/protoc.go @@ -1,26 +1,30 @@ package cmd import ( + "context" + "github.com/pkg/errors" "github.com/spf13/cobra" "github.com/izumin5210/grapi/pkg/grapicmd" - "github.com/izumin5210/grapi/pkg/grapicmd/internal/module" - "github.com/izumin5210/grapi/pkg/grapicmd/internal/usecase" + "github.com/izumin5210/grapi/pkg/grapicmd/di" ) -func newProtocCommand(cfg grapicmd.Config, ui module.UI, commandFactory module.CommandFactory) *cobra.Command { +func newProtocCommand(ctx *grapicmd.Ctx) *cobra.Command { return &cobra.Command{ Use: "protoc", Short: "Run protoc", SilenceErrors: true, SilenceUsage: true, RunE: func(cmd *cobra.Command, args []string) error { - if !cfg.IsInsideApp() { + if !ctx.IsInsideApp() { return errors.New("protoc command should be execute inside a grapi application directory") } - u := usecase.NewExecuteProtocUsecase(cfg.ProtocConfig(), cfg.Fs(), ui, commandFactory, cfg.RootDir()) - return errors.WithStack(u.Perform()) + protocw, err := di.NewProtocWrapper(ctx) + if err != nil { + return errors.WithStack(err) + } + return errors.WithStack(protocw.Exec(context.TODO())) }, } } diff --git a/pkg/grapicmd/cmd/user_defined.go b/pkg/grapicmd/cmd/user_defined.go index 54d4820f..e60fe52b 100644 --- a/pkg/grapicmd/cmd/user_defined.go +++ b/pkg/grapicmd/cmd/user_defined.go @@ -3,11 +3,37 @@ package cmd import ( "github.com/pkg/errors" "github.com/spf13/cobra" + "github.com/srvc/appctx" + "github.com/izumin5210/grapi/pkg/cli" + "github.com/izumin5210/grapi/pkg/grapicmd" + "github.com/izumin5210/grapi/pkg/grapicmd/di" "github.com/izumin5210/grapi/pkg/grapicmd/internal/module" ) -func newUserDefinedCommand(ui module.UI, scriptLoader module.ScriptLoader, name string) *cobra.Command { +func newUserDefinedCommands(ctx *grapicmd.Ctx) (cmds []*cobra.Command) { + if !ctx.IsInsideApp() { + return + } + + scriptLoader := di.NewScriptLoader(ctx) + + err := scriptLoader.Load(ctx.RootDir.Join("cmd").String()) + if err != nil { + // TODO: log + return + } + + ui := di.NewUI(ctx) + + for _, name := range scriptLoader.Names() { + cmds = append(cmds, newUserDefinedCommand(ui, scriptLoader, name)) + } + + return +} + +func newUserDefinedCommand(ui cli.UI, scriptLoader module.ScriptLoader, name string) *cobra.Command { return &cobra.Command{ Use: name + " [-- BUILD_OPTIONS] [-- RUN_ARGS]", SilenceErrors: true, @@ -15,7 +41,8 @@ func newUserDefinedCommand(ui module.UI, scriptLoader module.ScriptLoader, name RunE: func(c *cobra.Command, args []string) (err error) { script, ok := scriptLoader.Get(name) if !ok { - err = errors.Wrapf(err, "faild to find subcommand %d", name) + err = errors.Wrapf(err, "faild to find subcommand %s", name) + return } pos := len(args) @@ -33,15 +60,17 @@ func newUserDefinedCommand(ui module.UI, scriptLoader module.ScriptLoader, name runArgs = args[pos+1:] } + ctx := appctx.Global() + ui.Section(script.Name()) ui.Subsection("Building...") - err = errors.WithStack(script.Build(buildArgs...)) + err = errors.WithStack(script.Build(ctx, buildArgs...)) if err != nil { return } ui.Subsection("Starting...") - return errors.WithStack(script.Run(runArgs...)) + return errors.WithStack(script.Run(ctx, runArgs...)) }, } } diff --git a/pkg/grapicmd/cmd/user_defined_test.go b/pkg/grapicmd/cmd/user_defined_test.go index 9020d2f9..29f4ef82 100644 --- a/pkg/grapicmd/cmd/user_defined_test.go +++ b/pkg/grapicmd/cmd/user_defined_test.go @@ -4,6 +4,7 @@ import ( "testing" "github.com/golang/mock/gomock" + "github.com/izumin5210/grapi/pkg/cli" "github.com/izumin5210/grapi/pkg/grapicmd/internal/module/testing" ) @@ -67,9 +68,6 @@ func Test_userDefinedCommand(t *testing.T) { }, } - ui := moduletesting.NewMockUI(ctrl) - ui.EXPECT().Section(gomock.Any()).AnyTimes() - ui.EXPECT().Subsection(gomock.Any()).AnyTimes() name := "testcommand" for _, c := range cases { @@ -77,14 +75,14 @@ func Test_userDefinedCommand(t *testing.T) { script := moduletesting.NewMockScript(ctrl) var verbose bool - cmd := newUserDefinedCommand(ui, loader, name) + cmd := newUserDefinedCommand(cli.NopUI, loader, name) cmd.SetArgs(c.args) cmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "") loader.EXPECT().Get(name).Return(script, true) - script.EXPECT().Build(c.buildArgs...) - script.EXPECT().Run(c.runArgs...) + script.EXPECT().Build(gomock.Any(), c.buildArgs...) + script.EXPECT().Run(gomock.Any(), c.runArgs...) script.EXPECT().Name().Return(name) err := cmd.Execute() diff --git a/pkg/grapicmd/cmd/version.go b/pkg/grapicmd/cmd/version.go deleted file mode 100644 index 98426301..00000000 --- a/pkg/grapicmd/cmd/version.go +++ /dev/null @@ -1,20 +0,0 @@ -package cmd - -import ( - "github.com/spf13/cobra" - - "github.com/izumin5210/grapi/pkg/grapicmd" -) - -func newVersionCommand(cfg grapicmd.Config) *cobra.Command { - return &cobra.Command{ - Use: "version", - Short: "Print version information", - Long: "Print version information", - SilenceErrors: true, - SilenceUsage: true, - Run: func(cmd *cobra.Command, _ []string) { - cmd.Printf("%s %s %s (%s %s)\n", cfg.AppName(), cfg.Version(), cfg.ReleaseType(), cfg.BuildDate(), cfg.Revision()) - }, - } -} diff --git a/pkg/grapicmd/config.go b/pkg/grapicmd/config.go deleted file mode 100644 index b5feaa1e..00000000 --- a/pkg/grapicmd/config.go +++ /dev/null @@ -1,134 +0,0 @@ -package grapicmd - -import ( - "io" - - "github.com/spf13/afero" - "github.com/spf13/viper" - - "github.com/izumin5210/grapi/pkg/grapicmd/protoc" - "github.com/izumin5210/grapi/pkg/grapicmd/util/fs" -) - -// Config stores general setting params and provides accessors for them. -type Config interface { - Init(cfgFile string) - Fs() afero.Fs - CurrentDir() string - RootDir() string - IsInsideApp() bool - AppName() string - Version() string - Revision() string - BuildDate() string - ReleaseType() string - InReader() io.Reader - OutWriter() io.Writer - ErrWriter() io.Writer - ServerDir() string - ProtocConfig() *protoc.Config -} - -// NewConfig creates new Config object. -func NewConfig( - currentDir string, - appName, version, revision string, - buildDate, releaseType string, - in io.Reader, - out, err io.Writer, -) Config { - afs := afero.NewOsFs() - rootDir, insideApp := fs.LookupRoot(afs, currentDir) - return &config{ - v: viper.New(), - fs: afs, - currentDir: currentDir, - rootDir: rootDir, - insideApp: insideApp, - appName: appName, - version: version, - revision: revision, - buildDate: buildDate, - releaseType: releaseType, - in: in, - out: out, - err: err, - } -} - -type config struct { - cfgFile string - v *viper.Viper - fs afero.Fs - currentDir, rootDir string - insideApp bool - appName, version, revision string - buildDate, releaseType string - in io.Reader - out, err io.Writer - readConfigErr error -} - -func (c *config) Init(cfgFile string) { - c.cfgFile = cfgFile - c.v.SetConfigFile(c.cfgFile) - c.readConfigErr = c.v.ReadInConfig() -} - -func (c *config) Fs() afero.Fs { - return c.fs -} - -func (c *config) CurrentDir() string { - return c.currentDir -} - -func (c *config) RootDir() string { - return c.rootDir -} - -func (c *config) IsInsideApp() bool { - return c.insideApp -} - -func (c *config) AppName() string { - return c.appName -} - -func (c *config) Version() string { - return c.version -} - -func (c *config) Revision() string { - return c.revision -} - -func (c *config) BuildDate() string { - return c.buildDate -} - -func (c *config) ReleaseType() string { - return c.releaseType -} - -func (c *config) InReader() io.Reader { - return c.in -} - -func (c *config) OutWriter() io.Writer { - return c.out -} - -func (c *config) ErrWriter() io.Writer { - return c.err -} - -func (c *config) ServerDir() string { - return c.v.GetString("grapi.server_dir") -} - -func (c *config) ProtocConfig() *protoc.Config { - cfg := &protoc.Config{} - c.v.UnmarshalKey("protoc", cfg) - return cfg -} diff --git a/pkg/grapicmd/context.go b/pkg/grapicmd/context.go new file mode 100644 index 00000000..eceab49e --- /dev/null +++ b/pkg/grapicmd/context.go @@ -0,0 +1,128 @@ +package grapicmd + +import ( + "os" + "path/filepath" + + "github.com/google/wire" + "github.com/pkg/errors" + "github.com/spf13/afero" + "github.com/spf13/viper" + "go.uber.org/zap" + + "github.com/izumin5210/clig/pkg/clib" + "github.com/izumin5210/execx" + "github.com/izumin5210/grapi/pkg/cli" + "github.com/izumin5210/grapi/pkg/protoc" +) + +// Ctx contains the runtime context of grpai. +type Ctx struct { + FS afero.Fs + Viper *viper.Viper + Exec *execx.Executor + IO *clib.IO + + RootDir cli.RootDir + insideApp bool + + Config Config + Build clib.Build + ProtocConfig protoc.Config +} + +// Config stores general setting params and provides accessors for them. +type Config struct { + Package string + Grapi struct { + ServerDir string + } +} + +// Init initializes the runtime context. +func (c *Ctx) Init() error { + if c.RootDir.String() == "" { + dir, _ := os.Getwd() + c.RootDir = cli.RootDir{clib.Path(dir)} + } + + if c.IO == nil { + c.IO = clib.Stdio() + } + + if c.FS == nil { + c.FS = afero.NewOsFs() + } + + if c.Viper == nil { + c.Viper = viper.New() + } + + c.Viper.SetFs(c.FS) + + if c.Exec == nil { + c.Exec = execx.New() + } + + if c.Build.AppName == "" { + c.Build.AppName = "grapi" + } + + return errors.WithStack(c.loadConfig()) +} + +func (c *Ctx) loadConfig() error { + c.Viper.SetConfigName(c.Build.AppName) + for dir := c.RootDir.String(); dir != "/"; dir = filepath.Dir(dir) { + c.Viper.AddConfigPath(dir) + } + + err := c.Viper.ReadInConfig() + if err != nil { + zap.L().Info("failed to find config file", zap.Error(err)) + return nil + } + + c.insideApp = true + c.RootDir = cli.RootDir{clib.Path(filepath.Dir(c.Viper.ConfigFileUsed()))} + + err = c.Viper.Unmarshal(&c.Config) + if err != nil { + zap.L().Warn("failed to parse config", zap.Error(err)) + return errors.WithStack(err) + } + + err = c.Viper.UnmarshalKey("protoc", &c.ProtocConfig) + if err != nil { + zap.L().Warn("failed to parse protoc config", zap.Error(err)) + return errors.WithStack(err) + } + + return nil +} + +// IsInsideApp returns true if the current working directory is inside a grapi project. +func (c *Ctx) IsInsideApp() bool { + return c.insideApp +} + +// CtxSet is a provider set that includes modules contained in Ctx. +var CtxSet = wire.NewSet( + ProvideFS, + ProvideViper, + ProvideExec, + ProvideIO, + ProvideRootDir, + ProvideConfig, + ProvideBuildConfig, + ProvideProtocConfig, +) + +func ProvideFS(c *Ctx) afero.Fs { return c.FS } +func ProvideViper(c *Ctx) *viper.Viper { return c.Viper } +func ProvideExec(c *Ctx) *execx.Executor { return c.Exec } +func ProvideIO(c *Ctx) *clib.IO { return c.IO } +func ProvideRootDir(c *Ctx) cli.RootDir { return c.RootDir } +func ProvideConfig(c *Ctx) *Config { return &c.Config } +func ProvideBuildConfig(c *Ctx) *clib.Build { return &c.Build } +func ProvideProtocConfig(c *Ctx) *protoc.Config { return &c.ProtocConfig } diff --git a/pkg/grapicmd/context_test.go b/pkg/grapicmd/context_test.go new file mode 100644 index 00000000..774c1caa --- /dev/null +++ b/pkg/grapicmd/context_test.go @@ -0,0 +1,124 @@ +package grapicmd_test + +import ( + "testing" + + "github.com/spf13/afero" + + "github.com/izumin5210/clig/pkg/clib" + "github.com/izumin5210/grapi/pkg/cli" + "github.com/izumin5210/grapi/pkg/grapicmd" +) + +func TestCtx(t *testing.T) { + root := cli.RootDir{clib.Path("/go/src/awesomeapp")} + cwd := root.Join("api").String() + + orDie := func(t *testing.T, err error) { + t.Helper() + if err != nil { + t.Fatalf("unexpected error %v", err) + } + } + + fs := afero.NewMemMapFs() + orDie(t, fs.MkdirAll(cwd, 0755)) + orDie(t, afero.WriteFile(fs, root.Join("grapi.toml").String(), []byte(` +package = "awesomeapp" + +[grapi] +server_dir = "./app/server" + +[protoc] +protos_dir = "./api/protos" +out_dir = "./api" +import_dirs = [ + "./api/protos", + "./vendor/github.com/grpc-ecosystem/grpc-gateway", + "./vendor/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis", +] + + [[protoc.plugins]] + name = "go" + args = { plugins = "grpc", paths = "source_relative" } + + [[protoc.plugins]] + name = "grpc-gateway" + args = { logtostderr = true, paths = "source_relative" } + + [[protoc.plugins]] + name = "swagger" + args = { logtostderr = true } +`), 0644)) + + ctx := &grapicmd.Ctx{FS: fs, RootDir: cli.RootDir{clib.Path(cwd)}} + + err := ctx.Init() + + if err != nil { + t.Errorf("Init() returned %v", err) + } + + if got, want := ctx.RootDir, root; got != want { + t.Errorf("RootDir is %q, want %q", got, want) + } + + if got, want := ctx.IsInsideApp(), true; got != want { + t.Errorf("IsInsideApp() returned %t, want %t", got, want) + } + + if got, want := ctx.Config.Package, "awesomeapp"; got != want { + t.Errorf("Config.Package is %q, want %q", got, want) + } + + if got, want := ctx.ProtocConfig.ProtosDir, "./api/protos"; got != want { + t.Errorf("ProtocConfig.ProtosDir is %q, want %q", got, want) + } + + if got, want := len(ctx.ProtocConfig.Plugins), 3; got != want { + t.Errorf("ProtocConfig has %d plugins, want %d", got, want) + } +} + +func TestCtx_outsideApp(t *testing.T) { + root := cli.RootDir{clib.Path("/go/src/awesomeapp")} + cwd := root.Join("api").String() + + orDie := func(t *testing.T, err error) { + t.Helper() + if err != nil { + t.Fatalf("unexpected error %v", err) + } + } + + fs := afero.NewMemMapFs() + orDie(t, fs.MkdirAll(cwd, 0755)) + + ctx := &grapicmd.Ctx{FS: fs, RootDir: cli.RootDir{clib.Path(cwd)}} + + err := ctx.Init() + + if err != nil { + t.Errorf("Init() returned %v", err) + } + + if got, want := ctx.RootDir.String(), cwd; got != want { + t.Errorf("RootDir is %q, want %q", got, want) + } + + if got, want := ctx.IsInsideApp(), false; got != want { + t.Errorf("IsInsideApp() returned %t, want %t", got, want) + } + + if got, want := ctx.Config.Package, ""; got != want { + t.Errorf("Config.Package is %q, want %q", got, want) + } + + if got, want := ctx.ProtocConfig.ProtosDir, ""; got != want { + t.Errorf("ProtocConfig.ProtosDir is %q, want %q", got, want) + } + + if got, want := len(ctx.ProtocConfig.Plugins), 0; got != want { + t.Errorf("ProtocConfig has %d plugins, want %d", got, want) + } +} diff --git a/pkg/grapicmd/di/providers.go b/pkg/grapicmd/di/providers.go new file mode 100644 index 00000000..2848f913 --- /dev/null +++ b/pkg/grapicmd/di/providers.go @@ -0,0 +1,43 @@ +package di + +import ( + "net/http" + + "github.com/google/wire" + "github.com/izumin5210/clig/pkg/clib" + "github.com/izumin5210/execx" + "github.com/rakyll/statik/fs" + "github.com/spf13/afero" + + "github.com/izumin5210/grapi/pkg/cli" + "github.com/izumin5210/grapi/pkg/gencmd" + "github.com/izumin5210/grapi/pkg/grapicmd" + "github.com/izumin5210/grapi/pkg/grapicmd/internal/module" + "github.com/izumin5210/grapi/pkg/grapicmd/internal/module/script" + "github.com/izumin5210/grapi/pkg/grapicmd/internal/usecase" + "github.com/izumin5210/grapi/pkg/protoc" +) + +func ProvideScriptLoader(ctx *grapicmd.Ctx, io *clib.IO, exec *execx.Executor) module.ScriptLoader { + return script.NewLoader(ctx.FS, io, exec, ctx.RootDir.String()) +} + +func ProvideGenerator(ctx *grapicmd.Ctx, ui cli.UI, fs afero.Fs, tmplFs http.FileSystem, outDir clib.Path) gencmd.Generator { + return gencmd.NewGenerator( + fs, + ui, + outDir, + tmplFs, + nil, + ) +} + +var Set = wire.NewSet( + grapicmd.CtxSet, + protoc.WrapperSet, + cli.UIInstance, + ProvideScriptLoader, + ProvideGenerator, + fs.New, + usecase.NewInitializeProjectUsecase, +) diff --git a/pkg/grapicmd/di/wire.go b/pkg/grapicmd/di/wire.go new file mode 100644 index 00000000..456a55c7 --- /dev/null +++ b/pkg/grapicmd/di/wire.go @@ -0,0 +1,40 @@ +//+build wireinject + +package di + +import ( + "github.com/google/wire" + "github.com/izumin5210/clig/pkg/clib" + "github.com/izumin5210/gex/pkg/tool" + + "github.com/izumin5210/grapi/pkg/cli" + "github.com/izumin5210/grapi/pkg/grapicmd" + "github.com/izumin5210/grapi/pkg/grapicmd/internal/module" + "github.com/izumin5210/grapi/pkg/grapicmd/internal/usecase" + "github.com/izumin5210/grapi/pkg/protoc" +) + +func NewUI(*grapicmd.Ctx) cli.UI { + wire.Build(Set) + return nil +} + +func NewScriptLoader(*grapicmd.Ctx) module.ScriptLoader { + wire.Build(Set) + return nil +} + +func NewToolRepository(*grapicmd.Ctx) (tool.Repository, error) { + wire.Build(Set) + return nil, nil +} + +func NewProtocWrapper(*grapicmd.Ctx) (protoc.Wrapper, error) { + wire.Build(Set) + return nil, nil +} + +func NewInitializeProjectUsecase(*grapicmd.Ctx, clib.Path) (usecase.InitializeProjectUsecase, error) { + wire.Build(Set) + return nil, nil +} diff --git a/pkg/grapicmd/di/wire_gen.go b/pkg/grapicmd/di/wire_gen.go new file mode 100644 index 00000000..d411510b --- /dev/null +++ b/pkg/grapicmd/di/wire_gen.go @@ -0,0 +1,77 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate wire +//+build !wireinject + +package di + +import ( + "github.com/izumin5210/clig/pkg/clib" + "github.com/izumin5210/gex/pkg/tool" + "github.com/izumin5210/grapi/pkg/cli" + "github.com/izumin5210/grapi/pkg/grapicmd" + "github.com/izumin5210/grapi/pkg/grapicmd/internal/module" + "github.com/izumin5210/grapi/pkg/grapicmd/internal/usecase" + "github.com/izumin5210/grapi/pkg/protoc" + "github.com/rakyll/statik/fs" +) + +// Injectors from wire.go: + +func NewUI(ctx *grapicmd.Ctx) cli.UI { + io := grapicmd.ProvideIO(ctx) + ui := cli.UIInstance(io) + return ui +} + +func NewScriptLoader(ctx *grapicmd.Ctx) module.ScriptLoader { + io := grapicmd.ProvideIO(ctx) + executor := grapicmd.ProvideExec(ctx) + scriptLoader := ProvideScriptLoader(ctx, io, executor) + return scriptLoader +} + +func NewToolRepository(ctx *grapicmd.Ctx) (tool.Repository, error) { + fs := grapicmd.ProvideFS(ctx) + executor := grapicmd.ProvideExec(ctx) + io := grapicmd.ProvideIO(ctx) + rootDir := grapicmd.ProvideRootDir(ctx) + config := protoc.ProvideGexConfig(fs, executor, io, rootDir) + repository, err := protoc.ProvideToolRepository(config) + if err != nil { + return nil, err + } + return repository, nil +} + +func NewProtocWrapper(ctx *grapicmd.Ctx) (protoc.Wrapper, error) { + config := grapicmd.ProvideProtocConfig(ctx) + fs := grapicmd.ProvideFS(ctx) + executor := grapicmd.ProvideExec(ctx) + io := grapicmd.ProvideIO(ctx) + ui := cli.UIInstance(io) + rootDir := grapicmd.ProvideRootDir(ctx) + gexConfig := protoc.ProvideGexConfig(fs, executor, io, rootDir) + repository, err := protoc.ProvideToolRepository(gexConfig) + if err != nil { + return nil, err + } + wrapper := protoc.NewWrapper(config, fs, executor, ui, repository, rootDir) + return wrapper, nil +} + +func NewInitializeProjectUsecase(ctx *grapicmd.Ctx, path clib.Path) (usecase.InitializeProjectUsecase, error) { + io := grapicmd.ProvideIO(ctx) + ui := cli.UIInstance(io) + aferoFs := grapicmd.ProvideFS(ctx) + fileSystem, err := fs.New() + if err != nil { + return nil, err + } + generator := ProvideGenerator(ctx, ui, aferoFs, fileSystem, path) + executor := grapicmd.ProvideExec(ctx) + rootDir := grapicmd.ProvideRootDir(ctx) + config := protoc.ProvideGexConfig(aferoFs, executor, io, rootDir) + initializeProjectUsecase := usecase.NewInitializeProjectUsecase(ui, aferoFs, generator, io, executor, config) + return initializeProjectUsecase, nil +} diff --git a/pkg/grapicmd/internal/module/command.go b/pkg/grapicmd/internal/module/command.go deleted file mode 100644 index 865b53f4..00000000 --- a/pkg/grapicmd/internal/module/command.go +++ /dev/null @@ -1,14 +0,0 @@ -package module - -// CommandFactory is an interface for executing commands. -type CommandFactory interface { - Create(nameAndArgs []string) Command -} - -// Command is an interface of executable and configurable command object. -type Command interface { - SetDir(dir string) Command - AddEnv(key, value string) Command - ConnectIO() Command - Exec() ([]byte, error) -} diff --git a/pkg/grapicmd/internal/module/command/command.go b/pkg/grapicmd/internal/module/command/command.go deleted file mode 100644 index d6cd5942..00000000 --- a/pkg/grapicmd/internal/module/command/command.go +++ /dev/null @@ -1,121 +0,0 @@ -package command - -import ( - "bytes" - "io" - "os" - "os/exec" - "os/signal" - "sync" - - "github.com/izumin5210/clicontrib/pkg/clog" - "github.com/pkg/errors" - - "github.com/izumin5210/grapi/pkg/grapicmd/internal/module" -) - -type command struct { - name string - args []string - dir string - env []string - ioConnected bool - outWriter io.Writer - errWriter io.Writer - inReader io.Reader -} - -func (c *command) SetDir(dir string) module.Command { - c.dir = dir - return c -} - -func (c *command) AddEnv(key, value string) module.Command { - c.env = append(c.env, key+"="+value) - return c -} - -func (c *command) ConnectIO() module.Command { - c.ioConnected = true - return c -} - -func (c *command) Exec() (out []byte, err error) { - var wg sync.WaitGroup - - cmd := c.build() - - sigCh := make(chan os.Signal, 1) - signal.Notify(sigCh) - wg.Add(1) - - go func() { - defer wg.Done() - defer recover() - for sig := range sigCh { - clog.Debug("signal received", "signal", sig) - if cmd.ProcessState == nil || cmd.ProcessState.Exited() { - break - } - cmd.Process.Signal(sig) - } - }() - - clog.Debug("execute", "command", cmd.Args, "dir", cmd.Dir) - if c.ioConnected { - var ( - buf bytes.Buffer - wg sync.WaitGroup - ) - - closers := make([]func() error, 0, 2) - - outReader, eerr := cmd.StdoutPipe() - if eerr != nil { - err = errors.WithStack(eerr) - return - } - errReader, eerr := cmd.StderrPipe() - if eerr != nil { - err = errors.WithStack(eerr) - return - } - - wg.Add(2) - go func() { - defer wg.Done() - io.Copy(c.outWriter, io.TeeReader(outReader, &buf)) - }() - closers = append(closers, outReader.Close) - go func() { - defer wg.Done() - io.Copy(c.errWriter, io.TeeReader(errReader, &buf)) - }() - closers = append(closers, errReader.Close) - - cmd.Stdin = c.inReader - - err = cmd.Run() - for _, c := range closers { - c() - } - wg.Wait() - - out = buf.Bytes() - } else { - out, err = cmd.CombinedOutput() - } - - signal.Reset() - close(sigCh) - - wg.Wait() - return -} - -func (c *command) build() *exec.Cmd { - cmd := exec.Command(c.name, c.args...) - cmd.Dir = c.dir - cmd.Env = c.env - return cmd -} diff --git a/pkg/grapicmd/internal/module/command/factory.go b/pkg/grapicmd/internal/module/command/factory.go deleted file mode 100644 index fcff08d3..00000000 --- a/pkg/grapicmd/internal/module/command/factory.go +++ /dev/null @@ -1,43 +0,0 @@ -package command - -import ( - "io" - "os" - - "github.com/izumin5210/grapi/pkg/grapicmd/internal/module" -) - -// NewFactory creates a new module.CommandFactory instance. -func NewFactory( - outWriter io.Writer, - errWriter io.Writer, - inReader io.Reader, -) module.CommandFactory { - return &factory{ - outWriter: outWriter, - errWriter: errWriter, - inReader: inReader, - } -} - -type factory struct { - outWriter io.Writer - errWriter io.Writer - inReader io.Reader -} - -func (f *factory) Create(nameAndArgs []string) module.Command { - name := nameAndArgs[0] - args := make([]string, 0, len(nameAndArgs)-1) - if len(nameAndArgs) > 1 { - args = nameAndArgs[1:] - } - return &command{ - name: name, - args: args, - env: os.Environ(), - outWriter: f.outWriter, - errWriter: f.errWriter, - inReader: f.inReader, - } -} diff --git a/pkg/grapicmd/internal/module/gen.go b/pkg/grapicmd/internal/module/gen.go index 51f8c44c..8767ef9a 100644 --- a/pkg/grapicmd/internal/module/gen.go +++ b/pkg/grapicmd/internal/module/gen.go @@ -1,5 +1,4 @@ -//go:generate mockgen -package=moduletesting -source=ui.go -destination=testing/ui_mock.go -//go:generate mockgen -package=moduletesting -imports=.=github.com/izumin5210/grapi/pkg/grapicmd/internal/module -source=generator.go -destination=testing/generator_mock.go -//go:generate mockgen -package=moduletesting -imports=.=github.com/izumin5210/grapi/pkg/grapicmd/internal/module -source=script.go -destination=testing/script_mock.go +//go:generate mockgen -package=moduletesting -source=generator.go -destination=testing/generator_mock.go +//go:generate mockgen -package=moduletesting -source=script.go -destination=testing/script_mock.go package module diff --git a/pkg/grapicmd/internal/module/generator.go b/pkg/grapicmd/internal/module/generator.go index 247ad5f4..3e4ff554 100644 --- a/pkg/grapicmd/internal/module/generator.go +++ b/pkg/grapicmd/internal/module/generator.go @@ -3,31 +3,9 @@ package module // Generator creates files from templates and given params. type Generator interface { ProjectGenerator - ServiceGenerator - CommandGenerator } // ProjectGenerator is an interface to build a new project. type ProjectGenerator interface { - GenerateProject(rootDir string, useHead bool) error -} - -// ServiceGenerator is an interface to create or destroy gRPC services and implementations. -type ServiceGenerator interface { - GenerateService(name string, cfg ServiceGenerationConfig) error - ScaffoldService(name string, cfg ServiceGenerationConfig) error - DestroyService(name string) error -} - -// ServiceGenerationConfig contains configurations for generating a new service. -type ServiceGenerationConfig struct { - ResourceName string - Methods []string - SkipTest bool -} - -// CommandGenerator is an interface to create or destroy user-defined command tempates. -type CommandGenerator interface { - GenerateCommand(name string) error - DestroyCommand(name string) error + GenerateProject(rootDir, pkgName string) error } diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ProjectGenerator-.gitignore b/pkg/grapicmd/internal/module/generator/.snapshots/Test_ProjectGenerator-.gitignore deleted file mode 100644 index 9746e024..00000000 --- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ProjectGenerator-.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -*.so -/vendor -/bin -/tmp - diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ProjectGenerator-Gopkg.toml b/pkg/grapicmd/internal/module/generator/.snapshots/Test_ProjectGenerator-Gopkg.toml deleted file mode 100644 index e162e021..00000000 --- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ProjectGenerator-Gopkg.toml +++ /dev/null @@ -1,10 +0,0 @@ -required = [ - "github.com/golang/protobuf/protoc-gen-go", - "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway", - "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger", -] - -[[constraint]] - name = "github.com/izumin5210/grapi" - version = "" - diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ProjectGenerator-grapi.toml b/pkg/grapicmd/internal/module/generator/.snapshots/Test_ProjectGenerator-grapi.toml deleted file mode 100644 index 2b9e69ca..00000000 --- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ProjectGenerator-grapi.toml +++ /dev/null @@ -1,26 +0,0 @@ -[grapi] -server_dir = "./app/server" - -[protoc] -protos_dir = "./api/protos" -out_dir = "./api" -import_dirs = [ - "./vendor/github.com/grpc-ecosystem/grpc-gateway", - "./vendor/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis", -] - - [[protoc.plugins]] - path = "./vendor/github.com/golang/protobuf/protoc-gen-go" - name = "go" - args = { plugins = "grpc", paths = "source_relative" } - - [[protoc.plugins]] - path = "./vendor/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway" - name = "grpc-gateway" - args = { logtostderr = true } - - [[protoc.plugins]] - path = "./vendor/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger" - name = "swagger" - args = { logtostderr = true } - diff --git a/pkg/grapicmd/internal/module/generator/base.go b/pkg/grapicmd/internal/module/generator/base.go deleted file mode 100644 index 3b624191..00000000 --- a/pkg/grapicmd/internal/module/generator/base.go +++ /dev/null @@ -1,159 +0,0 @@ -package generator - -import ( - "path/filepath" - "sort" - "strings" - - "github.com/izumin5210/clicontrib/pkg/clog" - assets "github.com/jessevdk/go-assets" - "github.com/pkg/errors" - "github.com/spf13/afero" - - "github.com/izumin5210/grapi/pkg/grapicmd/internal/module" - "github.com/izumin5210/grapi/pkg/grapicmd/util/fs" -) - -type baseGenerator interface { - Generate(dir string, data interface{}, cfg generationConfig) error - Destroy(dir string, data interface{}) error -} - -func newBaseGenerator(tmplFs *assets.FileSystem, fs afero.Fs, ui module.UI) baseGenerator { - return &baseGeneratorImpl{ - tmplFs: tmplFs, - fs: fs, - ui: ui, - } -} - -type baseGeneratorImpl struct { - tmplFs *assets.FileSystem - fs afero.Fs - ui module.UI -} - -type generationConfig struct { - skipTest bool -} - -func (g *baseGeneratorImpl) Generate(dir string, data interface{}, genCfg generationConfig) error { - for _, tmplPath := range g.sortedEntryPaths() { - if genCfg.skipTest && strings.HasSuffix(tmplPath, "_test.go.tmpl") { - continue - } - entry := g.tmplFs.Files[tmplPath] - path, err := TemplateString(strings.TrimSuffix(tmplPath, ".tmpl")).Compile(data) - if err != nil { - return errors.Wrapf(err, "failed to parse path: %s", path) - } - absPath := filepath.Join(dir, path) - dirPath := filepath.Dir(absPath) - - // create directory if not exists - if err := fs.CreateDirIfNotExists(g.fs, dirPath); err != nil { - return errors.WithStack(err) - } - - // generate content - body, err := TemplateString(string(entry.Data)).Compile(data) - if err != nil { - return errors.Wrapf(err, "failed to generate %s", path) - } - - // check existed entries - st := statusCreate - if ok, err := afero.Exists(g.fs, absPath); err != nil { - // TODO: handle an error - st = statusSkipped - } else if ok { - existedBody, err := afero.ReadFile(g.fs, absPath) - if err != nil { - // TODO: handle an error - st = statusSkipped - } - if string(existedBody) == body { - st = statusIdentical - } else { - st = statusSkipped - g.ui.ItemFailure(path[1:] + " is conflicted.") - if ok, err := g.ui.Confirm("Overwite it?"); err != nil { - clog.Error("failed to confirm to apply", "error", err) - return errors.WithStack(err) - } else if ok { - st = statusCreate - } - } - } - - // create - if st.ShouldCreate() { - err = afero.WriteFile(g.fs, absPath, []byte(body), 0644) - if err != nil { - return errors.Wrapf(err, "failed to write %s", path) - } - } - - st.Fprint(g.ui, path[1:]) - } - - return nil -} - -func (g *baseGeneratorImpl) Destroy(dir string, data interface{}) error { - for _, tmplPath := range g.sortedEntryPaths() { - path, err := TemplateString(strings.TrimSuffix(tmplPath, ".tmpl")).Compile(data) - if err != nil { - return errors.Wrapf(err, "failed to parse path: %s", path) - } - absPath := filepath.Join(dir, path) - - st := statusSkipped - if ok, err := afero.Exists(g.fs, absPath); err != nil { - g.ui.ItemFailure("failed to get " + path[1:]) - return errors.WithStack(err) - } else if ok { - err = g.fs.Remove(absPath) - if err != nil { - g.ui.ItemFailure("failed to remove " + path[1:]) - return errors.WithStack(err) - } - st = statusDelete - } - - st.Fprint(g.ui, path[1:]) - - dirPath := filepath.Dir(path) - absDirPath := filepath.Dir(absPath) - if ok, err := afero.DirExists(g.fs, absDirPath); err == nil && ok { - if r, err := afero.Glob(g.fs, filepath.Join(absDirPath, "*")); err == nil && len(r) == 0 { - err = g.fs.Remove(absDirPath) - if err != nil { - g.ui.ItemFailure("failed to remove " + dirPath[1:]) - return errors.Wrapf(err, "failed to remove %q", dirPath[1:]) - } - statusDelete.Fprint(g.ui, dirPath[1:]) - } - } - } - - return nil -} - -func (g *baseGeneratorImpl) sortedEntryPaths() []string { - rootFiles := make([]string, 0, len(g.tmplFs.Files)) - tmplPaths := make([]string, 0, len(g.tmplFs.Files)) - for path, entry := range g.tmplFs.Files { - if entry.IsDir() { - continue - } - if strings.Count(entry.Path[1:], "/") == 0 { - rootFiles = append(rootFiles, path) - } else { - tmplPaths = append(tmplPaths, path) - } - } - sort.Strings(rootFiles) - sort.Strings(tmplPaths) - return append(rootFiles, tmplPaths...) -} diff --git a/pkg/grapicmd/internal/module/generator/command.go b/pkg/grapicmd/internal/module/generator/command.go deleted file mode 100644 index aa65f585..00000000 --- a/pkg/grapicmd/internal/module/generator/command.go +++ /dev/null @@ -1,29 +0,0 @@ -package generator - -import ( - "github.com/pkg/errors" - "github.com/spf13/afero" - - "github.com/izumin5210/grapi/pkg/grapicmd/internal/module" - "github.com/izumin5210/grapi/pkg/grapicmd/internal/module/generator/template" -) - -type commandGenerator struct { - baseGenerator - rootDir string -} - -func newCommandGenerator(fs afero.Fs, ui module.UI, rootDir string) module.CommandGenerator { - return &commandGenerator{ - baseGenerator: newBaseGenerator(template.Command, fs, ui), - rootDir: rootDir, - } -} - -func (g *commandGenerator) GenerateCommand(name string) error { - return errors.WithStack(g.Generate(g.rootDir, map[string]string{"name": name}, generationConfig{})) -} - -func (g *commandGenerator) DestroyCommand(name string) error { - return errors.WithStack(g.Destroy(g.rootDir, map[string]string{"name": name})) -} diff --git a/pkg/grapicmd/internal/module/generator/command_test.go b/pkg/grapicmd/internal/module/generator/command_test.go deleted file mode 100644 index b525f0ee..00000000 --- a/pkg/grapicmd/internal/module/generator/command_test.go +++ /dev/null @@ -1,80 +0,0 @@ -package generator - -import ( - "go/build" - "path/filepath" - "testing" - - "github.com/bradleyjkemp/cupaloy" - "github.com/golang/mock/gomock" - "github.com/spf13/afero" - - moduletesting "github.com/izumin5210/grapi/pkg/grapicmd/internal/module/testing" - "github.com/izumin5210/grapi/pkg/grapicmd/util/fs" -) - -func Test_CommandGenerator(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - tmpBuildContext := fs.BuildContext - defer func() { fs.BuildContext = tmpBuildContext }() - fs.BuildContext = build.Context{ - GOPATH: "/home", - } - - rootDir := "/home/src/testapp" - - ui := moduletesting.NewMockUI(ctrl) - ui.EXPECT().ItemSuccess(gomock.Any()).AnyTimes() - fs := afero.NewMemMapFs() - - generator := newCommandGenerator(fs, ui, rootDir) - - name := "foo" - files := []string{ - "cmd/foo/run.go", - } - - t.Run("Generate", func(t *testing.T) { - err := generator.GenerateCommand(name) - - if err != nil { - t.Errorf("returned an error %v", err) - } - - for _, file := range files { - t.Run(file, func(t *testing.T) { - data, err := afero.ReadFile(fs, filepath.Join(rootDir, file)) - - if err != nil { - t.Errorf("returned an error %v", err) - } - - cupaloy.SnapshotT(t, string(data)) - }) - } - }) - - t.Run("Destroy", func(t *testing.T) { - err := generator.DestroyCommand(name) - - if err != nil { - t.Errorf("returned an error %v", err) - } - - for _, file := range files { - t.Run(file, func(t *testing.T) { - ok, err := afero.Exists(fs, filepath.Join(rootDir, file)) - - if err != nil { - t.Errorf("Exists(fs, %q) returned an error %v", file, err) - } - - if ok { - t.Errorf("%q should not exist", file) - } - }) - } - }) -} diff --git a/pkg/grapicmd/internal/module/generator/generator.go b/pkg/grapicmd/internal/module/generator/generator.go deleted file mode 100644 index c368e6b0..00000000 --- a/pkg/grapicmd/internal/module/generator/generator.go +++ /dev/null @@ -1,22 +0,0 @@ -package generator - -import ( - "github.com/spf13/afero" - - "github.com/izumin5210/grapi/pkg/grapicmd/internal/module" -) - -// New creates a module.Generator instance. -func New(fs afero.Fs, ui module.UI, rootDir, protoDir, protoOutDir, serverDir, version string) module.Generator { - return &generator{ - ProjectGenerator: newProjectGenerator(fs, ui, version), - ServiceGenerator: newServiceGenerator(fs, ui, rootDir, protoDir, protoOutDir, serverDir), - CommandGenerator: newCommandGenerator(fs, ui, rootDir), - } -} - -type generator struct { - module.ProjectGenerator - module.ServiceGenerator - module.CommandGenerator -} diff --git a/pkg/grapicmd/internal/module/generator/project.go b/pkg/grapicmd/internal/module/generator/project.go deleted file mode 100644 index d6ceb430..00000000 --- a/pkg/grapicmd/internal/module/generator/project.go +++ /dev/null @@ -1,35 +0,0 @@ -package generator - -import ( - "github.com/pkg/errors" - "github.com/spf13/afero" - - "github.com/izumin5210/grapi/pkg/grapicmd/internal/module" - "github.com/izumin5210/grapi/pkg/grapicmd/internal/module/generator/template" - "github.com/izumin5210/grapi/pkg/grapicmd/util/fs" -) - -type projectGenerator struct { - baseGenerator - version string -} - -func newProjectGenerator(fs afero.Fs, ui module.UI, version string) module.ProjectGenerator { - return &projectGenerator{ - baseGenerator: newBaseGenerator(template.Init, fs, ui), - version: version, - } -} - -func (g *projectGenerator) GenerateProject(rootDir string, useHead bool) error { - importPath, err := fs.GetImportPath(rootDir) - if err != nil { - return errors.WithStack(err) - } - data := map[string]interface{}{ - "importPath": importPath, - "version": g.version, - "headUsed": useHead, - } - return g.Generate(rootDir, data, generationConfig{}) -} diff --git a/pkg/grapicmd/internal/module/generator/project_test.go b/pkg/grapicmd/internal/module/generator/project_test.go deleted file mode 100644 index e28320c8..00000000 --- a/pkg/grapicmd/internal/module/generator/project_test.go +++ /dev/null @@ -1,59 +0,0 @@ -package generator - -import ( - "go/build" - "path/filepath" - "testing" - - "github.com/bradleyjkemp/cupaloy" - "github.com/golang/mock/gomock" - "github.com/spf13/afero" - - moduletesting "github.com/izumin5210/grapi/pkg/grapicmd/internal/module/testing" - "github.com/izumin5210/grapi/pkg/grapicmd/util/fs" -) - -func Test_ProjectGenerator(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - tmpBuildContext := fs.BuildContext - defer func() { fs.BuildContext = tmpBuildContext }() - fs.BuildContext = build.Context{ - GOPATH: "/home", - } - - rootDir := "/home/src/testapp" - - ui := moduletesting.NewMockUI(ctrl) - ui.EXPECT().ItemSuccess(gomock.Any()).AnyTimes() - fs := afero.NewMemMapFs() - - generator := newProjectGenerator(fs, ui, "") - - err := generator.GenerateProject(rootDir, false) - - if err != nil { - t.Errorf("returned an error %v", err) - } - - files := []string{ - ".gitignore", - "Gopkg.toml", - "grapi.toml", - "app/run.go", - "cmd/server/run.go", - } - - for _, file := range files { - t.Run(file, func(t *testing.T) { - data, err := afero.ReadFile(fs, filepath.Join(rootDir, file)) - - if err != nil { - t.Errorf("returned an error %v", err) - } - - cupaloy.SnapshotT(t, string(data)) - }) - } -} diff --git a/pkg/grapicmd/internal/module/generator/service.go b/pkg/grapicmd/internal/module/generator/service.go deleted file mode 100644 index cfef2efb..00000000 --- a/pkg/grapicmd/internal/module/generator/service.go +++ /dev/null @@ -1,406 +0,0 @@ -package generator - -import ( - "path/filepath" - "sort" - "strings" - - "github.com/jinzhu/inflection" - "github.com/pkg/errors" - "github.com/serenize/snaker" - "github.com/spf13/afero" - - "github.com/izumin5210/grapi/pkg/grapicmd/internal/module" - "github.com/izumin5210/grapi/pkg/grapicmd/internal/module/generator/template" - "github.com/izumin5210/grapi/pkg/grapicmd/util/fs" -) - -type serviceGenerator struct { - baseGenerator - rootDir, protoDir, protoOutDir, serverDir string -} - -func newServiceGenerator(fs afero.Fs, ui module.UI, rootDir, protoDir, protoOutDir, serverDir string) module.ServiceGenerator { - if protoDir == "" { - protoDir = filepath.Join("api", "protos") - } - if protoOutDir == "" { - protoOutDir = filepath.Join("api") - } - if serverDir == "" { - serverDir = filepath.Join("app", "server") - } - - return &serviceGenerator{ - baseGenerator: newBaseGenerator(template.Service, fs, ui), - rootDir: rootDir, - protoDir: protoDir, - protoOutDir: protoOutDir, - serverDir: serverDir, - } -} - -func (g *serviceGenerator) GenerateService(name string, cfg module.ServiceGenerationConfig) error { - data, err := g.createParams(name, cfg.ResourceName, cfg.Methods) - if err != nil { - return errors.WithStack(err) - } - return g.Generate(g.rootDir, data, generationConfig{skipTest: cfg.SkipTest}) -} - -func (g *serviceGenerator) ScaffoldService(name string, cfg module.ServiceGenerationConfig) error { - data, err := g.createParams(name, cfg.ResourceName, []string{"list", "get", "create", "update", "delete"}) - if err != nil { - return errors.WithStack(err) - } - return g.Generate(g.rootDir, data, generationConfig{skipTest: cfg.SkipTest}) -} - -func (g *serviceGenerator) DestroyService(name string) error { - data, err := g.createParams(name, "", []string{}) - if err != nil { - return errors.WithStack(err) - } - return g.Destroy(g.rootDir, data) -} - -type nameParams struct { - pluralCamel string - pluralCamelLower string - pluralSnake string - singularCamel string - singularCamelLower string - singularSnake string -} - -func createNameParams(name string) nameParams { - nameParams := nameParams{ - pluralCamel: inflection.Plural(snaker.SnakeToCamel(name)), - singularCamel: inflection.Singular(snaker.SnakeToCamel(name)), - } - nameParams.pluralCamelLower = strings.ToLower(string(nameParams.pluralCamel[0])) + nameParams.pluralCamel[1:] - nameParams.pluralSnake = snaker.CamelToSnake(nameParams.pluralCamel) - nameParams.singularCamelLower = strings.ToLower(string(nameParams.singularCamel[0])) + nameParams.singularCamel[1:] - nameParams.singularSnake = snaker.CamelToSnake(nameParams.singularCamel) - return nameParams -} - -type serviceParams struct { - ProtoDir string - ProtoOutDir string - ServerDir string - Path string - ServiceName string - Methods []serviceMethodParams - Proto serviceProtoParams - PbGo servicePbGoParams - Go serviceGoParams -} - -type serviceProtoParams struct { - Package string - Imports []string - Messages []serviceMethodMessage -} - -type servicePbGoParams struct { - PackageName string - PackagePath string -} - -type serviceGoParams struct { - Package string - Imports []string - TestImports []string - ServerName string - StructName string -} - -type serviceMethodsParams struct { - Methods []serviceMethodParams - ProtoImports []string - GoImports []string - Messages []serviceMethodMessage -} - -type serviceMethodParams struct { - Method string - HTTP serviceMethodHTTPParams - requestCommon string - requestGo string - requestProto string - responseCommon string - responseGo string - responseProto string -} - -func (p *serviceMethodParams) RequestGo(pkg string) string { - if p.requestGo == "" { - return pkg + "." + p.requestCommon - } - return p.requestGo -} - -func (p *serviceMethodParams) RequestProto() string { - if p.requestProto == "" { - return p.requestCommon - } - return p.requestProto -} - -func (p *serviceMethodParams) ResponseGo(pkg string) string { - if p.responseGo == "" { - return pkg + "." + p.responseCommon - } - return p.responseGo -} - -func (p *serviceMethodParams) ResponseProto() string { - if p.responseProto == "" { - return p.responseCommon - } - return p.responseProto -} - -type serviceMethodMessage struct { - Name string - Fields []serviceMethodMessageField -} - -type serviceMethodMessageField struct { - Name string - Type string - Repeated bool - Tag uint -} - -type serviceMethodHTTPParams struct { - Method string - Path string - Body string -} - -func (g *serviceGenerator) createParams(path string, resName string, methodNames []string) (*serviceParams, error) { - // github.com/foo/bar - importPath, err := fs.GetImportPath(g.rootDir) - if err != nil { - return nil, errors.WithStack(err) - } - - // path => baz/qux/quux - path = strings.Replace(path, "-", "_", -1) - - // quux - name := filepath.Base(path) - - nameParams := createNameParams(name) - - // Quux - serviceName := nameParams.singularCamel - // quux - localServiceName := strings.ToLower(string(serviceName[0])) + serviceName[1:] - - // baz/qux - packagePath := filepath.Dir(path) - // qux - packageName := filepath.Base(packagePath) - - // api/baz/qux - pbgoPackagePath := filepath.Join(g.protoOutDir, packagePath) - // qux_pb - pbgoPackageName := filepath.Base(pbgoPackagePath) + "_pb" - - if packagePath == "." { - packagePath = filepath.Base(g.serverDir) - packageName = packagePath - pbgoPackagePath = g.protoOutDir - pbgoPackageName = filepath.Base(pbgoPackagePath) + "_pb" - } - - protoPackageChunks := []string{} - for _, pkg := range strings.Split(filepath.ToSlash(filepath.Join(importPath, g.protoOutDir, filepath.Dir(path))), "/") { - chunks := strings.Split(strings.Replace(pkg, "-", "_", -1), ".") - for i := len(chunks) - 1; i >= 0; i-- { - protoPackageChunks = append(protoPackageChunks, chunks[i]) - } - } - // com.github.foo.bar.baz.qux - protoPackage := strings.Join(protoPackageChunks, ".") - - protoImports := []string{ - "google/api/annotations.proto", - } - goImports := []string{ - "github.com/izumin5210/grapi/pkg/grapiserver", - "google.golang.org/grpc/codes", - "google.golang.org/grpc/status", - } - goTestImports := []string{} - - resNameParams := nameParams - if resName != "" { - resNameParams = createNameParams(resName) - } - methods := g.createMethodParams(resNameParams, methodNames) - - protoImports = append(protoImports, methods.ProtoImports...) - sort.Strings(protoImports) - goImports = append(goImports, methods.GoImports...) - sort.Strings(goImports) - goTestImports = append(goTestImports, methods.GoImports...) - sort.Strings(goTestImports) - - params := &serviceParams{ - ProtoDir: g.protoDir, - ProtoOutDir: g.protoOutDir, - ServerDir: g.serverDir, - Path: path, - ServiceName: serviceName, - Methods: methods.Methods, - Proto: serviceProtoParams{ - Package: protoPackage, - Imports: protoImports, - Messages: methods.Messages, - }, - PbGo: servicePbGoParams{ - PackageName: pbgoPackageName, - PackagePath: filepath.ToSlash(filepath.Join(importPath, pbgoPackagePath)), - }, - Go: serviceGoParams{ - Package: packageName, - Imports: goImports, - TestImports: goTestImports, - ServerName: serviceName + "Service" + "Server", - StructName: localServiceName + "Service" + "Server" + "Impl", - }, - } - - return params, nil -} - -func (g *serviceGenerator) createMethodParams(name nameParams, methods []string) ( - params serviceMethodsParams, -) { - id := name.singularSnake + "_id" - resource := &serviceMethodMessage{ - Name: name.singularCamel, - Fields: []serviceMethodMessageField{{Name: id, Type: "string", Tag: 1}}, - } - - basicMethods := [5]*serviceMethodParams{} - customMethods := []serviceMethodParams{} - basicMessages := [7]*serviceMethodMessage{} - customMessages := []serviceMethodMessage{} - - for _, meth := range methods { - switch strings.ToLower(meth) { - case "list": - methodName := "List" + name.pluralCamel - reqName := methodName + "Request" - respName := methodName + "Response" - basicMethods[0] = &serviceMethodParams{ - Method: methodName, - requestCommon: reqName, - responseCommon: respName, - HTTP: serviceMethodHTTPParams{Method: "get", Path: name.pluralSnake}, - } - basicMessages[0] = resource - basicMessages[1] = &serviceMethodMessage{Name: reqName} - basicMessages[2] = &serviceMethodMessage{ - Name: respName, - Fields: []serviceMethodMessageField{{Name: name.pluralSnake, Type: name.singularCamel, Repeated: true, Tag: 1}}, - } - case "get": - methodName := "Get" + name.singularCamel - reqName := methodName + "Request" - basicMethods[1] = &serviceMethodParams{ - Method: methodName, - requestCommon: reqName, - responseCommon: resource.Name, - HTTP: serviceMethodHTTPParams{Method: "get", Path: name.pluralSnake + "/{" + id + "}"}, - } - basicMessages[0] = resource - basicMessages[3] = &serviceMethodMessage{ - Name: reqName, - Fields: []serviceMethodMessageField{{Name: id, Type: "string", Tag: 1}}, - } - case "create": - methodName := "Create" + name.singularCamel - reqName := methodName + "Request" - basicMethods[2] = &serviceMethodParams{ - Method: methodName, - requestCommon: reqName, - responseCommon: resource.Name, - HTTP: serviceMethodHTTPParams{Method: "post", Path: name.pluralSnake, Body: name.singularSnake}, - } - basicMessages[0] = resource - basicMessages[4] = &serviceMethodMessage{ - Name: reqName, - Fields: []serviceMethodMessageField{{Name: name.singularSnake, Type: name.singularCamel, Tag: 1}}, - } - case "update": - methodName := "Update" + name.singularCamel - reqName := methodName + "Request" - basicMethods[3] = &serviceMethodParams{ - Method: methodName, - requestCommon: reqName, - responseCommon: resource.Name, - HTTP: serviceMethodHTTPParams{Method: "patch", Path: name.pluralSnake + "/{" + name.singularSnake + "." + id + "}", Body: name.singularSnake}, - } - basicMessages[0] = resource - basicMessages[5] = &serviceMethodMessage{ - Name: reqName, - Fields: []serviceMethodMessageField{{Name: name.singularSnake, Type: name.singularCamel, Tag: 1}}, - } - case "delete": - methodName := "Delete" + name.singularCamel - reqName := methodName + "Request" - basicMethods[4] = &serviceMethodParams{ - Method: methodName, - requestCommon: reqName, - responseProto: "google.protobuf.Empty", - responseGo: "empty.Empty", - HTTP: serviceMethodHTTPParams{Method: "delete", Path: name.pluralSnake + "/{" + id + "}"}, - } - basicMessages[6] = &serviceMethodMessage{ - Name: reqName, - Fields: []serviceMethodMessageField{{Name: id, Type: "string", Tag: 1}}, - } - params.ProtoImports = append(params.ProtoImports, "google/protobuf/empty.proto") - params.GoImports = append(params.GoImports, "github.com/golang/protobuf/ptypes/empty") - default: - methodName := snaker.SnakeToCamel(meth) - reqName := methodName + "Request" - respName := methodName + "Response" - customMethods = append(customMethods, serviceMethodParams{ - Method: methodName, - requestCommon: reqName, - responseCommon: respName, - HTTP: serviceMethodHTTPParams{Method: "get", Path: name.pluralSnake + "/" + snaker.CamelToSnake(meth)}, - }) - customMessages = append( - customMessages, - serviceMethodMessage{Name: reqName}, - serviceMethodMessage{Name: respName}, - ) - } - } - - for _, meth := range basicMethods { - if meth != nil { - params.Methods = append(params.Methods, *meth) - } - } - for _, msg := range basicMessages { - if msg != nil { - params.Messages = append(params.Messages, *msg) - } - } - for _, meth := range customMethods { - params.Methods = append(params.Methods, meth) - } - for _, msg := range customMessages { - params.Messages = append(params.Messages, msg) - } - return -} diff --git a/pkg/grapicmd/internal/module/generator/service_test.go b/pkg/grapicmd/internal/module/generator/service_test.go deleted file mode 100644 index 339852ad..00000000 --- a/pkg/grapicmd/internal/module/generator/service_test.go +++ /dev/null @@ -1,244 +0,0 @@ -package generator - -import ( - "go/build" - "path/filepath" - "strings" - "testing" - - "github.com/bradleyjkemp/cupaloy" - "github.com/golang/mock/gomock" - "github.com/spf13/afero" - - "github.com/izumin5210/grapi/pkg/grapicmd/internal/module" - moduletesting "github.com/izumin5210/grapi/pkg/grapicmd/internal/module/testing" - "github.com/izumin5210/grapi/pkg/grapicmd/util/fs" -) - -func Test_ServiceGenerator(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - tmpBuildContext := fs.BuildContext - defer func() { fs.BuildContext = tmpBuildContext }() - fs.BuildContext = build.Context{ - GOPATH: "/home", - } - - rootDir := "/home/src/testapp" - - ui := moduletesting.NewMockUI(ctrl) - ui.EXPECT().ItemSuccess(gomock.Any()).AnyTimes() - ui.EXPECT().ItemSkipped(gomock.Any()).AnyTimes() - fs := afero.NewMemMapFs() - - cases := []struct { - name string - args []string - files []string - skippedFiles map[string]struct{} - scaffold bool - skipTest bool - resource string - protoDir string - protoOutDir string - serverDir string - }{ - { - name: "foo", - files: []string{ - "api/protos/foo.proto", - "app/server/foo_server.go", - "app/server/foo_server_register_funcs.go", - "app/server/foo_server_test.go", - }, - }, - { - name: "foo/bar", - files: []string{ - "api/protos/foo/bar.proto", - "app/server/foo/bar_server.go", - "app/server/foo/bar_server_register_funcs.go", - "app/server/foo/bar_server_test.go", - }, - }, - { - name: "foo/bar_baz", - files: []string{ - "api/protos/foo/bar_baz.proto", - "app/server/foo/bar_baz_server.go", - "app/server/foo/bar_baz_server_register_funcs.go", - "app/server/foo/bar_baz_server_test.go", - }, - }, - { - name: "foo/bar-baz", - files: []string{ - "api/protos/foo/bar_baz.proto", - "app/server/foo/bar_baz_server.go", - "app/server/foo/bar_baz_server_register_funcs.go", - "app/server/foo/bar_baz_server_test.go", - }, - }, - { - name: "foo/bar-baz", - args: []string{"list", "create", "delete"}, - files: []string{ - "api/protos/foo/bar_baz.proto", - "app/server/foo/bar_baz_server.go", - "app/server/foo/bar_baz_server_register_funcs.go", - "app/server/foo/bar_baz_server_test.go", - }, - }, - { - name: "foo/bar-baz", - args: []string{"list", "create", "rename", "delete", "move_move"}, - files: []string{ - "api/protos/foo/bar_baz.proto", - "app/server/foo/bar_baz_server.go", - "app/server/foo/bar_baz_server_register_funcs.go", - "app/server/foo/bar_baz_server_test.go", - }, - }, - { - name: "qux", - files: []string{ - "pkg/foo/protos/qux.proto", - "app/server/qux_server.go", - "app/server/qux_server_register_funcs.go", - "app/server/qux_server_test.go", - }, - protoDir: "pkg/foo/protos", - }, - { - name: "quux", - files: []string{ - "api/protos/quux.proto", - "app/server/quux_server.go", - "app/server/quux_server_register_funcs.go", - "app/server/quux_server_test.go", - }, - protoOutDir: "api/out", - }, - { - name: "corge", - files: []string{ - "api/protos/corge.proto", - "pkg/foo/server/corge_server.go", - "pkg/foo/server/corge_server_register_funcs.go", - "pkg/foo/server/corge_server_test.go", - }, - serverDir: "pkg/foo/server", - }, - { - name: "book", - files: []string{ - "api/protos/book.proto", - "app/server/book_server.go", - "app/server/book_server_register_funcs.go", - "app/server/book_server_test.go", - }, - scaffold: true, - }, - { - name: "book", - files: []string{ - "api/protos/book.proto", - "app/server/book_server.go", - "app/server/book_server_register_funcs.go", - }, - skippedFiles: map[string]struct{}{ - "app/server/book_server_test.go": {}, - }, - skipTest: true, - }, - { - name: "library", - files: []string{ - "api/protos/library.proto", - "app/server/library_server.go", - "app/server/library_server_register_funcs.go", - "app/server/library_server_test.go", - }, - resource: "book", - scaffold: true, - }, - } - - for _, c := range cases { - test := c.name - if len(c.args) > 0 { - test += " with " + strings.Join(c.args, ",") - } - - generator := newServiceGenerator(fs, ui, rootDir, c.protoDir, c.protoOutDir, c.serverDir) - - t.Run(test, func(t *testing.T) { - test := "Generate" - if c.scaffold { - test = "Scaffold" - } - if c.skipTest { - test += " without test" - } - t.Run(test, func(t *testing.T) { - var err error - if c.scaffold { - err = generator.ScaffoldService(c.name, module.ServiceGenerationConfig{ResourceName: c.resource, SkipTest: c.skipTest}) - } else { - err = generator.GenerateService(c.name, module.ServiceGenerationConfig{ResourceName: c.resource, Methods: c.args, SkipTest: c.skipTest}) - } - - if err != nil { - t.Errorf("returned an error: %v", err) - } - - for _, file := range c.files { - t.Run(file, func(t *testing.T) { - if _, ok := c.skippedFiles[file]; ok { - ok, err := afero.Exists(fs, file) - - if err != nil { - t.Errorf("returned an error: %v", err) - } - - if ok { - t.Error("should not exist") - } - } else { - data, err := afero.ReadFile(fs, filepath.Join(rootDir, file)) - - if err != nil { - t.Errorf("returned an error: %v", err) - } - - cupaloy.SnapshotT(t, string(data)) - } - }) - } - }) - - t.Run("Destroy", func(t *testing.T) { - err := generator.DestroyService(c.name) - - if err != nil { - t.Errorf("returned an error: %v", err) - } - - for _, file := range c.files { - t.Run(file, func(t *testing.T) { - ok, err := afero.Exists(fs, filepath.Join(rootDir, file)) - - if err != nil { - t.Errorf("Exists(fs, %q) returned an error: %v", file, err) - } - - if ok { - t.Errorf("%q should not exist", file) - } - }) - } - }) - }) - } -} diff --git a/pkg/grapicmd/internal/module/generator/status.go b/pkg/grapicmd/internal/module/generator/status.go deleted file mode 100644 index 8aab4756..00000000 --- a/pkg/grapicmd/internal/module/generator/status.go +++ /dev/null @@ -1,38 +0,0 @@ -package generator - -import "github.com/izumin5210/grapi/pkg/grapicmd/internal/module" - -type status int - -const ( - statusCreate status = iota - statusDelete - statusExist - statusIdentical - statusConflicted - statusForce - statusSkipped -) - -var ( - creatableStatusSet = map[status]struct{}{ - statusCreate: {}, - statusForce: {}, - } -) - -func (s status) Fprint(ui module.UI, msg string) { - switch s { - case statusCreate, statusForce, statusDelete: - ui.ItemSuccess(msg) - case statusConflicted: - ui.ItemFailure(msg) - default: - ui.ItemSkipped(msg) - } -} - -func (s status) ShouldCreate() bool { - _, ok := creatableStatusSet[s] - return ok -} diff --git a/pkg/grapicmd/internal/module/generator/template/command.go b/pkg/grapicmd/internal/module/generator/template/command.go deleted file mode 100644 index 097f29b7..00000000 --- a/pkg/grapicmd/internal/module/generator/template/command.go +++ /dev/null @@ -1,33 +0,0 @@ -package template - -import ( - "time" - - "github.com/jessevdk/go-assets" -) - -var _Command9acbc40e153d42b0b3e06ca83e7c53e3670d8cdc = "package main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n)\n\nfunc main() {\n\tos.Exit(run())\n}\n\nfunc run() int {\n\tfmt.Println(\"It works!\")\n\treturn 0\n}\n" - -// Command returns go-assets FileSystem -var Command = assets.NewFileSystem(map[string][]string{"/cmd/{{ .name }}": []string{"run.go.tmpl"}, "/": []string{}, "/cmd": []string{}}, map[string]*assets.File{ - "/cmd/{{ .name }}": &assets.File{ - Path: "/cmd/{{ .name }}", - FileMode: 0x800001ed, - Mtime: time.Unix(1520753819, 1520753819000000000), - Data: nil, - }, "/cmd/{{ .name }}/run.go.tmpl": &assets.File{ - Path: "/cmd/{{ .name }}/run.go.tmpl", - FileMode: 0x1a4, - Mtime: time.Unix(1520753819, 1520753819000000000), - Data: []byte(_Command9acbc40e153d42b0b3e06ca83e7c53e3670d8cdc), - }, "/": &assets.File{ - Path: "/", - FileMode: 0x800001ed, - Mtime: time.Unix(1520753819, 1520753819000000000), - Data: nil, - }, "/cmd": &assets.File{ - Path: "/cmd", - FileMode: 0x800001ed, - Mtime: time.Unix(1520753819, 1520753819000000000), - Data: nil, - }}, "") diff --git a/pkg/grapicmd/internal/module/generator/template/gen.go b/pkg/grapicmd/internal/module/generator/template/gen.go deleted file mode 100644 index 0d097c3c..00000000 --- a/pkg/grapicmd/internal/module/generator/template/gen.go +++ /dev/null @@ -1,5 +0,0 @@ -//go:generate go-assets-builder -p template -s="/init" -o init.go -v Init init -//go:generate go-assets-builder -p template -s="/service" -o service.go -v Service service -//go:generate go-assets-builder -p template -s="/command" -o command.go -v Command command - -package template diff --git a/pkg/grapicmd/internal/module/generator/template/init.go b/pkg/grapicmd/internal/module/generator/template/init.go deleted file mode 100644 index 55e5789c..00000000 --- a/pkg/grapicmd/internal/module/generator/template/init.go +++ /dev/null @@ -1,89 +0,0 @@ -package template - -import ( - "time" - - "github.com/jessevdk/go-assets" -) - -var _Initbc4053f4dd26ceb67e4646e8c1d2cc75897c4dd0 = "package app\n\nimport (\n\t\"github.com/izumin5210/grapi/pkg/grapiserver\"\n)\n\n// Run starts the grapiserver.\nfunc Run() error {\n\ts := grapiserver.New(\n\t\tgrapiserver.WithDefaultLogger(),\n\t\tgrapiserver.WithServers(\n\t\t// TODO\n\t\t),\n\t)\n\treturn s.Serve()\n}\n" -var _Init38e76c5db8962fa825cf2bd8b23a2dc985c4513e = "*.so\n/vendor\n/bin\n/tmp\n" -var _Init71ed560e812a4261bc8b56d9feaef4800830e0b7 = "" -var _Initd135936e91856b6159ac2eedcf89aa9f07773f82 = "package main\n\nimport (\n\t\"os\"\n\n\t\"google.golang.org/grpc/grpclog\"\n\n\t\"{{ .importPath }}/app\"\n)\n\nfunc main() {\n\tos.Exit(run())\n}\n\nfunc run() int {\n\terr := app.Run()\n\tif err != nil {\n\t\tgrpclog.Errorf(\"server was shutdown with errors: %v\", err)\n\t\treturn 1\n\t}\n\treturn 0\n}\n" -var _Init23b808cac963edf44a497827f2a6eff5ddac970f = "required = [\n \"github.com/golang/protobuf/protoc-gen-go\",\n \"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway\",\n \"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger\",\n]\n\n[[constraint]]\n{{- if .headUsed }}\n branch = \"master\"\n{{- end }}\n name = \"github.com/izumin5210/grapi\"\n{{- if not .headUsed }}\n version = \"{{ .version }}\"\n{{- end }}\n" -var _Init8d21956ba8abe388f964e47be0f7e5d170a2fce5 = "" -var _Initc051c9ff1a8e446bc9636d3144c2775a7e235322 = "[grapi]\nserver_dir = \"./app/server\"\n\n[protoc]\nprotos_dir = \"./api/protos\"\nout_dir = \"./api\"\nimport_dirs = [\n \"./vendor/github.com/grpc-ecosystem/grpc-gateway\",\n \"./vendor/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis\",\n]\n\n [[protoc.plugins]]\n path = \"./vendor/github.com/golang/protobuf/protoc-gen-go\"\n name = \"go\"\n args = { plugins = \"grpc\", paths = \"source_relative\" }\n\n [[protoc.plugins]]\n path = \"./vendor/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway\"\n name = \"grpc-gateway\"\n args = { logtostderr = true }\n\n [[protoc.plugins]]\n path = \"./vendor/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger\"\n name = \"swagger\"\n args = { logtostderr = true }\n" - -// Init returns go-assets FileSystem -var Init = assets.NewFileSystem(map[string][]string{"/app/server": []string{".keep.tmpl"}, "/cmd": []string{}, "/cmd/server": []string{"run.go.tmpl"}, "/": []string{".gitignore.tmpl", "Gopkg.toml.tmpl", "grapi.toml.tmpl"}, "/api": []string{}, "/api/protos": []string{".keep.tmpl"}, "/app": []string{"run.go.tmpl"}}, map[string]*assets.File{ - "/app/server/.keep.tmpl": &assets.File{ - Path: "/app/server/.keep.tmpl", - FileMode: 0x1a4, - Mtime: time.Unix(1521042119, 1521042119000000000), - Data: []byte(_Init71ed560e812a4261bc8b56d9feaef4800830e0b7), - }, "/cmd/server": &assets.File{ - Path: "/cmd/server", - FileMode: 0x800001ed, - Mtime: time.Unix(1530424640, 1530424640000000000), - Data: nil, - }, "/cmd/server/run.go.tmpl": &assets.File{ - Path: "/cmd/server/run.go.tmpl", - FileMode: 0x1a4, - Mtime: time.Unix(1530424640, 1530424640000000000), - Data: []byte(_Initd135936e91856b6159ac2eedcf89aa9f07773f82), - }, "/Gopkg.toml.tmpl": &assets.File{ - Path: "/Gopkg.toml.tmpl", - FileMode: 0x1a4, - Mtime: time.Unix(1522493033, 1522493033000000000), - Data: []byte(_Init23b808cac963edf44a497827f2a6eff5ddac970f), - }, "/api/protos": &assets.File{ - Path: "/api/protos", - FileMode: 0x800001ed, - Mtime: time.Unix(1520753819, 1520753819000000000), - Data: nil, - }, "/api/protos/.keep.tmpl": &assets.File{ - Path: "/api/protos/.keep.tmpl", - FileMode: 0x1a4, - Mtime: time.Unix(1520753819, 1520753819000000000), - Data: []byte(_Init8d21956ba8abe388f964e47be0f7e5d170a2fce5), - }, "/app": &assets.File{ - Path: "/app", - FileMode: 0x800001ed, - Mtime: time.Unix(1521995193, 1521995193000000000), - Data: nil, - }, "/cmd": &assets.File{ - Path: "/cmd", - FileMode: 0x800001ed, - Mtime: time.Unix(1520753819, 1520753819000000000), - Data: nil, - }, "/grapi.toml.tmpl": &assets.File{ - Path: "/grapi.toml.tmpl", - FileMode: 0x1a4, - Mtime: time.Unix(1531820124, 1531820124000000000), - Data: []byte(_Initc051c9ff1a8e446bc9636d3144c2775a7e235322), - }, "/": &assets.File{ - Path: "/", - FileMode: 0x800001ed, - Mtime: time.Unix(1531820124, 1531820124000000000), - Data: nil, - }, "/api": &assets.File{ - Path: "/api", - FileMode: 0x800001ed, - Mtime: time.Unix(1520753819, 1520753819000000000), - Data: nil, - }, "/app/run.go.tmpl": &assets.File{ - Path: "/app/run.go.tmpl", - FileMode: 0x1a4, - Mtime: time.Unix(1521995193, 1521995193000000000), - Data: []byte(_Initbc4053f4dd26ceb67e4646e8c1d2cc75897c4dd0), - }, "/app/server": &assets.File{ - Path: "/app/server", - FileMode: 0x800001ed, - Mtime: time.Unix(1521042119, 1521042119000000000), - Data: nil, - }, "/.gitignore.tmpl": &assets.File{ - Path: "/.gitignore.tmpl", - FileMode: 0x1a4, - Mtime: time.Unix(1520753819, 1520753819000000000), - Data: []byte(_Init38e76c5db8962fa825cf2bd8b23a2dc985c4513e), - }}, "") diff --git a/pkg/grapicmd/internal/module/generator/template/init/Gopkg.toml.tmpl b/pkg/grapicmd/internal/module/generator/template/init/Gopkg.toml.tmpl deleted file mode 100644 index 3551c7f6..00000000 --- a/pkg/grapicmd/internal/module/generator/template/init/Gopkg.toml.tmpl +++ /dev/null @@ -1,14 +0,0 @@ -required = [ - "github.com/golang/protobuf/protoc-gen-go", - "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway", - "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger", -] - -[[constraint]] -{{- if .headUsed }} - branch = "master" -{{- end }} - name = "github.com/izumin5210/grapi" -{{- if not .headUsed }} - version = "{{ .version }}" -{{- end }} diff --git a/pkg/grapicmd/internal/module/generator/template/init/grapi.toml.tmpl b/pkg/grapicmd/internal/module/generator/template/init/grapi.toml.tmpl deleted file mode 100644 index 3a0a8101..00000000 --- a/pkg/grapicmd/internal/module/generator/template/init/grapi.toml.tmpl +++ /dev/null @@ -1,25 +0,0 @@ -[grapi] -server_dir = "./app/server" - -[protoc] -protos_dir = "./api/protos" -out_dir = "./api" -import_dirs = [ - "./vendor/github.com/grpc-ecosystem/grpc-gateway", - "./vendor/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis", -] - - [[protoc.plugins]] - path = "./vendor/github.com/golang/protobuf/protoc-gen-go" - name = "go" - args = { plugins = "grpc", paths = "source_relative" } - - [[protoc.plugins]] - path = "./vendor/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway" - name = "grpc-gateway" - args = { logtostderr = true } - - [[protoc.plugins]] - path = "./vendor/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger" - name = "swagger" - args = { logtostderr = true } diff --git a/pkg/grapicmd/internal/module/generator/template/service.go b/pkg/grapicmd/internal/module/generator/template/service.go deleted file mode 100644 index 06070cac..00000000 --- a/pkg/grapicmd/internal/module/generator/template/service.go +++ /dev/null @@ -1,51 +0,0 @@ -package template - -import ( - "time" - - "github.com/jessevdk/go-assets" -) - -var _Servicec080f048193e3b40b184b8e68c773e1b1bf56088 = "syntax = \"proto3\";\noption go_package = \"{{ .PbGo.PackageName }}\";\npackage {{ .Proto.Package }};\n{{range .Proto.Imports}}\nimport \"{{.}}\";\n{{- end}}\n\nservice {{ .ServiceName }}Service {\t\n{{- range .Methods}}\n rpc {{.Method}} ({{.RequestProto}}) returns ({{.ResponseProto}}) {\n option (google.api.http) = {\n {{.HTTP.Method}}: \"/{{.HTTP.Path}}\"\n {{- if .HTTP.Body}}\n body: \"{{.HTTP.Body}}\"\n {{- end}}\n };\n }\n{{- end}}\n}\n{{range .Proto.Messages}}\nmessage {{.Name}} {\n {{- range .Fields}}\n {{- if .Repeated}}\n repeated {{.Type}} {{.Name}} = {{.Tag}};\n {{- else}}\n {{.Type}} {{.Name}} = {{.Tag}};\n {{- end}}\n {{- end}}\n}\n{{end -}}\n" -var _Servicedd0ea9770a89c1038381555c2b8b1dfc7d52b6d8 = "package {{.Go.Package }}\n\nimport (\n\t\"context\"\n{{range .Go.Imports}}\n\t\"{{.}}\"\n{{- end}}\n\n\t{{.PbGo.PackageName}} \"{{ .PbGo.PackagePath }}\"\n)\n\n// New{{.Go.ServerName}} creates a new {{.Go.ServerName}} instance.\nfunc New{{.Go.ServerName}}() interface {\n\t{{.PbGo.PackageName }}.{{.Go.ServerName}}\n\tgrapiserver.Server\n} {\n\treturn &{{.Go.StructName}}{}\n}\n\ntype {{.Go.StructName}} struct {\n}\n{{$go := .Go -}}\n{{$pbGo := .PbGo -}}\n{{- range .Methods}}\nfunc (s *{{$go.StructName}}) {{.Method}}(ctx context.Context, req *{{.RequestGo $pbGo.PackageName}}) (*{{.ResponseGo $pbGo.PackageName}}, error) {\n\t// TODO: Not yet implemented.\n\treturn nil, status.Error(codes.Unimplemented, \"TODO: You should implement it!\")\n}\n{{end -}}\n" -var _Servicea66d22b3414614e986072628e857757760b5fab0 = "package {{.Go.Package }}\n\nimport (\n\t\"context\"\n\n\t\"github.com/grpc-ecosystem/grpc-gateway/runtime\"\n\t\"google.golang.org/grpc\"\n\n\t{{.PbGo.PackageName}} \"{{ .PbGo.PackagePath }}\"\n)\n\n// RegisterWithServer implements grapiserver.Server.RegisterWithServer.\nfunc (s *{{.Go.StructName}}) RegisterWithServer(grpcSvr *grpc.Server) {\n\t{{.PbGo.PackageName}}.Register{{.Go.ServerName}}(grpcSvr, s)\n}\n\n// RegisterWithHandler implements grapiserver.Server.RegisterWithHandler.\nfunc (s *{{.Go.StructName}}) RegisterWithHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {\n\treturn {{.PbGo.PackageName}}.Register{{.ServiceName}}ServiceHandler(ctx, mux, conn)\n}\n" -var _Service92aa9b7adaba175be79051b307a723ea2c536222 = "package {{.Go.Package }}\n{{if .Methods}}\nimport (\n\t\"context\"\n\t\"testing\"\n{{range .Go.TestImports}}\n\t\"{{.}}\"\n{{- end}}\n\n\t{{.PbGo.PackageName}} \"{{ .PbGo.PackagePath }}\"\n)\n{{$go := .Go -}}\n{{$pbGo := .PbGo -}}\n{{- range .Methods}}\nfunc Test_{{$go.ServerName}}_{{.Method}}(t *testing.T) {\n\tsvr := New{{$go.ServerName}}()\n\n\tctx := context.Background()\n\treq := &{{.RequestGo $pbGo.PackageName}}{}\n\n\tresp, err := svr.{{.Method}}(ctx, req)\n\n\tif err != nil {\n\t\tt.Errorf(\"returned an error %v\", err)\n\t}\n\n\tif resp == nil {\n\t\tt.Error(\"response should not nil\")\n\t}\n}\n{{end -}}\n{{end -}}\n" - -// Service returns go-assets FileSystem -var Service = assets.NewFileSystem(map[string][]string{"/{{.ProtoDir}}": []string{"{{.Path}}.proto.tmpl"}, "/{{.ServerDir}}": []string{"{{.Path}}_server.go.tmpl", "{{.Path}}_server_register_funcs.go.tmpl", "{{.Path}}_server_test.go.tmpl"}, "/": []string{}}, map[string]*assets.File{ - "/{{.ServerDir}}": &assets.File{ - Path: "/{{.ServerDir}}", - FileMode: 0x800001ed, - Mtime: time.Unix(1530427283, 1530427283000000000), - Data: nil, - }, "/{{.ServerDir}}/{{.Path}}_server.go.tmpl": &assets.File{ - Path: "/{{.ServerDir}}/{{.Path}}_server.go.tmpl", - FileMode: 0x1a4, - Mtime: time.Unix(1523623016, 1523623016000000000), - Data: []byte(_Servicedd0ea9770a89c1038381555c2b8b1dfc7d52b6d8), - }, "/{{.ServerDir}}/{{.Path}}_server_register_funcs.go.tmpl": &assets.File{ - Path: "/{{.ServerDir}}/{{.Path}}_server_register_funcs.go.tmpl", - FileMode: 0x1a4, - Mtime: time.Unix(1523623016, 1523623016000000000), - Data: []byte(_Servicea66d22b3414614e986072628e857757760b5fab0), - }, "/{{.ServerDir}}/{{.Path}}_server_test.go.tmpl": &assets.File{ - Path: "/{{.ServerDir}}/{{.Path}}_server_test.go.tmpl", - FileMode: 0x1a4, - Mtime: time.Unix(1521996238, 1521996238000000000), - Data: []byte(_Service92aa9b7adaba175be79051b307a723ea2c536222), - }, "/": &assets.File{ - Path: "/", - FileMode: 0x800001ed, - Mtime: time.Unix(1530427296, 1530427296000000000), - Data: nil, - }, "/{{.ProtoDir}}": &assets.File{ - Path: "/{{.ProtoDir}}", - FileMode: 0x800001ed, - Mtime: time.Unix(1530426353, 1530426353000000000), - Data: nil, - }, "/{{.ProtoDir}}/{{.Path}}.proto.tmpl": &assets.File{ - Path: "/{{.ProtoDir}}/{{.Path}}.proto.tmpl", - FileMode: 0x1a4, - Mtime: time.Unix(1521913317, 1521913317000000000), - Data: []byte(_Servicec080f048193e3b40b184b8e68c773e1b1bf56088), - }}, "") diff --git a/pkg/grapicmd/internal/module/script.go b/pkg/grapicmd/internal/module/script.go index 5e1c10a6..77d6c3f3 100644 --- a/pkg/grapicmd/internal/module/script.go +++ b/pkg/grapicmd/internal/module/script.go @@ -1,10 +1,12 @@ package module +import "context" + // Script represents an user-defined command. type Script interface { Name() string - Build(args ...string) error - Run(args ...string) error + Build(ctx context.Context, args ...string) error + Run(ctx context.Context, args ...string) error } // ScriptLoader is a factory object for creating Script objects. diff --git a/pkg/grapicmd/internal/module/script/loader.go b/pkg/grapicmd/internal/module/script/loader.go index ff2a531d..4c7e15f5 100644 --- a/pkg/grapicmd/internal/module/script/loader.go +++ b/pkg/grapicmd/internal/module/script/loader.go @@ -7,33 +7,39 @@ import ( "github.com/pkg/errors" "github.com/spf13/afero" + "go.uber.org/zap" + "github.com/izumin5210/clig/pkg/clib" + "github.com/izumin5210/execx" "github.com/izumin5210/grapi/pkg/grapicmd/internal/module" "github.com/izumin5210/grapi/pkg/grapicmd/util/fs" ) // NewLoader creates a new ScriptLoader instance. -func NewLoader(fs afero.Fs, commandFactory module.CommandFactory, rootDir string) module.ScriptLoader { +func NewLoader(fs afero.Fs, io *clib.IO, exec *execx.Executor, rootDir string) module.ScriptLoader { return &scriptLoader{ - fs: fs, - commandFactory: commandFactory, - rootDir: rootDir, - binDir: filepath.Join(rootDir, "bin"), - scripts: make(map[string]module.Script), + fs: fs, + io: io, + exec: exec, + rootDir: rootDir, + binDir: filepath.Join(rootDir, "bin"), + scripts: make(map[string]module.Script), } } type scriptLoader struct { - fs afero.Fs - commandFactory module.CommandFactory - rootDir string - binDir string - scripts map[string]module.Script - names []string + fs afero.Fs + io *clib.IO + exec *execx.Executor + rootDir string + binDir string + scripts map[string]module.Script + names []string } func (f *scriptLoader) Load(dir string) error { srcsByDir, err := fs.FindMainPackagesAndSources(f.fs, dir) + zap.L().Debug("found main packages", zap.Any("srcs_by_dir", srcsByDir)) if err != nil { return errors.Wrap(err, "failed to find commands") } @@ -48,12 +54,13 @@ func (f *scriptLoader) Load(dir string) error { ext = ".exe" } f.scripts[name] = &script{ - fs: f.fs, - commandFactory: f.commandFactory, - srcPaths: srcPaths, - name: name, - binPath: filepath.Join(f.binDir, name+ext), - rootDir: f.rootDir, + fs: f.fs, + io: f.io, + exec: f.exec, + srcPaths: srcPaths, + name: name, + binPath: filepath.Join(f.binDir, name+ext), + rootDir: f.rootDir, } f.names = append(f.names, name) } diff --git a/pkg/grapicmd/internal/module/script/script.go b/pkg/grapicmd/internal/module/script/script.go index 89b78d8f..ce8302b6 100644 --- a/pkg/grapicmd/internal/module/script/script.go +++ b/pkg/grapicmd/internal/module/script/script.go @@ -1,35 +1,45 @@ package script import ( + "context" + "fmt" "path/filepath" "github.com/pkg/errors" "github.com/spf13/afero" + "go.uber.org/zap" - "github.com/izumin5210/grapi/pkg/grapicmd/internal/module" + "github.com/izumin5210/clig/pkg/clib" + "github.com/izumin5210/execx" "github.com/izumin5210/grapi/pkg/grapicmd/util/fs" ) type script struct { - fs afero.Fs - commandFactory module.CommandFactory - rootDir string - name, binPath string - srcPaths []string + fs afero.Fs + io *clib.IO + exec *execx.Executor + rootDir string + name, binPath string + srcPaths []string } func (s *script) Name() string { return s.name } -func (s *script) Build(args ...string) error { +func (s *script) Build(ctx context.Context, args ...string) error { + zap.L().Debug("build script", zap.String("name", s.name), zap.String("bin", s.binPath), zap.Strings("srcs", s.srcPaths)) err := fs.CreateDirIfNotExists(s.fs, filepath.Dir(s.binPath)) if err != nil { return errors.WithStack(err) } - cmd := s.commandFactory.Create(s.buildCmd(args)) - _, err = cmd.ConnectIO().SetDir(s.rootDir).Exec() + cmd := s.exec.CommandContext(ctx, "go", s.buildArgs(args)...) + cmd.Dir = s.rootDir + cmd.Stdout = s.io.Out + cmd.Stderr = s.io.Err + cmd.Stdin = s.io.In + err = cmd.Run() if err != nil { return errors.Wrapf(err, "failed to build %v", s.srcPaths) } @@ -37,16 +47,24 @@ func (s *script) Build(args ...string) error { return nil } -func (s *script) Run(args ...string) error { - cmd := s.commandFactory.Create(append([]string{s.binPath}, args...)) - _, err := cmd.ConnectIO().SetDir(s.rootDir).Exec() +func (s *script) Run(ctx context.Context, args ...string) error { + cmd := s.exec.CommandContext(ctx, s.binPath, args...) + cmd.Dir = s.rootDir + cmd.Stdout = s.io.Out + cmd.Stderr = s.io.Err + cmd.Stdin = s.io.In + err := cmd.Run() + fmt.Println(err) + if err == context.Canceled { + return nil + } return errors.WithStack(err) } -func (s *script) buildCmd(args []string) []string { - cmd := make([]string, 0, 3+len(args)+len(s.srcPaths)) - cmd = append(cmd, "go", "build", "-o="+s.binPath) - cmd = append(cmd, args...) - cmd = append(cmd, s.srcPaths...) - return cmd +func (s *script) buildArgs(args []string) []string { + built := make([]string, 0, 3+len(args)+len(s.srcPaths)) + built = append(built, "build", "-o="+s.binPath) + built = append(built, args...) + built = append(built, s.srcPaths...) + return built } diff --git a/pkg/grapicmd/internal/module/script/script_test.go b/pkg/grapicmd/internal/module/script/script_test.go index 9422e38f..74242991 100644 --- a/pkg/grapicmd/internal/module/script/script_test.go +++ b/pkg/grapicmd/internal/module/script/script_test.go @@ -1,28 +1,27 @@ package script import ( + "context" + "os/exec" "path/filepath" "reflect" "testing" - "github.com/izumin5210/grapi/pkg/grapicmd/internal/module" "github.com/spf13/afero" -) -type execution struct { - nameAndArgs []string - dir string - connected bool -} + "github.com/izumin5210/clig/pkg/clib" + "github.com/izumin5210/execx" + "github.com/izumin5210/grapi/pkg/grapicmd/internal/module" +) type testContext struct { fs afero.Fs - executions []*execution loader module.ScriptLoader binName string rootDir string cmdDir string srcsByBinName map[string][]string + cmds []*exec.Cmd } func createTestContext(t *testing.T) *testContext { @@ -34,9 +33,11 @@ func createTestContext(t *testing.T) *testContext { binName: binName, rootDir: rootDir, cmdDir: filepath.Join(rootDir, "cmd"), - executions: []*execution{}, srcsByBinName: map[string][]string{}, } + exec := execx.New(execx.WithFakeProcess( + func(_ context.Context, c *exec.Cmd) error { ctx.cmds = append(ctx.cmds, c); return nil }, + )) srcsByBinName := map[string][]string{ binName: {"bar.go", "foo.go", "main.go"}, @@ -60,22 +61,7 @@ func createTestContext(t *testing.T) *testContext { ctx.srcsByBinName[binName] = srcPaths } - commandFactory := &fakeCommandFactory{ - fakeCreate: func(nameAndArgs []string) module.Command { - c := &fakeCommand{} - c.fakeExec = func() ([]byte, error) { - ctx.executions = append(ctx.executions, &execution{ - nameAndArgs: nameAndArgs, - dir: c.dir, - connected: c.ioConnected, - }) - return []byte{}, nil - } - return c - }, - } - - ctx.loader = NewLoader(fs, commandFactory, rootDir) + ctx.loader = NewLoader(fs, &clib.IO{}, exec, rootDir) return ctx } @@ -101,16 +87,16 @@ func Test_Script(t *testing.T) { t.Errorf("script.Build() returned an error %v", err) } - err = s.Build("-v") + err = s.Build(context.Background(), "-v") binPath := filepath.Join(ctx.rootDir, "bin", ctx.binName) - exec := ctx.executions[0] + cmd := ctx.cmds[0] srcs := ctx.srcsByBinName[ctx.binName] - if got, want := exec.nameAndArgs, append([]string{"go", "build", "-o=" + binPath, "-v"}, srcs...); !reflect.DeepEqual(got, want) { + if got, want := cmd.Args, append([]string{"go", "build", "-o=" + binPath, "-v"}, srcs...); !reflect.DeepEqual(got, want) { t.Errorf("Build() executed %v, want %v", got, want) } - if got, want := exec.dir, "/home/app"; got != want { + if got, want := cmd.Dir, "/home/app"; got != want { t.Errorf("Build() executed a command in %v, want %v", got, want) } @@ -118,14 +104,14 @@ func Test_Script(t *testing.T) { t.Errorf("Build() returned an error %v", err) } - err = s.Run("-v") - exec = ctx.executions[1] + err = s.Run(context.Background(), "-v") + cmd = ctx.cmds[1] - if got, want := exec.nameAndArgs, []string{binPath, "-v"}; !reflect.DeepEqual(got, want) { + if got, want := cmd.Path, binPath; got != want { t.Errorf("Run() executed %v, want %v", got, want) } - if got, want := exec.dir, "/home/app"; got != want { + if got, want := cmd.Dir, "/home/app"; got != want { t.Errorf("Run() executed a command in %v, want %v", got, want) } @@ -133,35 +119,3 @@ func Test_Script(t *testing.T) { t.Errorf("Run() returned an error %v", err) } } - -// fake impls - -type fakeCommandFactory struct { - module.CommandFactory - fakeCreate func([]string) module.Command -} - -func (f *fakeCommandFactory) Create(nameAndArgs []string) module.Command { - return f.fakeCreate(nameAndArgs) -} - -type fakeCommand struct { - module.Command - dir string - ioConnected bool - fakeExec func() ([]byte, error) -} - -func (c *fakeCommand) SetDir(dir string) module.Command { - c.dir = dir - return c -} - -func (c *fakeCommand) ConnectIO() module.Command { - c.ioConnected = true - return c -} - -func (c *fakeCommand) Exec() ([]byte, error) { - return c.fakeExec() -} diff --git a/pkg/grapicmd/internal/module/testing/generator_mock.go b/pkg/grapicmd/internal/module/testing/generator_mock.go index 60c5678e..2a491442 100644 --- a/pkg/grapicmd/internal/module/testing/generator_mock.go +++ b/pkg/grapicmd/internal/module/testing/generator_mock.go @@ -1,11 +1,11 @@ // Code generated by MockGen. DO NOT EDIT. // Source: generator.go +// Package moduletesting is a generated GoMock package. package moduletesting import ( gomock "github.com/golang/mock/gomock" - . "github.com/izumin5210/grapi/pkg/grapicmd/internal/module" reflect "reflect" ) @@ -28,80 +28,22 @@ func NewMockGenerator(ctrl *gomock.Controller) *MockGenerator { } // EXPECT returns an object that allows the caller to indicate expected use -func (_m *MockGenerator) EXPECT() *MockGeneratorMockRecorder { - return _m.recorder +func (m *MockGenerator) EXPECT() *MockGeneratorMockRecorder { + return m.recorder } // GenerateProject mocks base method -func (_m *MockGenerator) GenerateProject(rootDir string, useHead bool) error { - ret := _m.ctrl.Call(_m, "GenerateProject", rootDir, useHead) +func (m *MockGenerator) GenerateProject(rootDir, pkgName string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GenerateProject", rootDir, pkgName) ret0, _ := ret[0].(error) return ret0 } // GenerateProject indicates an expected call of GenerateProject -func (_mr *MockGeneratorMockRecorder) GenerateProject(arg0, arg1 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "GenerateProject", reflect.TypeOf((*MockGenerator)(nil).GenerateProject), arg0, arg1) -} - -// GenerateService mocks base method -func (_m *MockGenerator) GenerateService(name string, cfg ServiceGenerationConfig) error { - ret := _m.ctrl.Call(_m, "GenerateService", name, cfg) - ret0, _ := ret[0].(error) - return ret0 -} - -// GenerateService indicates an expected call of GenerateService -func (_mr *MockGeneratorMockRecorder) GenerateService(arg0, arg1 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "GenerateService", reflect.TypeOf((*MockGenerator)(nil).GenerateService), arg0, arg1) -} - -// ScaffoldService mocks base method -func (_m *MockGenerator) ScaffoldService(name string, cfg ServiceGenerationConfig) error { - ret := _m.ctrl.Call(_m, "ScaffoldService", name, cfg) - ret0, _ := ret[0].(error) - return ret0 -} - -// ScaffoldService indicates an expected call of ScaffoldService -func (_mr *MockGeneratorMockRecorder) ScaffoldService(arg0, arg1 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "ScaffoldService", reflect.TypeOf((*MockGenerator)(nil).ScaffoldService), arg0, arg1) -} - -// DestroyService mocks base method -func (_m *MockGenerator) DestroyService(name string) error { - ret := _m.ctrl.Call(_m, "DestroyService", name) - ret0, _ := ret[0].(error) - return ret0 -} - -// DestroyService indicates an expected call of DestroyService -func (_mr *MockGeneratorMockRecorder) DestroyService(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "DestroyService", reflect.TypeOf((*MockGenerator)(nil).DestroyService), arg0) -} - -// GenerateCommand mocks base method -func (_m *MockGenerator) GenerateCommand(name string) error { - ret := _m.ctrl.Call(_m, "GenerateCommand", name) - ret0, _ := ret[0].(error) - return ret0 -} - -// GenerateCommand indicates an expected call of GenerateCommand -func (_mr *MockGeneratorMockRecorder) GenerateCommand(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "GenerateCommand", reflect.TypeOf((*MockGenerator)(nil).GenerateCommand), arg0) -} - -// DestroyCommand mocks base method -func (_m *MockGenerator) DestroyCommand(name string) error { - ret := _m.ctrl.Call(_m, "DestroyCommand", name) - ret0, _ := ret[0].(error) - return ret0 -} - -// DestroyCommand indicates an expected call of DestroyCommand -func (_mr *MockGeneratorMockRecorder) DestroyCommand(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "DestroyCommand", reflect.TypeOf((*MockGenerator)(nil).DestroyCommand), arg0) +func (mr *MockGeneratorMockRecorder) GenerateProject(rootDir, pkgName interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GenerateProject", reflect.TypeOf((*MockGenerator)(nil).GenerateProject), rootDir, pkgName) } // MockProjectGenerator is a mock of ProjectGenerator interface @@ -123,124 +65,20 @@ func NewMockProjectGenerator(ctrl *gomock.Controller) *MockProjectGenerator { } // EXPECT returns an object that allows the caller to indicate expected use -func (_m *MockProjectGenerator) EXPECT() *MockProjectGeneratorMockRecorder { - return _m.recorder +func (m *MockProjectGenerator) EXPECT() *MockProjectGeneratorMockRecorder { + return m.recorder } // GenerateProject mocks base method -func (_m *MockProjectGenerator) GenerateProject(rootDir string, useHead bool) error { - ret := _m.ctrl.Call(_m, "GenerateProject", rootDir, useHead) +func (m *MockProjectGenerator) GenerateProject(rootDir, pkgName string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GenerateProject", rootDir, pkgName) ret0, _ := ret[0].(error) return ret0 } // GenerateProject indicates an expected call of GenerateProject -func (_mr *MockProjectGeneratorMockRecorder) GenerateProject(arg0, arg1 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "GenerateProject", reflect.TypeOf((*MockProjectGenerator)(nil).GenerateProject), arg0, arg1) -} - -// MockServiceGenerator is a mock of ServiceGenerator interface -type MockServiceGenerator struct { - ctrl *gomock.Controller - recorder *MockServiceGeneratorMockRecorder -} - -// MockServiceGeneratorMockRecorder is the mock recorder for MockServiceGenerator -type MockServiceGeneratorMockRecorder struct { - mock *MockServiceGenerator -} - -// NewMockServiceGenerator creates a new mock instance -func NewMockServiceGenerator(ctrl *gomock.Controller) *MockServiceGenerator { - mock := &MockServiceGenerator{ctrl: ctrl} - mock.recorder = &MockServiceGeneratorMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use -func (_m *MockServiceGenerator) EXPECT() *MockServiceGeneratorMockRecorder { - return _m.recorder -} - -// GenerateService mocks base method -func (_m *MockServiceGenerator) GenerateService(name string, cfg ServiceGenerationConfig) error { - ret := _m.ctrl.Call(_m, "GenerateService", name, cfg) - ret0, _ := ret[0].(error) - return ret0 -} - -// GenerateService indicates an expected call of GenerateService -func (_mr *MockServiceGeneratorMockRecorder) GenerateService(arg0, arg1 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "GenerateService", reflect.TypeOf((*MockServiceGenerator)(nil).GenerateService), arg0, arg1) -} - -// ScaffoldService mocks base method -func (_m *MockServiceGenerator) ScaffoldService(name string, cfg ServiceGenerationConfig) error { - ret := _m.ctrl.Call(_m, "ScaffoldService", name, cfg) - ret0, _ := ret[0].(error) - return ret0 -} - -// ScaffoldService indicates an expected call of ScaffoldService -func (_mr *MockServiceGeneratorMockRecorder) ScaffoldService(arg0, arg1 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "ScaffoldService", reflect.TypeOf((*MockServiceGenerator)(nil).ScaffoldService), arg0, arg1) -} - -// DestroyService mocks base method -func (_m *MockServiceGenerator) DestroyService(name string) error { - ret := _m.ctrl.Call(_m, "DestroyService", name) - ret0, _ := ret[0].(error) - return ret0 -} - -// DestroyService indicates an expected call of DestroyService -func (_mr *MockServiceGeneratorMockRecorder) DestroyService(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "DestroyService", reflect.TypeOf((*MockServiceGenerator)(nil).DestroyService), arg0) -} - -// MockCommandGenerator is a mock of CommandGenerator interface -type MockCommandGenerator struct { - ctrl *gomock.Controller - recorder *MockCommandGeneratorMockRecorder -} - -// MockCommandGeneratorMockRecorder is the mock recorder for MockCommandGenerator -type MockCommandGeneratorMockRecorder struct { - mock *MockCommandGenerator -} - -// NewMockCommandGenerator creates a new mock instance -func NewMockCommandGenerator(ctrl *gomock.Controller) *MockCommandGenerator { - mock := &MockCommandGenerator{ctrl: ctrl} - mock.recorder = &MockCommandGeneratorMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use -func (_m *MockCommandGenerator) EXPECT() *MockCommandGeneratorMockRecorder { - return _m.recorder -} - -// GenerateCommand mocks base method -func (_m *MockCommandGenerator) GenerateCommand(name string) error { - ret := _m.ctrl.Call(_m, "GenerateCommand", name) - ret0, _ := ret[0].(error) - return ret0 -} - -// GenerateCommand indicates an expected call of GenerateCommand -func (_mr *MockCommandGeneratorMockRecorder) GenerateCommand(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "GenerateCommand", reflect.TypeOf((*MockCommandGenerator)(nil).GenerateCommand), arg0) -} - -// DestroyCommand mocks base method -func (_m *MockCommandGenerator) DestroyCommand(name string) error { - ret := _m.ctrl.Call(_m, "DestroyCommand", name) - ret0, _ := ret[0].(error) - return ret0 -} - -// DestroyCommand indicates an expected call of DestroyCommand -func (_mr *MockCommandGeneratorMockRecorder) DestroyCommand(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "DestroyCommand", reflect.TypeOf((*MockCommandGenerator)(nil).DestroyCommand), arg0) +func (mr *MockProjectGeneratorMockRecorder) GenerateProject(rootDir, pkgName interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GenerateProject", reflect.TypeOf((*MockProjectGenerator)(nil).GenerateProject), rootDir, pkgName) } diff --git a/pkg/grapicmd/internal/module/testing/script_mock.go b/pkg/grapicmd/internal/module/testing/script_mock.go index 8220f9c8..c70cde2b 100644 --- a/pkg/grapicmd/internal/module/testing/script_mock.go +++ b/pkg/grapicmd/internal/module/testing/script_mock.go @@ -1,11 +1,13 @@ // Code generated by MockGen. DO NOT EDIT. // Source: script.go +// Package moduletesting is a generated GoMock package. package moduletesting import ( + context "context" gomock "github.com/golang/mock/gomock" - . "github.com/izumin5210/grapi/pkg/grapicmd/internal/module" + module "github.com/izumin5210/grapi/pkg/grapicmd/internal/module" reflect "reflect" ) @@ -28,52 +30,60 @@ func NewMockScript(ctrl *gomock.Controller) *MockScript { } // EXPECT returns an object that allows the caller to indicate expected use -func (_m *MockScript) EXPECT() *MockScriptMockRecorder { - return _m.recorder +func (m *MockScript) EXPECT() *MockScriptMockRecorder { + return m.recorder } // Name mocks base method -func (_m *MockScript) Name() string { - ret := _m.ctrl.Call(_m, "Name") +func (m *MockScript) Name() string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Name") ret0, _ := ret[0].(string) return ret0 } // Name indicates an expected call of Name -func (_mr *MockScriptMockRecorder) Name() *gomock.Call { - return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "Name", reflect.TypeOf((*MockScript)(nil).Name)) +func (mr *MockScriptMockRecorder) Name() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Name", reflect.TypeOf((*MockScript)(nil).Name)) } // Build mocks base method -func (_m *MockScript) Build(args ...string) error { - _s := []interface{}{} - for _, _x := range args { - _s = append(_s, _x) +func (m *MockScript) Build(ctx context.Context, args ...string) error { + m.ctrl.T.Helper() + varargs := []interface{}{ctx} + for _, a := range args { + varargs = append(varargs, a) } - ret := _m.ctrl.Call(_m, "Build", _s...) + ret := m.ctrl.Call(m, "Build", varargs...) ret0, _ := ret[0].(error) return ret0 } // Build indicates an expected call of Build -func (_mr *MockScriptMockRecorder) Build(arg0 ...interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "Build", reflect.TypeOf((*MockScript)(nil).Build), arg0...) +func (mr *MockScriptMockRecorder) Build(ctx interface{}, args ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx}, args...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Build", reflect.TypeOf((*MockScript)(nil).Build), varargs...) } // Run mocks base method -func (_m *MockScript) Run(args ...string) error { - _s := []interface{}{} - for _, _x := range args { - _s = append(_s, _x) +func (m *MockScript) Run(ctx context.Context, args ...string) error { + m.ctrl.T.Helper() + varargs := []interface{}{ctx} + for _, a := range args { + varargs = append(varargs, a) } - ret := _m.ctrl.Call(_m, "Run", _s...) + ret := m.ctrl.Call(m, "Run", varargs...) ret0, _ := ret[0].(error) return ret0 } // Run indicates an expected call of Run -func (_mr *MockScriptMockRecorder) Run(arg0 ...interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "Run", reflect.TypeOf((*MockScript)(nil).Run), arg0...) +func (mr *MockScriptMockRecorder) Run(ctx interface{}, args ...interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + varargs := append([]interface{}{ctx}, args...) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Run", reflect.TypeOf((*MockScript)(nil).Run), varargs...) } // MockScriptLoader is a mock of ScriptLoader interface @@ -95,43 +105,49 @@ func NewMockScriptLoader(ctrl *gomock.Controller) *MockScriptLoader { } // EXPECT returns an object that allows the caller to indicate expected use -func (_m *MockScriptLoader) EXPECT() *MockScriptLoaderMockRecorder { - return _m.recorder +func (m *MockScriptLoader) EXPECT() *MockScriptLoaderMockRecorder { + return m.recorder } // Load mocks base method -func (_m *MockScriptLoader) Load(dir string) error { - ret := _m.ctrl.Call(_m, "Load", dir) +func (m *MockScriptLoader) Load(dir string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Load", dir) ret0, _ := ret[0].(error) return ret0 } // Load indicates an expected call of Load -func (_mr *MockScriptLoaderMockRecorder) Load(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "Load", reflect.TypeOf((*MockScriptLoader)(nil).Load), arg0) +func (mr *MockScriptLoaderMockRecorder) Load(dir interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Load", reflect.TypeOf((*MockScriptLoader)(nil).Load), dir) } // Get mocks base method -func (_m *MockScriptLoader) Get(name string) (Script, bool) { - ret := _m.ctrl.Call(_m, "Get", name) - ret0, _ := ret[0].(Script) +func (m *MockScriptLoader) Get(name string) (module.Script, bool) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Get", name) + ret0, _ := ret[0].(module.Script) ret1, _ := ret[1].(bool) return ret0, ret1 } // Get indicates an expected call of Get -func (_mr *MockScriptLoaderMockRecorder) Get(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "Get", reflect.TypeOf((*MockScriptLoader)(nil).Get), arg0) +func (mr *MockScriptLoaderMockRecorder) Get(name interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockScriptLoader)(nil).Get), name) } // Names mocks base method -func (_m *MockScriptLoader) Names() []string { - ret := _m.ctrl.Call(_m, "Names") +func (m *MockScriptLoader) Names() []string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Names") ret0, _ := ret[0].([]string) return ret0 } // Names indicates an expected call of Names -func (_mr *MockScriptLoaderMockRecorder) Names() *gomock.Call { - return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "Names", reflect.TypeOf((*MockScriptLoader)(nil).Names)) +func (mr *MockScriptLoaderMockRecorder) Names() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Names", reflect.TypeOf((*MockScriptLoader)(nil).Names)) } diff --git a/pkg/grapicmd/internal/module/testing/ui_mock.go b/pkg/grapicmd/internal/module/testing/ui_mock.go deleted file mode 100644 index 240b037f..00000000 --- a/pkg/grapicmd/internal/module/testing/ui_mock.go +++ /dev/null @@ -1,125 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: ui.go - -package moduletesting - -import ( - gomock "github.com/golang/mock/gomock" - reflect "reflect" -) - -// MockUI is a mock of UI interface -type MockUI struct { - ctrl *gomock.Controller - recorder *MockUIMockRecorder -} - -// MockUIMockRecorder is the mock recorder for MockUI -type MockUIMockRecorder struct { - mock *MockUI -} - -// NewMockUI creates a new mock instance -func NewMockUI(ctrl *gomock.Controller) *MockUI { - mock := &MockUI{ctrl: ctrl} - mock.recorder = &MockUIMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use -func (_m *MockUI) EXPECT() *MockUIMockRecorder { - return _m.recorder -} - -// Output mocks base method -func (_m *MockUI) Output(msg string) { - _m.ctrl.Call(_m, "Output", msg) -} - -// Output indicates an expected call of Output -func (_mr *MockUIMockRecorder) Output(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "Output", reflect.TypeOf((*MockUI)(nil).Output), arg0) -} - -// Section mocks base method -func (_m *MockUI) Section(msg string) { - _m.ctrl.Call(_m, "Section", msg) -} - -// Section indicates an expected call of Section -func (_mr *MockUIMockRecorder) Section(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "Section", reflect.TypeOf((*MockUI)(nil).Section), arg0) -} - -// Subsection mocks base method -func (_m *MockUI) Subsection(msg string) { - _m.ctrl.Call(_m, "Subsection", msg) -} - -// Subsection indicates an expected call of Subsection -func (_mr *MockUIMockRecorder) Subsection(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "Subsection", reflect.TypeOf((*MockUI)(nil).Subsection), arg0) -} - -// Warn mocks base method -func (_m *MockUI) Warn(msg string) { - _m.ctrl.Call(_m, "Warn", msg) -} - -// Warn indicates an expected call of Warn -func (_mr *MockUIMockRecorder) Warn(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "Warn", reflect.TypeOf((*MockUI)(nil).Warn), arg0) -} - -// Error mocks base method -func (_m *MockUI) Error(msg string) { - _m.ctrl.Call(_m, "Error", msg) -} - -// Error indicates an expected call of Error -func (_mr *MockUIMockRecorder) Error(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "Error", reflect.TypeOf((*MockUI)(nil).Error), arg0) -} - -// ItemSuccess mocks base method -func (_m *MockUI) ItemSuccess(msg string) { - _m.ctrl.Call(_m, "ItemSuccess", msg) -} - -// ItemSuccess indicates an expected call of ItemSuccess -func (_mr *MockUIMockRecorder) ItemSuccess(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "ItemSuccess", reflect.TypeOf((*MockUI)(nil).ItemSuccess), arg0) -} - -// ItemSkipped mocks base method -func (_m *MockUI) ItemSkipped(msg string) { - _m.ctrl.Call(_m, "ItemSkipped", msg) -} - -// ItemSkipped indicates an expected call of ItemSkipped -func (_mr *MockUIMockRecorder) ItemSkipped(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "ItemSkipped", reflect.TypeOf((*MockUI)(nil).ItemSkipped), arg0) -} - -// ItemFailure mocks base method -func (_m *MockUI) ItemFailure(msg string) { - _m.ctrl.Call(_m, "ItemFailure", msg) -} - -// ItemFailure indicates an expected call of ItemFailure -func (_mr *MockUIMockRecorder) ItemFailure(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "ItemFailure", reflect.TypeOf((*MockUI)(nil).ItemFailure), arg0) -} - -// Confirm mocks base method -func (_m *MockUI) Confirm(msg string) (bool, error) { - ret := _m.ctrl.Call(_m, "Confirm", msg) - ret0, _ := ret[0].(bool) - ret1, _ := ret[1].(error) - return ret0, ret1 -} - -// Confirm indicates an expected call of Confirm -func (_mr *MockUIMockRecorder) Confirm(arg0 interface{}) *gomock.Call { - return _mr.mock.ctrl.RecordCallWithMethodType(_mr.mock, "Confirm", reflect.TypeOf((*MockUI)(nil).Confirm), arg0) -} diff --git a/pkg/grapicmd/internal/module/ui.go b/pkg/grapicmd/internal/module/ui.go deleted file mode 100644 index 2f289963..00000000 --- a/pkg/grapicmd/internal/module/ui.go +++ /dev/null @@ -1,14 +0,0 @@ -package module - -// UI is an interface for intaracting with the terminal. -type UI interface { - Output(msg string) - Section(msg string) - Subsection(msg string) - Warn(msg string) - Error(msg string) - ItemSuccess(msg string) - ItemSkipped(msg string) - ItemFailure(msg string) - Confirm(msg string) (bool, error) -} diff --git a/pkg/grapicmd/internal/module/ui/config.go b/pkg/grapicmd/internal/module/ui/config.go deleted file mode 100644 index 579aab9c..00000000 --- a/pkg/grapicmd/internal/module/ui/config.go +++ /dev/null @@ -1,101 +0,0 @@ -package ui - -import ( - "fmt" - "io" - "strconv" - - "github.com/fatih/color" -) - -type fprintFunc func(w io.Writer, msg string) - -type printConfig struct { - prefix string - colorAttrs []color.Attribute - indent int - allColor bool -} - -type printType int - -const ( - printTypeOutput printType = iota - printTypeSection - printTypeSubsection - printTypeWarn - printTypeError - printTypeItemSuccess - printTypeItemSkipped - printTypeItemFailure -) - -const ( - indentSizeItem = 4 -) - -var ( - configByPrintType = map[printType]printConfig{ - printTypeSection: { - prefix: "โžœ", - colorAttrs: []color.Attribute{color.FgYellow}, - allColor: true, - }, - printTypeSubsection: { - prefix: "โ–ธ", - colorAttrs: []color.Attribute{color.FgBlue}, - allColor: true, - }, - printTypeWarn: { - prefix: "โš ", - colorAttrs: []color.Attribute{color.FgHiYellow}, - allColor: true, - }, - printTypeError: { - prefix: "โ˜“", - colorAttrs: []color.Attribute{color.FgHiRed}, - allColor: true, - }, - printTypeItemSuccess: { - prefix: "โœ”", - colorAttrs: []color.Attribute{color.Bold, color.FgGreen}, - indent: indentSizeItem, - }, - printTypeItemSkipped: { - prefix: "โ•Œ", - colorAttrs: []color.Attribute{color.Bold, color.FgBlue}, - indent: indentSizeItem, - }, - printTypeItemFailure: { - prefix: "โœ—", - colorAttrs: []color.Attribute{color.Bold, color.FgRed}, - indent: indentSizeItem, - }, - } - fprintlnFuncByPrintType = map[printType]fprintFunc{} -) - -func init() { - for pt, cfg := range configByPrintType { - cfg := cfg - fmtStr := "%s" - if cfg.indent > 0 { - fmtStr = "%" + strconv.FormatInt(int64(cfg.indent), 10) + "s" - } - if cfg.allColor { - colored := color.New(cfg.colorAttrs...).FprintfFunc() - fprintlnFuncByPrintType[pt] = func(w io.Writer, msg string) { - colored(w, " "+fmtStr+" %s\n", cfg.prefix, msg) - } - } else { - prefix := color.New(cfg.colorAttrs...).Sprintf(fmtStr, cfg.prefix) - fprintlnFuncByPrintType[pt] = func(w io.Writer, msg string) { - fmt.Fprintf(w, " %s %s\n", prefix, msg) - } - } - } -} - -func (pt printType) Fprintln(w io.Writer, msg string) { - fprintlnFuncByPrintType[pt](w, msg) -} diff --git a/pkg/grapicmd/internal/module/ui/ui.go b/pkg/grapicmd/internal/module/ui/ui.go deleted file mode 100644 index ce53b207..00000000 --- a/pkg/grapicmd/internal/module/ui/ui.go +++ /dev/null @@ -1,92 +0,0 @@ -package ui - -import ( - "fmt" - "io" - - "github.com/izumin5210/clicontrib/pkg/clog" - "github.com/tcnksm/go-input" - - "github.com/izumin5210/grapi/pkg/grapicmd/internal/module" -) - -// New creates a new UI instance. -func New(out io.Writer, in io.Reader) module.UI { - return &uiImpl{ - out: out, - inputUI: &input.UI{ - Reader: in, - Writer: out, - }, - } -} - -type uiImpl struct { - out io.Writer - inSection bool - inputUI *input.UI -} - -func (u *uiImpl) Output(msg string) { - u.inSection = true - fmt.Fprintf(u.out, " %s\n", msg) -} - -func (u *uiImpl) Section(msg string) { - if u.inSection { - fmt.Fprintln(u.out) - u.inSection = false - } - printTypeSection.Fprintln(u.out, msg) -} - -func (u *uiImpl) Subsection(msg string) { - if u.inSection { - fmt.Fprintln(u.out) - u.inSection = false - } - printTypeSubsection.Fprintln(u.out, msg) -} - -func (u *uiImpl) Warn(msg string) { - u.inSection = true - printTypeWarn.Fprintln(u.out, msg) -} - -func (u *uiImpl) Error(msg string) { - u.inSection = true - printTypeError.Fprintln(u.out, msg) -} - -func (u *uiImpl) ItemSuccess(msg string) { - u.inSection = true - printTypeItemSuccess.Fprintln(u.out, msg) -} - -func (u *uiImpl) ItemSkipped(msg string) { - u.inSection = true - printTypeItemSkipped.Fprintln(u.out, msg) -} - -func (u *uiImpl) ItemFailure(msg string) { - u.inSection = true - printTypeItemFailure.Fprintln(u.out, msg) -} - -func (u *uiImpl) Confirm(msg string) (bool, error) { - ans, err := u.inputUI.Ask(fmt.Sprintf("%s [Y/n]", msg), &input.Options{ - HideOrder: true, - Loop: true, - ValidateFunc: func(ans string) error { - clog.Debug("receive user input", "query", msg, "input", ans) - if ans != "Y" && ans != "n" { - return fmt.Errorf("input must be Y or n") - } - return nil - }, - }) - if err != nil { - return false, err - } - return ans == "Y", nil -} diff --git a/pkg/grapicmd/internal/usecase/execute_protoc_usecase.go b/pkg/grapicmd/internal/usecase/execute_protoc_usecase.go deleted file mode 100644 index 1f8d3f43..00000000 --- a/pkg/grapicmd/internal/usecase/execute_protoc_usecase.go +++ /dev/null @@ -1,141 +0,0 @@ -package usecase - -import ( - "os" - "path/filepath" - - "github.com/pkg/errors" - "github.com/spf13/afero" - - "github.com/izumin5210/grapi/pkg/grapicmd/internal/module" - "github.com/izumin5210/grapi/pkg/grapicmd/protoc" - "github.com/izumin5210/grapi/pkg/grapicmd/util/fs" -) - -// ExecuteProtocUsecase is an useecase interface for executing protoc module. -type ExecuteProtocUsecase interface { - Perform() error - InstallPlugins() error - ExecuteProtoc() error -} - -type executeProtocUsecase struct { - cfg *protoc.Config - fs afero.Fs - ui module.UI - commandFactory module.CommandFactory - rootDir, binDir string -} - -// NewExecuteProtocUsecase returns an new ExecuteProtocUsecase implementation instance. -func NewExecuteProtocUsecase(cfg *protoc.Config, fs afero.Fs, ui module.UI, commandFactory module.CommandFactory, rootDir string) ExecuteProtocUsecase { - return &executeProtocUsecase{ - cfg: cfg, - fs: fs, - ui: ui, - commandFactory: commandFactory, - rootDir: rootDir, - binDir: filepath.Join(rootDir, "bin"), - } -} - -func (u *executeProtocUsecase) Perform() error { - u.ui.Section("Execute protoc") - u.ui.Subsection("Install plugins") - err := errors.WithStack(u.InstallPlugins()) - if err != nil { - return err - } - u.ui.Subsection("Execute protoc") - return errors.WithStack(u.ExecuteProtoc()) -} - -func (u *executeProtocUsecase) InstallPlugins() error { - if err := fs.CreateDirIfNotExists(u.fs, u.binDir); err != nil { - return errors.WithStack(err) - } - var errs []error - for _, plugin := range u.cfg.Plugins { - ok, err := u.installPlugin(plugin) - if err != nil { - errs = append(errs, err) - u.ui.ItemFailure(plugin.BinName()) - } else if !ok { - u.ui.ItemSkipped(plugin.BinName()) - } else { - u.ui.ItemSuccess(plugin.BinName()) - } - } - if len(errs) > 0 { - for _, err := range errs { - u.ui.Error(err.Error()) - } - return errors.New("failed to install protoc plugins") - } - return nil -} - -func (u *executeProtocUsecase) ExecuteProtoc() error { - protoFiles, err := u.cfg.ProtoFiles(u.fs, u.rootDir) - if err != nil { - return errors.WithStack(err) - } - var errs []error - for _, path := range protoFiles { - err = u.executeProtoc(path) - relPath, _ := filepath.Rel(u.rootDir, path) - if err == nil { - u.ui.ItemSuccess(relPath) - } else { - errs = append(errs, err) - u.ui.ItemFailure(relPath) - } - } - if len(errs) > 0 { - for _, err := range errs { - u.ui.Error(err.Error()) - } - return errors.New("failed to execute protoc") - } - return nil -} - -func (u *executeProtocUsecase) installPlugin(plugin *protoc.Plugin) (bool, error) { - binPath := filepath.Join(u.binDir, plugin.BinName()) - if ok, err := afero.Exists(u.fs, binPath); err != nil { - return false, errors.Wrapf(err, "failed to get %q binary", plugin.BinName()) - } else if ok { - return false, nil - } - dir := filepath.Join(u.rootDir, plugin.Path) - if ok, _ := afero.DirExists(u.fs, dir); !ok { - return false, errors.Errorf("%s is not found", plugin.Path) - } - cmd := u.commandFactory.Create([]string{"go", "install", "."}) - out, err := cmd.SetDir(dir).AddEnv("GOBIN", u.binDir).Exec() - if err != nil { - return false, errors.Wrapf(err, "failed to execute module: %s", string(out)) - } - return true, nil -} - -func (u *executeProtocUsecase) executeProtoc(protoPath string) error { - outDir, err := u.cfg.OutDirOf(u.rootDir, protoPath) - if err != nil { - return errors.WithStack(err) - } - if err = fs.CreateDirIfNotExists(u.fs, outDir); err != nil { - return errors.WithStack(err) - } - cmds, err := u.cfg.Commands(u.rootDir, protoPath) - if err != nil { - return errors.WithStack(err) - } - for _, cmd := range cmds { - out, err := u.commandFactory.Create(cmd).AddEnv("PATH", u.binDir+string(filepath.ListSeparator)+os.Getenv("PATH")).SetDir(u.rootDir).Exec() - if err != nil { - return errors.Wrapf(err, "failed to execute module: %s", string(out)) - } - } - return nil -} diff --git a/pkg/grapicmd/internal/usecase/init_config.go b/pkg/grapicmd/internal/usecase/init_config.go new file mode 100644 index 00000000..3520df4e --- /dev/null +++ b/pkg/grapicmd/internal/usecase/init_config.go @@ -0,0 +1,32 @@ +package usecase + +import "bytes" + +type InitConfig struct { + Revision string + Branch string + Version string + HEAD bool + GrapiReplacementURL string + Package string + Dep bool +} + +func (c *InitConfig) BuildSpec() string { + buf := bytes.NewBufferString("") + var constraint string + switch { + case c.Revision != "": + constraint = c.Revision + case c.Branch != "": + constraint = c.Branch + case c.HEAD: + constraint = "master" + case c.Version != "": + constraint = c.Version + } + if constraint != "" { + buf.WriteString("@" + constraint) + } + return buf.String() +} diff --git a/pkg/grapicmd/internal/usecase/init_config_test.go b/pkg/grapicmd/internal/usecase/init_config_test.go new file mode 100644 index 00000000..e8d45c7d --- /dev/null +++ b/pkg/grapicmd/internal/usecase/init_config_test.go @@ -0,0 +1,43 @@ +package usecase + +import "testing" + +func TestInitConfig_BuildSpec(t *testing.T) { + cases := []struct { + test string + cfg InitConfig + out string + }{ + { + test: "empty", + }, + { + test: "HEAD", + cfg: InitConfig{HEAD: true}, + out: "@master", + }, + { + test: "branch", + cfg: InitConfig{Branch: "foo/bar"}, + out: "@foo/bar", + }, + { + test: "version", + cfg: InitConfig{Version: "^0.3.0"}, + out: "@^0.3.0", + }, + { + test: "revision", + cfg: InitConfig{Revision: "a2489d2"}, + out: "@a2489d2", + }, + } + + for _, tc := range cases { + t.Run(tc.test, func(t *testing.T) { + if got, want := tc.cfg.BuildSpec(), tc.out; got != want { + t.Errorf("BuildSpec() returned %q, want %q", got, want) + } + }) + } +} diff --git a/pkg/grapicmd/internal/usecase/initialize_project_usecase.go b/pkg/grapicmd/internal/usecase/initialize_project_usecase.go index fa2f9221..cdb3d3c8 100644 --- a/pkg/grapicmd/internal/usecase/initialize_project_usecase.go +++ b/pkg/grapicmd/internal/usecase/initialize_project_usecase.go @@ -1,61 +1,174 @@ package usecase import ( + "context" + "os" + "path/filepath" + + "github.com/izumin5210/clig/pkg/clib" + "github.com/izumin5210/execx" + "github.com/izumin5210/gex" + "github.com/izumin5210/gex/pkg/tool" "github.com/pkg/errors" + "github.com/spf13/afero" - "github.com/izumin5210/grapi/pkg/grapicmd/internal/module" + "github.com/izumin5210/grapi/pkg/cli" + "github.com/izumin5210/grapi/pkg/gencmd" + _ "github.com/izumin5210/grapi/pkg/grapicmd/internal/usecase/template" + "github.com/izumin5210/grapi/pkg/grapicmd/util/fs" ) // InitializeProjectUsecase is an interface to create a new grapi project. type InitializeProjectUsecase interface { - Perform(rootDir string, depSkipped, headUsed bool) error - GenerateProject(rootDir string, headUsed bool) error - InstallDeps(rootDir string) error + Perform(rootDir string, cfg InitConfig) error + GenerateProject(rootDir, pkgName string) error + InstallDeps(rootDir string, cfg InitConfig) error } // NewInitializeProjectUsecase creates a new InitializeProjectUsecase instance. -func NewInitializeProjectUsecase(ui module.UI, generator module.ProjectGenerator, commandFactory module.CommandFactory, version string) InitializeProjectUsecase { +func NewInitializeProjectUsecase(ui cli.UI, fs afero.Fs, generator gencmd.Generator, io *clib.IO, exec *execx.Executor, gexCfg *gex.Config) InitializeProjectUsecase { return &initializeProjectUsecase{ - ui: ui, - generator: generator, - commandFactory: commandFactory, - version: version, + ui: ui, + fs: fs, + generator: generator, + io: io, + exec: exec, + gexCfg: gexCfg, } } type initializeProjectUsecase struct { - ui module.UI - generator module.ProjectGenerator - commandFactory module.CommandFactory - version string + ui cli.UI + fs afero.Fs + generator gencmd.Generator + io *clib.IO + exec *execx.Executor + gexCfg *gex.Config } -func (u *initializeProjectUsecase) Perform(rootDir string, depSkipped, headUsed bool) error { +func (u *initializeProjectUsecase) Perform(rootDir string, cfg InitConfig) error { u.ui.Section("Initialize project") var err error - err = u.GenerateProject(rootDir, headUsed) + err = u.GenerateProject(rootDir, cfg.Package) if err != nil { return errors.Wrap(err, "failed to initialize project") } u.ui.Subsection("Install dependencies") - if !depSkipped { - err = u.InstallDeps(rootDir) - if err != nil { - return errors.Wrap(err, "failed to execute `dep ensure`") - } + err = u.InstallDeps(rootDir, cfg) + if err != nil { + return errors.Wrap(err, "failed to install dependencies") } return nil } -func (u *initializeProjectUsecase) GenerateProject(rootDir string, headUsed bool) error { - return errors.WithStack(u.generator.GenerateProject(rootDir, headUsed)) +func (u *initializeProjectUsecase) GenerateProject(rootDir, pkgName string) error { + importPath, err := fs.GetImportPath(rootDir) + if err != nil { + return errors.WithStack(err) + } + + if pkgName == "" { + pkgName, err = fs.GetPackageName(rootDir) + if err != nil { + return errors.Wrap(err, "failed to decide a package name") + } + } + + data := map[string]interface{}{ + "packageName": pkgName, + "importPath": importPath, + } + return errors.WithStack(u.generator.Generate(data)) } -func (u *initializeProjectUsecase) InstallDeps(rootDir string) error { - cmd := u.commandFactory.Create([]string{"dep", "ensure", "-v"}) - _, err := cmd.ConnectIO().SetDir(rootDir).Exec() - return errors.WithStack(err) +func (u *initializeProjectUsecase) InstallDeps(rootDir string, cfg InitConfig) error { + invoke := func(name string, args ...string) error { + cmd := u.exec.CommandContext(context.TODO(), name, args...) + cmd.Stdout = u.io.Out + cmd.Stderr = u.io.Err + cmd.Stdin = u.io.In + cmd.Dir = rootDir + if !cfg.Dep { + cmd.Env = append(os.Environ(), "GO111MODULE=on") + } + + return errors.WithStack(cmd.Run()) + } + + if cfg.Dep { + err := invoke("dep", "init") + if err != nil { + return errors.WithStack(err) + } + } else if ok, _ := afero.Exists(u.fs, filepath.Join(rootDir, "go.mod")); ok { + err := invoke("go", "mod", "tidy") + if err != nil { + return errors.WithStack(err) + } + } else { + err := invoke("go", "mod", "init", filepath.Base(rootDir)) + if err != nil { + return errors.WithStack(err) + } + } + + if cfg.Dep { + if spec := cfg.BuildSpec(); spec != "" { + u.ui.ItemFailure("--version, --revision, --branch and --HEAD are not supported in dep mode") + } + } else { + if cfg.GrapiReplacementURL != "" { + err := invoke("go", "mod", "edit", "-replace", "github.com/izumin5210/grapi="+cfg.GrapiReplacementURL) + if err != nil { + return errors.WithStack(err) + } + } else if spec := cfg.BuildSpec(); spec != "" { + pkg := "github.com/izumin5210/grapi/pkg/grapiserver" + err := invoke("go", "get", pkg+spec) + if err != nil { + return errors.WithStack(err) + } + } + err := invoke("go", "get", "./...") + if err != nil { + return errors.WithStack(err) + } + } + + u.gexCfg.WorkingDir = rootDir + u.gexCfg.RootDir = rootDir + toolRepo, err := u.gexCfg.Create() + if err != nil { + return errors.WithStack(err) + } + + { + // regen manifest + path := filepath.Join(u.gexCfg.RootDir, u.gexCfg.ManifestName) + m, err := tool.NewParser(u.fs, u.gexCfg.ManagerType).Parse(path) + if err != nil { + return errors.Wrapf(err, "%s was not found", path) + } + err = tool.NewWriter(u.fs).Write(path, m) + if err != nil { + return errors.WithStack(err) + } + } + + err = toolRepo.BuildAll(context.Background()) + if err != nil { + return errors.WithStack(err) + } + + if !cfg.Dep { + err := invoke("go", "mod", "tidy") + if err != nil { + return errors.WithStack(err) + } + } + + return nil } diff --git a/pkg/grapicmd/internal/usecase/template/gen.go b/pkg/grapicmd/internal/usecase/template/gen.go new file mode 100644 index 00000000..70a73172 --- /dev/null +++ b/pkg/grapicmd/internal/usecase/template/gen.go @@ -0,0 +1,3 @@ +//go:generate statik -src ./init -dest .. -p template -f -m + +package template diff --git a/pkg/grapicmd/internal/module/generator/template/init/.gitignore.tmpl b/pkg/grapicmd/internal/usecase/template/init/.gitignore.tmpl similarity index 100% rename from pkg/grapicmd/internal/module/generator/template/init/.gitignore.tmpl rename to pkg/grapicmd/internal/usecase/template/init/.gitignore.tmpl diff --git a/pkg/grapicmd/internal/module/generator/template/init/app/server/.keep.tmpl b/pkg/grapicmd/internal/usecase/template/init/api/protos/.keep.tmpl similarity index 100% rename from pkg/grapicmd/internal/module/generator/template/init/app/server/.keep.tmpl rename to pkg/grapicmd/internal/usecase/template/init/api/protos/.keep.tmpl diff --git a/testing/api/protos/.keep b/pkg/grapicmd/internal/usecase/template/init/api/protos/type/.keep.tmpl similarity index 100% rename from testing/api/protos/.keep rename to pkg/grapicmd/internal/usecase/template/init/api/protos/type/.keep.tmpl diff --git a/testing/app/server/.keep b/pkg/grapicmd/internal/usecase/template/init/app/server/.keep.tmpl similarity index 100% rename from testing/app/server/.keep rename to pkg/grapicmd/internal/usecase/template/init/app/server/.keep.tmpl diff --git a/pkg/grapicmd/internal/module/generator/template/init/cmd/server/run.go.tmpl b/pkg/grapicmd/internal/usecase/template/init/cmd/server/main.go.tmpl similarity index 61% rename from pkg/grapicmd/internal/module/generator/template/init/cmd/server/run.go.tmpl rename to pkg/grapicmd/internal/usecase/template/init/cmd/server/main.go.tmpl index f9f171d9..41c9e51e 100644 --- a/pkg/grapicmd/internal/module/generator/template/init/cmd/server/run.go.tmpl +++ b/pkg/grapicmd/internal/usecase/template/init/cmd/server/main.go.tmpl @@ -4,19 +4,12 @@ import ( "os" "google.golang.org/grpc/grpclog" - - "{{ .importPath }}/app" ) func main() { - os.Exit(run()) -} - -func run() int { - err := app.Run() + err := run() if err != nil { grpclog.Errorf("server was shutdown with errors: %v", err) - return 1 + os.Exit(1) } - return 0 } diff --git a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ProjectGenerator-app-run.go b/pkg/grapicmd/internal/usecase/template/init/cmd/server/run.go.tmpl similarity index 56% rename from pkg/grapicmd/internal/module/generator/.snapshots/Test_ProjectGenerator-app-run.go rename to pkg/grapicmd/internal/usecase/template/init/cmd/server/run.go.tmpl index 13226b76..ddd13198 100644 --- a/pkg/grapicmd/internal/module/generator/.snapshots/Test_ProjectGenerator-app-run.go +++ b/pkg/grapicmd/internal/usecase/template/init/cmd/server/run.go.tmpl @@ -1,17 +1,20 @@ -package app +package main import ( + "github.com/srvc/appctx" + "github.com/izumin5210/grapi/pkg/grapiserver" ) -// Run starts the grapiserver. -func Run() error { +func run() error { + // Application context + ctx := appctx.Global() + s := grapiserver.New( grapiserver.WithDefaultLogger(), grapiserver.WithServers( // TODO ), ) - return s.Serve() + return s.Serve(ctx) } - diff --git a/pkg/grapicmd/internal/usecase/template/init/grapi.toml.tmpl b/pkg/grapicmd/internal/usecase/template/init/grapi.toml.tmpl new file mode 100644 index 00000000..215c06b5 --- /dev/null +++ b/pkg/grapicmd/internal/usecase/template/init/grapi.toml.tmpl @@ -0,0 +1,25 @@ +package = "{{.packageName}}" + +[grapi] +server_dir = "./app/server" + +[protoc] +protos_dir = "./api/protos" +out_dir = "./api" +import_dirs = [ + "./api/protos", + '{{`{{ module "github.com/grpc-ecosystem/grpc-gateway" }}`}}', + '{{`{{ module "github.com/grpc-ecosystem/grpc-gateway" }}`}}/third_party/googleapis', +] + + [[protoc.plugins]] + name = "go" + args = { plugins = "grpc", paths = "source_relative" } + + [[protoc.plugins]] + name = "grpc-gateway" + args = { logtostderr = true, paths = "source_relative" } + + [[protoc.plugins]] + name = "swagger" + args = { logtostderr = true } diff --git a/pkg/grapicmd/internal/usecase/template/init/tools.go b/pkg/grapicmd/internal/usecase/template/init/tools.go new file mode 100644 index 00000000..09481548 --- /dev/null +++ b/pkg/grapicmd/internal/usecase/template/init/tools.go @@ -0,0 +1,16 @@ +// +build tools + +package tools + +// tool dependencies +import ( + _ "github.com/golang/protobuf/protoc-gen-go" + _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway" + _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger" + _ "github.com/izumin5210/gex/cmd/gex" + _ "github.com/izumin5210/grapi/cmd/grapi" + _ "github.com/izumin5210/grapi/cmd/grapi-gen-command" + _ "github.com/izumin5210/grapi/cmd/grapi-gen-scaffold-service" + _ "github.com/izumin5210/grapi/cmd/grapi-gen-service" + _ "github.com/izumin5210/grapi/cmd/grapi-gen-type" +) diff --git a/pkg/grapicmd/internal/usecase/template/statik.go b/pkg/grapicmd/internal/usecase/template/statik.go new file mode 100644 index 00000000..0bafdf93 --- /dev/null +++ b/pkg/grapicmd/internal/usecase/template/statik.go @@ -0,0 +1,13 @@ +// Code generated by statik. DO NOT EDIT. + +// Package statik contains static assets. +package template + +import ( + "github.com/rakyll/statik/fs" +) + +func init() { + data := "PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x17\x00 \x00cmd/server/main.go.tmplUT\x05\x00\x01\x80Cm8,\xcb\xc1j\x860\x10\x04\xe0\xf3\xeeSl\x03\x85\x04JJ\xaf\x82G\x1f$\xd8\xb8\x86\xc6\xacl\xa2\x16\x8a\xef^\"\xffe`f\xf8\xf60\xff\x04\x8e\xb4\x85T\x10\xd3\xb6\x8b6\xb2\x08F\xaaA\x04\xc3\"\x9c\xa3g\xc9\xa1\xb0\x17\xe5O\xd6}~\"\x0b\x1bt\x88\xcbQ\xe6\xc7[G\x7f\x08Q\x95\x86\x91\xf4(\xd6!\xa4\x85\xfa\xf06RI\xb9\xdf\xf0\xa2~R\x15]\xac\xa9Q\xcf\xa8t\x85Ju=\xda\xb7\\\x85\xae\xd4\xd6\xeeD\xeb@\xef\xa7\xf9\xe8\xc5!\x80T?\xfd\xa6f\xbf\x1c\xc2\x8d7\xfe\x07\x00\x00\xff\xffPK\x07\x08\xb3\x90\xcfL\x99\x00\x00\x00\xbf\x00\x00\x00PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x16\x00 \x00cmd/server/run.go.tmplUT\x05\x00\x01\x80Cm8l\x8dAK31\x10@\xcf\x99_1\xec)\x81\x8f\xcd\xa7\xe0E\xf0 \x14\xbc\x88=(xN\xc34\x1d\xba\x9b\x84\xc9\xa4.\x8a\xff]\xb6\xbdT\xf0\xf6x\xf3f\xa6\x86x\x0c\x89p\x0e\x9c\x01x\xaeE\x14-\x98!\xb1\x1e\xfan\x8ce\xf6MN\xd1\x87Z\xa3.\x03\xfc\x1e\xf1g\x9f9\xdf\xdd\xde\xfc\xf7IBe_\x8f\xe9B\x8d\xe4D2\x80\x03\xd8\xf7\x1cQz\xb6\x0eI\xa4\x08~\x81\xf1\x1e\x1fk\x9d8\x06\xe5\x921\x96\xac\xb4(\x98\xa8\x0b\xde?\xe0\xe5\xdb\xf84\x95]\x98\xac\x030m\xd5W\x97\xc7\x17\xfa\xb0`\xcc\xb5zg=lh\x1f\xfa\xa4\xcf%%\x12\xeb\xfe\xfd\x91\xbc\x9e\xb1\xad\xdb\xde\xe3\xdbv\xb3\x05c\xd6\xd2\x81\x11\xd2.\x19\xdbx\x8el\xd4\xc5\xc17\xfc\x04\x00\x00\xff\xffPK\x07\x08\xabp\xba4\xce\x00\x00\x00&\x01\x00\x00PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\x00 \x00grapi.toml.tmplUT\x05\x00\x01\x80Cm8\xa4\x91A\x8e\xf20\x0c\x85\xf7>\x85\x95\x0d\x1b\xfe\xf6\x04\\\xe1\xbf\x00\xaa\xc0\x93Z&\x9a\x16GN\x02BQ\xee>j\xe9\x82\x99\xc5\xcc\x82U\xf4\x9e\xbf\xf8\xc5N$\xffI\xc2x@Wk\xb7\xa9\xff4sk\x0e\xe0(F1\x0c\x90\xd8nl\xa71\xd8\x02v=\xc5\xd8?\xbd\x05\x8a\xa6Y\xfd\x00\xeb\x99^\xa9\xd0?=\x07Z\xf2\xb7\x82\x830G\xb5\xd5Lx\xc0#\xe0\x8f+{@\xdc\xd5z\xae\x15g\x1d\xcb\xc4\xe8$\xe4K\xf9\xe8\xbc\xce\xbdX\xf4\xff\xd8kz\xa4\xcc\x9b\x14\xca|\xa7\x87\xc3\xd6\xce\xad\xed\xdem\xd0\xe7K\xb0\xf1\x14\xc9\xf2\xa3\x17U\x99\x98bH\xbb=\x0c\x00\x88\xc7m\xee.NE\xc25\x0d\x03 ^i^w)\xea\x00\x91L\x96\xd9*n\xc8Z\xb1\xe8\xdd\x1e#\xe5\xcb\xaa\x93\x16\xf3|2\x9e(\x87\x1b;l\x7f7\x7f}\xebk\xcc\xa4\x925\xe5\x91mYt\xb6\xc2\xef\x05\xa5;\x89,\x7f\xfck\x066\xf8\n\x00\x00\xff\xffPK\x07\x08\xfaZgN\xfd\x00\x00\x00F\x02\x00\x00PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00 \x00tools.goUT\x05\x00\x01\x80Cm8\x9c\x8e\xddJ\xc4@\x0c\x85\xaf\x9d\xa7\x18z\xa5H\x8d\n>\x8f\xa4\x994\x06;\x93a~\\\xeb\xd3\xcb\xee*(\x0b\x0b\xedU\xce \xdf\x17\x02\xe0\xef\xa7\xaeK\xf0\xcdl\xa9\xcee\xa4w\x14\xfe\xad\x00\xa7\xe4\x03gN\x81\x13)W\xa71[i\xfe\xd6\xdd\xbc\xfaA\xb4\xbd\xf5\xe9\x81,\x82\xd8\x82I \x17k6\xf5\xf9\x1ch\x14N\xa3\xd8pA\x97L#\x93\xd5\xb56\xfe\xa9\x82\x8d\x0f\xb8\xfe3\xff\xec\xf7\xde\xa8\x07\x14\xe1r\xa1\xebW\x8f\x9a^\x9e\x9f\x1eA\xf8\x13(\x86\xe3\xbc\x8a\x15\xccz\x06\x8fi\x03zz\x84,FLa\xabV \xe7\xd9\x960V.\x1fJ\xbc\xd9\xdf\xa7\xb55\xf3\xe0\xee\xdcw\x00\x00\x00\xff\xffPK\x07\x083'=\x96\xb9\x00\x00\x00%\x02\x00\x00PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(\xb3\x90\xcfL\x99\x00\x00\x00\xbf\x00\x00\x00\x17\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x00\x00\x00\x00cmd/server/main.go.tmplUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(\xabp\xba4\xce\x00\x00\x00&\x01\x00\x00\x16\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\xe7\x00\x00\x00cmd/server/run.go.tmplUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(\xfaZgN\xfd\x00\x00\x00F\x02\x00\x00\x0f\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x02\x02\x00\x00grapi.toml.tmplUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(3'=\x96\xb9\x00\x00\x00%\x02\x00\x00\x08\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81E\x03\x00\x00tools.goUT\x05\x00\x01\x80Cm8PK\x05\x06\x00\x00\x00\x00\x04\x00\x04\x00 \x01\x00\x00=\x04\x00\x00\x00\x00" + fs.Register(data) +} diff --git a/pkg/grapicmd/util/fs/dir.go b/pkg/grapicmd/util/fs/dir.go index d65a6681..014142eb 100644 --- a/pkg/grapicmd/util/fs/dir.go +++ b/pkg/grapicmd/util/fs/dir.go @@ -1,14 +1,14 @@ package fs import ( - "github.com/izumin5210/clicontrib/pkg/clog" "github.com/pkg/errors" "github.com/spf13/afero" + "go.uber.org/zap" ) // CreateDirIfNotExists creates a directory if it does not exist. func CreateDirIfNotExists(fs afero.Fs, path string) (err error) { err = fs.MkdirAll(path, 0755) - clog.Debug("CreateDirIfNotExists", "path", path, "error", err) + zap.L().Debug("CreateDirIfNotExists", zap.String("path", path), zap.Error(err)) return errors.Wrapf(err, "failed to create %q directory", path) } diff --git a/pkg/grapicmd/util/fs/exec.go b/pkg/grapicmd/util/fs/exec.go new file mode 100644 index 00000000..7a9256fd --- /dev/null +++ b/pkg/grapicmd/util/fs/exec.go @@ -0,0 +1,47 @@ +package fs + +import ( + "os" + "path/filepath" + "sort" + "strings" + "sync" + + "github.com/spf13/afero" +) + +func ListExecutableWithPrefix(fs afero.Fs, prefix string) []string { + var wg sync.WaitGroup + ch := make(chan string) + + for _, path := range filepath.SplitList(os.Getenv("PATH")) { + wg.Add(1) + go func(path string) { + defer wg.Done() + + files, err := afero.ReadDir(fs, path) + if err != nil { + return + } + + for _, f := range files { + if m := f.Mode(); !f.IsDir() && m&0111 != 0 && strings.HasPrefix(f.Name(), prefix) { + ch <- f.Name() + } + } + }(path) + } + + go func() { + wg.Wait() + close(ch) + }() + + execs := make([]string, 0, 100) + for exec := range ch { + execs = append(execs, exec) + } + sort.Strings(execs) + + return execs +} diff --git a/pkg/grapicmd/util/fs/path.go b/pkg/grapicmd/util/fs/path.go index 9b6f2fab..e63c65d9 100644 --- a/pkg/grapicmd/util/fs/path.go +++ b/pkg/grapicmd/util/fs/path.go @@ -5,22 +5,31 @@ import ( "go/parser" "go/token" "os" + "os/user" "path/filepath" "strings" - "github.com/izumin5210/clicontrib/pkg/clog" "github.com/pkg/errors" "github.com/spf13/afero" - "golang.org/x/sync/errgroup" + "go.uber.org/zap" ) +type getOSUserFunc func() (*user.User, error) + +const ( + // PackageSeparator is a package separator string on protobuf. + PackageSeparator = "." +) + +// Make visible for testing var ( - // BuildContext is a build context object. BuildContext build.Context + GetOSUser getOSUserFunc ) func init() { BuildContext = build.Default + GetOSUser = user.Current } // GetImportPath creates the golang package path from the given path. @@ -34,52 +43,36 @@ func GetImportPath(rootPath string) (importPath string, err error) { } } if importPath == "" { - err = errors.New("failed to get the import path") + importPath = filepath.Base(rootPath) } return } -var ( - requiredFiles = []string{"grapi.toml"} - requiredDirs = []string{"app", "api", "cmd"} -) - -// LookupRoot returns the application's root directory if the current directory is inside of a grapi application. -func LookupRoot(fs afero.Fs, dir string) (string, bool) { - var eg errgroup.Group - - for _, f := range requiredFiles { - f := f - eg.Go(func() error { - ok, err := afero.Exists(fs, filepath.Join(dir, f)) - if err != nil || !ok { - return errors.Errorf("%s does not exist", f) - } - return nil - }) - } - - for _, d := range requiredDirs { - d := d - eg.Go(func() error { - ok, err := afero.DirExists(fs, filepath.Join(dir, d)) - if err != nil || !ok { - return errors.Errorf("%s does not exist", d) - } - return nil - }) +// GetPackageName generates the package name of this application from the given path and envs. +func GetPackageName(rootPath string) (string, error) { + importPath, err := GetImportPath(rootPath) + if err != nil { + return "", errors.WithStack(err) } - - if eg.Wait() == nil { - return dir, true + entries := strings.Split(importPath, string(filepath.Separator)) + if len(entries) < 2 { + u, err := GetOSUser() + if err != nil { + return "", errors.WithStack(err) + } + entries = []string{u.Username, entries[0]} } - - p := dir[len(filepath.VolumeName(dir)):] - if p == string(filepath.Separator) { - return "", false + entries = entries[len(entries)-2:] + if strings.Contains(entries[0], PackageSeparator) { + s := strings.Split(entries[0], PackageSeparator) + for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { + s[i], s[j] = s[j], s[i] + } + entries[0] = strings.Join(s, PackageSeparator) } - - return LookupRoot(fs, filepath.Dir(dir)) + pkgName := strings.Join(entries[len(entries)-2:], PackageSeparator) + pkgName = strings.Replace(pkgName, "-", "_", -1) + return pkgName, nil } // FindMainPackagesAndSources returns go source file names by main package directories. @@ -95,12 +88,12 @@ func FindMainPackagesAndSources(fs afero.Fs, dir string) (map[string][]string, e } data, err := afero.ReadFile(fs, path) if err != nil { - clog.Warn("failed to read a file", "error", err, "path", path) + zap.L().Warn("failed to read a file", zap.Error(err), zap.String("path", path)) return nil } f, err := parser.ParseFile(fset, "", data, parser.PackageClauseOnly) if err != nil { - clog.Warn("failed to parse a file", "error", err, "path", path, "body", string(data)) + zap.L().Warn("failed to parse a file", zap.Error(err), zap.String("path", path), zap.String("body", string(data))) return nil } if f.Package.IsValid() && f.Name.Name == "main" { diff --git a/pkg/grapicmd/util/fs/path_test.go b/pkg/grapicmd/util/fs/path_test.go index 11328407..f476371c 100644 --- a/pkg/grapicmd/util/fs/path_test.go +++ b/pkg/grapicmd/util/fs/path_test.go @@ -1,6 +1,7 @@ package fs import ( + "os/user" "path/filepath" "testing" @@ -8,40 +9,99 @@ import ( "github.com/spf13/afero" ) -func Test_LookupRoot(t *testing.T) { - fs := afero.NewMemMapFs() +func Test_GetImportPath(t *testing.T) { + defer func(tmp string) { BuildContext.GOPATH = tmp }(BuildContext.GOPATH) + BuildContext.GOPATH = "/home/go" + + cases := []struct { + test string + in string + out string + isErr bool + }{ + { + test: "inside of GOPATH", + in: "/home/go/src/github.com/izumin5210/testapp", + out: "github.com/izumin5210/testapp", + }, + { + test: "directly under GOPATH", + in: "/home/go/src/testapp", + out: "testapp", + }, + { + test: "outside of GOPATH", + in: "/home/go/testapp", + out: "testapp", + }, + } + + for _, c := range cases { + t.Run(c.test, func(t *testing.T) { + out, err := GetImportPath(c.in) + + if got, want := err != nil, c.isErr; got != want { + t.Errorf("Returned error is %t, want %t (%v)", got, want, err) + } + + if got, want := out, c.out; got != want { + t.Errorf("Returned %s, want %s", got, want) + } + }) + } +} - fs.MkdirAll("/home/root/app/server", 0755) - fs.MkdirAll("/home/root/api/proto", 0755) - fs.MkdirAll("/home/root/cmd/server", 0755) - fs.MkdirAll("/home/app", 0755) - afero.WriteFile(fs, "/home/root/grapi.toml", []byte(""), 0644) - afero.WriteFile(fs, "/home/app/grapi.toml", []byte(""), 0644) +func Test_GetPackageName(t *testing.T) { + defer func(tmp getOSUserFunc) { GetOSUser = tmp }(GetOSUser) + GetOSUser = func() (*user.User, error) { return &user.User{Username: "testuser"}, nil } + defer func(tmp string) { BuildContext.GOPATH = tmp }(BuildContext.GOPATH) + BuildContext.GOPATH = "/home/go" cases := []struct { - cwd string - root string - ok bool + test string + in string + out string + isErr bool }{ - {cwd: "/", root: "", ok: false}, - {cwd: "/home", root: "", ok: false}, - {cwd: "/home/root", root: "/home/root", ok: true}, - {cwd: "/home/root/app", root: "/home/root", ok: true}, - {cwd: "/home/root/api/proto", root: "/home/root", ok: true}, - {cwd: "/home/app", root: "", ok: false}, - {cwd: "/home/api", root: "", ok: false}, + { + test: "inside of GOPATH", + in: "/home/go/src/github.com/izumin5210/testapp", + out: "izumin5210.testapp", + }, + { + test: "directly under GOPATH", + in: "/home/go/src/testapp", + out: "testuser.testapp", + }, + { + test: "company name includes separators", + in: "/home/go/src/go.example.com/testapp", + out: "com.example.go.testapp", + }, + { + test: "package name includes hyphens", + in: "/home/go/src/go.example.com/test-app", + out: "com.example.go.test_app", + }, + { + test: "outside of GOPATH", + in: "/home/go/testapp", + out: "testuser.testapp", + }, } for _, c := range cases { - root, ok := LookupRoot(fs, c.cwd) + t.Run(c.test, func(t *testing.T) { + out, err := GetPackageName(c.in) - if got, want := ok, c.ok; got != want { - t.Errorf("LookupRoot(fs, %q) returns %t, want %t", c.cwd, got, want) - } + if got, want := err != nil, c.isErr; got != want { + t.Errorf("Returned error is %t, want %t (%v)", got, want, err) + } - if got, want := root, c.root; got != want { - t.Errorf("LookupRoot(fs, %q) returns %q, want %q", c.cwd, got, want) - } + if got, want := out, c.out; got != want { + t.Errorf("Returned %s, want %s", got, want) + } + }) } } diff --git a/pkg/grapiserver/cmux.go b/pkg/grapiserver/cmux.go new file mode 100644 index 00000000..98e446fd --- /dev/null +++ b/pkg/grapiserver/cmux.go @@ -0,0 +1,37 @@ +package grapiserver + +import ( + "net" + + "github.com/soheilhy/cmux" + "google.golang.org/grpc/grpclog" +) + +type cmuxServer struct { + mux cmux.CMux + lis net.Listener +} + +func newCmuxServer(lis net.Listener) *cmuxServer { + return &cmuxServer{ + mux: cmux.New(lis), + lis: lis, + } +} + +// Serve implements Server.Serve +func (s *cmuxServer) Serve() { + grpclog.Info("mux is starting %s", s.lis.Addr()) + + err := s.mux.Serve() + + grpclog.Infof("mux is closed: %v", err) +} + +func (s *cmuxServer) GRPCListener() net.Listener { + return s.mux.MatchWithWriters(cmux.HTTP2MatchHeaderFieldSendSettings("content-type", "application/grpc")) +} + +func (s *cmuxServer) HTTPListener() net.Listener { + return s.mux.Match(cmux.HTTP2(), cmux.HTTP1Fast()) +} diff --git a/pkg/grapiserver/config.go b/pkg/grapiserver/config.go index fde1839f..ec66195a 100644 --- a/pkg/grapiserver/config.go +++ b/pkg/grapiserver/config.go @@ -1,7 +1,9 @@ package grapiserver import ( + "crypto/tls" "net" + "net/http" "os" "path/filepath" pkg_runtime "runtime" @@ -23,6 +25,11 @@ func createDefaultConfig() *Config { Network: "tcp", Addr: ":3000", }, + GatewayServerConfig: &HTTPServerConfig{ + ReadTimeout: 8 * time.Second, + WriteTimeout: 8 * time.Second, + IdleTimeout: 2 * time.Minute, + }, MaxConcurrentStreams: 1000, } if pkg_runtime.GOOS == "windows" { @@ -59,6 +66,28 @@ func (a *Address) createListener() (net.Listener, error) { return lis, nil } +type HTTPServerConfig struct { + TLSConfig *tls.Config + ReadTimeout time.Duration + ReadHeaderTimeout time.Duration + WriteTimeout time.Duration + IdleTimeout time.Duration + MaxHeaderBytes int + TLSNextProto map[string]func(*http.Server, *tls.Conn, http.Handler) + ConnState func(net.Conn, http.ConnState) +} + +func (c *HTTPServerConfig) applyTo(s *http.Server) { + s.TLSConfig = c.TLSConfig + s.ReadTimeout = c.ReadTimeout + s.ReadHeaderTimeout = c.ReadHeaderTimeout + s.WriteTimeout = c.WriteTimeout + s.IdleTimeout = c.IdleTimeout + s.MaxHeaderBytes = c.MaxHeaderBytes + s.TLSNextProto = c.TLSNextProto + s.ConnState = c.ConnState +} + // Config contains configurations of gRPC and Gateway server. type Config struct { GrpcAddr *Address @@ -72,6 +101,7 @@ type Config struct { GrpcServerOption []grpc.ServerOption GatewayDialOption []grpc.DialOption GatewayMuxOptions []runtime.ServeMuxOption + GatewayServerConfig *HTTPServerConfig MaxConcurrentStreams uint32 GatewayServerMiddlewares []HTTPServerMiddleware } diff --git a/pkg/grapiserver/engine.go b/pkg/grapiserver/engine.go index 6d6caa60..5e47e059 100644 --- a/pkg/grapiserver/engine.go +++ b/pkg/grapiserver/engine.go @@ -1,23 +1,18 @@ package grapiserver import ( + "context" "net" - "os" - "os/signal" "reflect" - "sync" - "syscall" "github.com/izumin5210/grapi/pkg/grapiserver/internal" "github.com/pkg/errors" - "github.com/soheilhy/cmux" - "google.golang.org/grpc/grpclog" + "golang.org/x/sync/errgroup" ) // Engine is the framework instance. type Engine struct { *Config - sdCh chan os.Signal } // New creates a server intstance. @@ -28,11 +23,12 @@ func New(opts ...Option) *Engine { } // Serve starts gRPC and Gateway servers. -func (e *Engine) Serve() error { +func (e *Engine) Serve(ctx context.Context) error { var ( - grpcServer, gatewayServer, muxServer internal.Server - grpcLis, gatewayLis, internalLis net.Listener - err error + grpcServer, gatewayServer internal.Server + grpcLis, gatewayLis, internalLis net.Listener + cmuxServer *cmuxServer + err error ) if e.GrpcAddr != nil && e.GatewayAddr != nil && reflect.DeepEqual(e.GrpcAddr, e.GatewayAddr) { @@ -40,14 +36,14 @@ func (e *Engine) Serve() error { if err != nil { return errors.Wrap(err, "failed to listen network for servers") } - mux := cmux.New(lis) - muxServer = NewMuxServer(mux, lis) - grpcLis = mux.Match(cmux.HTTP2HeaderField("content-type", "application/grpc")) - gatewayLis = mux.Match(cmux.HTTP2(), cmux.HTTP1Fast()) + defer lis.Close() + cmuxServer = newCmuxServer(lis) + grpcLis = cmuxServer.GRPCListener() + gatewayLis = cmuxServer.HTTPListener() } // Setup servers - grpcServer = NewGrpcServer(e.Config) + grpcServer = newGRPCServer(e.Config) // Setup listeners if grpcLis == nil && e.GrpcAddr != nil { @@ -59,7 +55,7 @@ func (e *Engine) Serve() error { } if e.GatewayAddr != nil { - gatewayServer = NewGatewayServer(e.Config) + gatewayServer = newGatewayServer(e.Config) internalLis, err = e.GrpcInternalAddr.createListener() if err != nil { return errors.Wrap(err, "failed to listen network for gRPC server internal") @@ -76,52 +72,22 @@ func (e *Engine) Serve() error { } // Start servers - var wg sync.WaitGroup + ctx, cancel := context.WithCancel(ctx) + defer cancel() + eg, ctx := errgroup.WithContext(ctx) if internalLis != nil { - wg.Add(1) - grpclog.Infof("gRPC server is starting %s://%s", e.GrpcInternalAddr.Network, e.GrpcInternalAddr.Addr) - go grpcServer.Serve(internalLis, &wg) + eg.Go(func() error { return grpcServer.Serve(ctx, internalLis) }) } if grpcLis != nil { - wg.Add(1) - grpclog.Infof("gRPC server is starting %s://%s", e.GrpcAddr.Network, e.GrpcAddr.Addr) - go grpcServer.Serve(grpcLis, &wg) + eg.Go(func() error { return grpcServer.Serve(ctx, grpcLis) }) } if gatewayLis != nil { - wg.Add(1) - grpclog.Infof("gRPC Gateway server is starting: %s://%s", e.GatewayAddr.Network, e.GatewayAddr.Addr) - go gatewayServer.Serve(gatewayLis, &wg) + eg.Go(func() error { return gatewayServer.Serve(ctx, gatewayLis) }) } - if muxServer != nil { - wg.Add(1) - go muxServer.Serve(nil, &wg) + if cmuxServer != nil { + eg.Go(func() error { cmuxServer.Serve(); return nil }) } - wg.Add(1) - go e.watchShutdownSignal(&wg, gatewayServer, grpcServer, muxServer) - - wg.Wait() - - return nil -} - -// Shutdown closes servers. -func (e *Engine) Shutdown() { - e.sdCh <- os.Interrupt -} - -func (e *Engine) watchShutdownSignal(wg *sync.WaitGroup, servers ...internal.Server) { - defer wg.Done() - e.sdCh = make(chan os.Signal, 1) - defer close(e.sdCh) - defer signal.Reset() - signal.Notify(e.sdCh, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) - sig := <-e.sdCh - grpclog.Infof("terminating now...: %v", sig) - for _, svr := range servers { - if svr != nil { - svr.Shutdown() - } - } + return errors.WithStack(eg.Wait()) } diff --git a/pkg/grapiserver/gateway.go b/pkg/grapiserver/gateway.go index 0e5068c9..ea2170a5 100644 --- a/pkg/grapiserver/gateway.go +++ b/pkg/grapiserver/gateway.go @@ -4,7 +4,6 @@ import ( "context" "net" "net/http" - "sync" "time" "github.com/grpc-ecosystem/grpc-gateway/runtime" @@ -14,52 +13,63 @@ import ( "google.golang.org/grpc/grpclog" ) -// NewGatewayServer creates GrpcServer instance. -func NewGatewayServer(c *Config) internal.Server { - return &GatewayServer{ +func newGatewayServer(c *Config) internal.Server { + return &gatewayServer{ Config: c, } } -// GatewayServer wraps gRPC gateway server setup process. -type GatewayServer struct { +// gatewayServer wraps gRPC gateway server setup process. +type gatewayServer struct { server *http.Server *Config } -// Serve implements Server.Shutdown -func (s *GatewayServer) Serve(l net.Listener, wg *sync.WaitGroup) { - defer wg.Done() - +// Serve implements Server.Server +func (s *gatewayServer) Serve(ctx context.Context, l net.Listener) error { conn, err := s.createConn() if err != nil { - grpclog.Errorf("failed to create connection with gRPC server: %v", err) - return + return errors.Wrap(err, "failed to create connection with grpc-gateway server") } defer conn.Close() s.server, err = s.createServer(conn) if err != nil { - grpclog.Errorf("failed to create gRPC Gateway server: %v", err) - return + return errors.Wrap(err, "failed to create gRPC Gateway server: %v") } + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + go func() { + <-ctx.Done() + s.shutdown() + }() + + grpclog.Infof("grpc-gateway server is starting %s", l.Addr()) + err = s.server.Serve(l) - grpclog.Infof("stopped taking more httr(s) requests: %v", err) + + grpclog.Infof("stopped taking more http(s) requests: %v", err) + + if err != http.ErrServerClosed { + return errors.Wrap(err, "failed to serve grpc-gateway server") + } + + return nil } -// Shutdown implements Server.Shutdown -func (s *GatewayServer) Shutdown() { +func (s *gatewayServer) shutdown() { ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) defer cancel() err := s.server.Shutdown(ctx) - grpclog.Info("All http(s) requets finished") + grpclog.Info("All http(s) requests finished") if err != nil { - grpclog.Errorf("failed to shutdown gRPC Gateway server: %v", err) + grpclog.Errorf("failed to shutdown grpc-gateway server: %v", err) } } -func (s *GatewayServer) createConn() (conn *grpc.ClientConn, err error) { +func (s *gatewayServer) createConn() (conn *grpc.ClientConn, err error) { conn, err = grpc.Dial(s.GrpcInternalAddr.Addr, s.clientOptions()...) if err != nil { err = errors.Wrap(err, "failed to connect to gRPC server") @@ -67,7 +77,7 @@ func (s *GatewayServer) createConn() (conn *grpc.ClientConn, err error) { return } -func (s *GatewayServer) createServer(conn *grpc.ClientConn) (*http.Server, error) { +func (s *gatewayServer) createServer(conn *grpc.ClientConn) (*http.Server, error) { mux := runtime.NewServeMux( append( []runtime.ServeMuxOption{runtime.WithProtoErrorHandler(runtime.DefaultHTTPProtoErrorHandler)}, @@ -90,10 +100,12 @@ func (s *GatewayServer) createServer(conn *grpc.ClientConn) (*http.Server, error handler = (s.GatewayServerMiddlewares[i])(handler) } - return &http.Server{ - ReadTimeout: 8 * time.Second, - WriteTimeout: 8 * time.Second, - IdleTimeout: 2 * time.Minute, - Handler: handler, - }, nil + svr := &http.Server{ + Handler: handler, + } + if cfg := s.GatewayServerConfig; cfg != nil { + cfg.applyTo(svr) + } + + return svr, nil } diff --git a/pkg/grapiserver/grpc.go b/pkg/grapiserver/grpc.go index 949dc933..1e7f4a12 100644 --- a/pkg/grapiserver/grpc.go +++ b/pkg/grapiserver/grpc.go @@ -1,44 +1,50 @@ package grapiserver import ( + "context" "net" - "sync" "google.golang.org/grpc" "google.golang.org/grpc/grpclog" "google.golang.org/grpc/reflection" "github.com/izumin5210/grapi/pkg/grapiserver/internal" + "github.com/pkg/errors" ) -// GrpcServer wraps grpc.Server setup process. -type GrpcServer struct { +// grpcServer wraps grpc.Server setup process. +type grpcServer struct { server *grpc.Server *Config } -// NewGrpcServer creates GrpcServer instance. -func NewGrpcServer(c *Config) internal.Server { +func newGRPCServer(c *Config) internal.Server { s := grpc.NewServer(c.serverOptions()...) reflection.Register(s) for _, svr := range c.Servers { svr.RegisterWithServer(s) } - return &GrpcServer{ + return &grpcServer{ server: s, Config: c, } } -// Serve implements Server.Shutdown -func (s *GrpcServer) Serve(l net.Listener, wg *sync.WaitGroup) { - defer wg.Done() +// Serve implements Server.Server +func (s *grpcServer) Serve(ctx context.Context, l net.Listener) error { + ctx, cancel := context.WithCancel(ctx) + defer cancel() + + go func() { + <-ctx.Done() + s.server.GracefulStop() + }() + + grpclog.Infof("gRPC server is starting %s", l.Addr()) err := s.server.Serve(l) - grpclog.Infof("gRPC server stopred: %v", err) -} -// Shutdown implements Server.Shutdown -func (s *GrpcServer) Shutdown() { - s.server.GracefulStop() + grpclog.Infof("gRPC server stopped: %v", err) + + return errors.Wrap(err, "failed to serve gRPC server") } diff --git a/pkg/grapiserver/internal/server.go b/pkg/grapiserver/internal/server.go index ae542341..4cff156f 100644 --- a/pkg/grapiserver/internal/server.go +++ b/pkg/grapiserver/internal/server.go @@ -1,12 +1,11 @@ package internal import ( + "context" "net" - "sync" ) // Server provides an interface for starting and stopping the server. type Server interface { - Serve(l net.Listener, wg *sync.WaitGroup) - Shutdown() + Serve(context.Context, net.Listener) error } diff --git a/testing/main_test.go b/pkg/grapiserver/main_test.go similarity index 68% rename from testing/main_test.go rename to pkg/grapiserver/main_test.go index 147956f0..505cad80 100644 --- a/testing/main_test.go +++ b/pkg/grapiserver/main_test.go @@ -1,4 +1,4 @@ -package testing +package grapiserver_test import ( "context" @@ -7,6 +7,7 @@ import ( "io/ioutil" "net/http" "strconv" + "strings" "sync" "testing" "time" @@ -15,10 +16,40 @@ import ( "google.golang.org/grpc" "github.com/izumin5210/grapi/pkg/grapiserver" - "github.com/izumin5210/grapi/testing/api" - "github.com/izumin5210/grapi/testing/app/server" + api_pb "github.com/izumin5210/grapi/pkg/grapiserver/testing/api" + "github.com/izumin5210/grapi/pkg/grapiserver/testing/app/server" ) +var ( + waitForServer = func() { time.Sleep(1 * time.Second) } +) + +func orDie(t *testing.T, err error) { + t.Helper() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } +} + +func startServer(t *testing.T, s *grapiserver.Engine) func() { + var wg sync.WaitGroup + wg.Add(1) + ctx, cancel := context.WithCancel(context.Background()) + go func() { + defer wg.Done() + if err := s.Serve(ctx); err != nil && + !strings.Contains(err.Error(), "use of closed network connection") && + !strings.Contains(err.Error(), "listener closed") { + t.Errorf("Engine.Serve returned an error: %v", err) + } + }() + waitForServer() + return func() { + cancel() + wg.Wait() + } +} + func Test_server_onlyGateway(t *testing.T) { var port int64 = 15261 s := grapiserver.New( @@ -28,37 +59,21 @@ func Test_server_onlyGateway(t *testing.T) { ), ) - var wg sync.WaitGroup - wg.Add(1) - go func() { - defer wg.Done() - if err := s.Serve(); err != nil { - t.Errorf("Engine.Serve returned an error: %v", err) - } - }() + defer startServer(t, s)() - time.Sleep(5) resp, err := http.Get(fmt.Sprintf("http://localhost:%d/books", port)) - - if err != nil { - t.Fatalf("failed to fetch book resources: %v", err) - } + orDie(t, err) defer resp.Body.Close() if got, want := resp.StatusCode, 200; got != want { - t.Fatalf("Response status is %d, want %d", got, want) + t.Errorf("Response status is %d, want %d", got, want) } data, err := ioutil.ReadAll(resp.Body) - if err != nil { - t.Fatalf("failed to read response body: %v", err) - } + orDie(t, err) got := map[string]interface{}{} - err = json.Unmarshal(data, &got) - if err != nil { - t.Fatalf("failed to parse response body: %v", err) - } + orDie(t, json.Unmarshal(data, &got)) want := map[string]interface{}{ "books": []interface{}{ map[string]interface{}{"book_id": "The Go Programming Language"}, @@ -69,9 +84,6 @@ func Test_server_onlyGateway(t *testing.T) { if diff := cmp.Diff(got, want); diff != "" { t.Errorf("Received body differs: (-got +want)\n%s", diff) } - - s.Shutdown() - wg.Wait() } func Test_server_samePort(t *testing.T) { @@ -84,39 +96,22 @@ func Test_server_samePort(t *testing.T) { ), ) - var wg sync.WaitGroup - wg.Add(1) - go func() { - defer wg.Done() - if err := s.Serve(); err != nil { - t.Errorf("Engine.Serve returned an error: %v", err) - } - }() - - time.Sleep(5) + defer startServer(t, s)() t.Run("http", func(t *testing.T) { resp, err := http.Get(fmt.Sprintf("http://localhost:%d/books", port)) - - if err != nil { - t.Fatalf("failed to fetch book resources: %v", err) - } + orDie(t, err) defer resp.Body.Close() if got, want := resp.StatusCode, 200; got != want { - t.Fatalf("Response status is %d, want %d", got, want) + t.Errorf("Response status is %d, want %d", got, want) } data, err := ioutil.ReadAll(resp.Body) - if err != nil { - t.Fatalf("failed to read response body: %v", err) - } + orDie(t, err) got := map[string]interface{}{} - err = json.Unmarshal(data, &got) - if err != nil { - t.Fatalf("failed to parse response body: %v", err) - } + orDie(t, json.Unmarshal(data, &got)) want := map[string]interface{}{ "books": []interface{}{ map[string]interface{}{"book_id": "The Go Programming Language"}, @@ -131,17 +126,12 @@ func Test_server_samePort(t *testing.T) { t.Run("gRPC", func(t *testing.T) { conn, err := grpc.Dial(addr, grpc.WithInsecure()) - if err != nil { - t.Fatalf("failed to connect with gRPC server: %v", err) - } + orDie(t, err) defer conn.Close() cli := api_pb.NewLibraryServiceClient(conn) resp, err := cli.ListBooks(context.Background(), &api_pb.ListBooksRequest{}) - - if err != nil { - t.Fatalf("failed to fetch book resources: %v", err) - } + orDie(t, err) want := &api_pb.ListBooksResponse{ Books: []*api_pb.Book{ @@ -153,9 +143,6 @@ func Test_server_samePort(t *testing.T) { t.Errorf("Received body differs: (-got +want)\n%s", diff) } }) - - s.Shutdown() - wg.Wait() } func Test_server_differentPort(t *testing.T) { @@ -175,39 +162,22 @@ func Test_server_differentPort(t *testing.T) { ), ) - var wg sync.WaitGroup - wg.Add(1) - go func() { - defer wg.Done() - if err := s.Serve(); err != nil { - t.Errorf("Engine.Serve returned an error: %v", err) - } - }() - - time.Sleep(5) + defer startServer(t, s)() t.Run("http", func(t *testing.T) { resp, err := http.Get(fmt.Sprintf("http://localhost:%d/books", httpPort)) - - if err != nil { - t.Fatalf("failed to fetch book resources: %v", err) - } + orDie(t, err) defer resp.Body.Close() if got, want := resp.StatusCode, 200; got != want { - t.Fatalf("Response status is %d, want %d", got, want) + t.Errorf("Response status is %d, want %d", got, want) } data, err := ioutil.ReadAll(resp.Body) - if err != nil { - t.Fatalf("failed to read response body: %v", err) - } + orDie(t, err) got := map[string]interface{}{} - err = json.Unmarshal(data, &got) - if err != nil { - t.Fatalf("failed to parse response body: %v", err) - } + orDie(t, json.Unmarshal(data, &got)) want := map[string]interface{}{ "books": []interface{}{ map[string]interface{}{"book_id": "The Go Programming Language"}, @@ -222,17 +192,12 @@ func Test_server_differentPort(t *testing.T) { t.Run("gRPC", func(t *testing.T) { conn, err := grpc.Dial(grpcAddr, grpc.WithInsecure()) - if err != nil { - t.Fatalf("failed to connect with gRPC server: %v", err) - } + orDie(t, err) defer conn.Close() cli := api_pb.NewLibraryServiceClient(conn) resp, err := cli.ListBooks(context.Background(), &api_pb.ListBooksRequest{}) - - if err != nil { - t.Fatalf("failed to fetch book resources: %v", err) - } + orDie(t, err) want := &api_pb.ListBooksResponse{ Books: []*api_pb.Book{ @@ -244,7 +209,4 @@ func Test_server_differentPort(t *testing.T) { t.Errorf("Received body differs: (-got +want)\n%s", diff) } }) - - s.Shutdown() - wg.Wait() } diff --git a/pkg/grapiserver/mux.go b/pkg/grapiserver/mux.go deleted file mode 100644 index 5ee12b41..00000000 --- a/pkg/grapiserver/mux.go +++ /dev/null @@ -1,40 +0,0 @@ -package grapiserver - -import ( - "net" - "sync" - - "github.com/izumin5210/grapi/pkg/grapiserver/internal" - "github.com/soheilhy/cmux" - "google.golang.org/grpc/grpclog" -) - -// MuxServer wraps a connection multiplexer and a listener. -type MuxServer struct { - mux cmux.CMux - lis net.Listener -} - -// NewMuxServer creates MuxServer instance. -func NewMuxServer(mux cmux.CMux, lis net.Listener) internal.Server { - return &MuxServer{ - mux: mux, - lis: lis, - } -} - -// Serve implements Server.Serve -func (s *MuxServer) Serve(lis net.Listener, wg *sync.WaitGroup) { - defer wg.Done() - grpclog.Info("mux is starting") - err := s.mux.Serve() - grpclog.Infof("mux is closed: %v", err) -} - -// Shutdown implements Server.Shutdown -func (s *MuxServer) Shutdown() { - err := s.lis.Close() - if err != nil { - grpclog.Errorf("failed to close cmux's listener: %v", err) - } -} diff --git a/pkg/grapiserver/options.go b/pkg/grapiserver/options.go index 7185a942..78b89f09 100644 --- a/pkg/grapiserver/options.go +++ b/pkg/grapiserver/options.go @@ -120,6 +120,13 @@ func WithGatewayServerMiddlewares(middlewares ...HTTPServerMiddleware) Option { } } +// WithGatewayServerConfig returns an Option that specifies http.Server configuration to a gateway server. +func WithGatewayServerConfig(cfg *HTTPServerConfig) Option { + return func(c *Config) { + c.GatewayServerConfig = cfg + } +} + // WithPassedHeader returns an Option that sets configurations about passed headers for a gateway server. func WithPassedHeader(decider PassedHeaderDeciderFunc) Option { return WithGatewayServerMiddlewares(createPassingHeaderMiddleware(decider)) diff --git a/testing/api/library.pb.go b/pkg/grapiserver/testing/api/library.pb.go similarity index 100% rename from testing/api/library.pb.go rename to pkg/grapiserver/testing/api/library.pb.go diff --git a/testing/api/library.pb.gw.go b/pkg/grapiserver/testing/api/library.pb.gw.go similarity index 100% rename from testing/api/library.pb.gw.go rename to pkg/grapiserver/testing/api/library.pb.gw.go diff --git a/testing/api/library.swagger.json b/pkg/grapiserver/testing/api/library.swagger.json similarity index 100% rename from testing/api/library.swagger.json rename to pkg/grapiserver/testing/api/library.swagger.json diff --git a/pkg/grapiserver/testing/api/protos/.keep b/pkg/grapiserver/testing/api/protos/.keep new file mode 100644 index 00000000..e69de29b diff --git a/testing/api/protos/library.proto b/pkg/grapiserver/testing/api/protos/library.proto similarity index 100% rename from testing/api/protos/library.proto rename to pkg/grapiserver/testing/api/protos/library.proto diff --git a/pkg/grapiserver/testing/app/server/.keep b/pkg/grapiserver/testing/app/server/.keep new file mode 100644 index 00000000..e69de29b diff --git a/testing/app/server/library_server.go b/pkg/grapiserver/testing/app/server/library_server.go similarity index 96% rename from testing/app/server/library_server.go rename to pkg/grapiserver/testing/app/server/library_server.go index 703f3c4f..e732f5ee 100644 --- a/testing/app/server/library_server.go +++ b/pkg/grapiserver/testing/app/server/library_server.go @@ -8,7 +8,7 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - api_pb "github.com/izumin5210/grapi/testing/api" + api_pb "github.com/izumin5210/grapi/pkg/grapiserver/testing/api" ) // NewLibraryServiceServer creates a new LibraryServiceServer instance. diff --git a/testing/app/server/library_server_register_funcs.go b/pkg/grapiserver/testing/app/server/library_server_register_funcs.go similarity index 89% rename from testing/app/server/library_server_register_funcs.go rename to pkg/grapiserver/testing/app/server/library_server_register_funcs.go index 9ef30678..637016fc 100644 --- a/testing/app/server/library_server_register_funcs.go +++ b/pkg/grapiserver/testing/app/server/library_server_register_funcs.go @@ -6,7 +6,7 @@ import ( "github.com/grpc-ecosystem/grpc-gateway/runtime" "google.golang.org/grpc" - api_pb "github.com/izumin5210/grapi/testing/api" + api_pb "github.com/izumin5210/grapi/pkg/grapiserver/testing/api" ) // RegisterWithServer implements grapiserver.Server.RegisterWithServer. diff --git a/pkg/protoc/.snapshots/TestWrapper_Exec b/pkg/protoc/.snapshots/TestWrapper_Exec new file mode 100644 index 00000000..6b89d87a --- /dev/null +++ b/pkg/protoc/.snapshots/TestWrapper_Exec @@ -0,0 +1,80 @@ +([][]string) (len=6) { + ([]string) (len=11) { + (string) (len=6) "protoc", + (string) (len=2) "-I", + (string) (len=10) "api/protos", + (string) (len=2) "-I", + (string) (len=57) "/go/pkg/mod/github.com/grpc-ecosystem/grpc-gateway@v1.8.5", + (string) (len=2) "-I", + (string) (len=80) "/go/pkg/mod/github.com/grpc-ecosystem/grpc-gateway@v1.8.5/third_party/googleapis", + (string) (len=2) "-I", + (string) (len=29) "/go/src/awesomeapp/api/protos", + (string) (len=25) "--go_out=plugins=grpc:api", + (string) (len=21) "api/protos/book.proto" + }, + ([]string) (len=11) { + (string) (len=6) "protoc", + (string) (len=2) "-I", + (string) (len=10) "api/protos", + (string) (len=2) "-I", + (string) (len=57) "/go/pkg/mod/github.com/grpc-ecosystem/grpc-gateway@v1.8.5", + (string) (len=2) "-I", + (string) (len=80) "/go/pkg/mod/github.com/grpc-ecosystem/grpc-gateway@v1.8.5/third_party/googleapis", + (string) (len=2) "-I", + (string) (len=29) "/go/src/awesomeapp/api/protos", + (string) (len=39) "--grpc-gateway_out=logtostderr=true:api", + (string) (len=21) "api/protos/book.proto" + }, + ([]string) (len=11) { + (string) (len=6) "protoc", + (string) (len=2) "-I", + (string) (len=10) "api/protos", + (string) (len=2) "-I", + (string) (len=57) "/go/pkg/mod/github.com/grpc-ecosystem/grpc-gateway@v1.8.5", + (string) (len=2) "-I", + (string) (len=80) "/go/pkg/mod/github.com/grpc-ecosystem/grpc-gateway@v1.8.5/third_party/googleapis", + (string) (len=2) "-I", + (string) (len=29) "/go/src/awesomeapp/api/protos", + (string) (len=34) "--swagger_out=logtostderr=true:api", + (string) (len=21) "api/protos/book.proto" + }, + ([]string) (len=11) { + (string) (len=6) "protoc", + (string) (len=2) "-I", + (string) (len=16) "api/protos/types", + (string) (len=2) "-I", + (string) (len=57) "/go/pkg/mod/github.com/grpc-ecosystem/grpc-gateway@v1.8.5", + (string) (len=2) "-I", + (string) (len=80) "/go/pkg/mod/github.com/grpc-ecosystem/grpc-gateway@v1.8.5/third_party/googleapis", + (string) (len=2) "-I", + (string) (len=29) "/go/src/awesomeapp/api/protos", + (string) (len=31) "--go_out=plugins=grpc:api/types", + (string) (len=28) "api/protos/types/users.proto" + }, + ([]string) (len=11) { + (string) (len=6) "protoc", + (string) (len=2) "-I", + (string) (len=16) "api/protos/types", + (string) (len=2) "-I", + (string) (len=57) "/go/pkg/mod/github.com/grpc-ecosystem/grpc-gateway@v1.8.5", + (string) (len=2) "-I", + (string) (len=80) "/go/pkg/mod/github.com/grpc-ecosystem/grpc-gateway@v1.8.5/third_party/googleapis", + (string) (len=2) "-I", + (string) (len=29) "/go/src/awesomeapp/api/protos", + (string) (len=45) "--grpc-gateway_out=logtostderr=true:api/types", + (string) (len=28) "api/protos/types/users.proto" + }, + ([]string) (len=11) { + (string) (len=6) "protoc", + (string) (len=2) "-I", + (string) (len=16) "api/protos/types", + (string) (len=2) "-I", + (string) (len=57) "/go/pkg/mod/github.com/grpc-ecosystem/grpc-gateway@v1.8.5", + (string) (len=2) "-I", + (string) (len=80) "/go/pkg/mod/github.com/grpc-ecosystem/grpc-gateway@v1.8.5/third_party/googleapis", + (string) (len=2) "-I", + (string) (len=29) "/go/src/awesomeapp/api/protos", + (string) (len=40) "--swagger_out=logtostderr=true:api/types", + (string) (len=28) "api/protos/types/users.proto" + } +} diff --git a/pkg/grapicmd/protoc/config.go b/pkg/protoc/config.go similarity index 66% rename from pkg/grapicmd/protoc/config.go rename to pkg/protoc/config.go index f7647e31..3b18fc5b 100644 --- a/pkg/grapicmd/protoc/config.go +++ b/pkg/protoc/config.go @@ -46,27 +46,3 @@ func (c *Config) OutDirOf(rootDir string, protoPath string) (string, error) { return filepath.Join(c.OutDir, relProtoDir), nil } - -// Commands returns protoc command and arguments for given proto file. -func (c *Config) Commands(rootDir, protoPath string) ([][]string, error) { - cmds := make([][]string, 0, len(c.Plugins)) - relProtoPath, _ := filepath.Rel(rootDir, protoPath) - - outDir, err := c.OutDirOf(rootDir, protoPath) - if err != nil { - return nil, errors.WithStack(err) - } - - for _, p := range c.Plugins { - args := []string{} - args = append(args, "-I", filepath.Dir(relProtoPath)) - for _, dir := range c.ImportDirs { - args = append(args, "-I", dir) - } - args = append(args, p.toProtocArg(outDir)) - args = append(args, relProtoPath) - cmds = append(cmds, append([]string{"protoc"}, args...)) - } - - return cmds, nil -} diff --git a/pkg/grapicmd/protoc/config_test.go b/pkg/protoc/config_test.go similarity index 55% rename from pkg/grapicmd/protoc/config_test.go rename to pkg/protoc/config_test.go index 44537342..c10159ba 100644 --- a/pkg/grapicmd/protoc/config_test.go +++ b/pkg/protoc/config_test.go @@ -2,7 +2,6 @@ package protoc import ( "path/filepath" - "reflect" "testing" ) @@ -16,17 +15,14 @@ func createDefaultConfig() *Config { OutDir: "./api", Plugins: []*Plugin{ { - Path: "./vendor/github.com/golang/protobuf/protoc-gen-go", Name: "go", Args: map[string]interface{}{"plugins": "grpc"}, }, { - Path: "./vendor/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway", Name: "grpc-gateway", Args: map[string]interface{}{"logtostderr": true}, }, { - Path: "./vendor/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger", Name: "swagger", Args: map[string]interface{}{"logtostderr": true}, }, @@ -62,30 +58,3 @@ func Test_Config_OutDirOf(t *testing.T) { } }) } - -func Test_Config_Commands(t *testing.T) { - cfg := createDefaultConfig() - rootDir := "/home/example/app" - protoPath := "api/protos/foo/bar.proto" - - cmds, err := cfg.Commands(rootDir, filepath.Join(rootDir, protoPath)) - - if err != nil { - t.Errorf("Commands() returned an error %v", err) - } - - wantCmd := []string{ - "protoc", - "-I", "api/protos/foo", - "-I", "./vendor/github.com/grpc-ecosystem/grpc-gateway", - "-I", "./vendor/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis", - "--go_out=plugins=grpc:api/foo", - "api/protos/foo/bar.proto", - } - - if got, want := len(cmds), len(cfg.Plugins); got != want { - t.Errorf("Commands() returned %d commands, want %d commands", got, want) - } else if got, want := cmds[0], wantCmd; !reflect.DeepEqual(got, want) { - t.Errorf("Commands()[0] returned %v, want %v", got, want) - } -} diff --git a/pkg/grapicmd/protoc/plugin.go b/pkg/protoc/plugin.go similarity index 91% rename from pkg/grapicmd/protoc/plugin.go rename to pkg/protoc/plugin.go index 6fb573f7..753c3e63 100644 --- a/pkg/grapicmd/protoc/plugin.go +++ b/pkg/protoc/plugin.go @@ -3,20 +3,18 @@ package protoc import ( "bytes" "fmt" - "path/filepath" "strings" ) // Plugin contains args and plugin name for using in protoc command. type Plugin struct { - Path string Name string Args map[string]interface{} } // BinName returns a executable binary name. func (p *Plugin) BinName() string { - return filepath.Base(p.Path) + return "protoc-gen-" + p.Name } func (p *Plugin) toProtocArg(outputPath string) string { diff --git a/pkg/protoc/providers.go b/pkg/protoc/providers.go new file mode 100644 index 00000000..42442349 --- /dev/null +++ b/pkg/protoc/providers.go @@ -0,0 +1,67 @@ +package protoc + +import ( + "sync" + + "github.com/google/wire" + "github.com/izumin5210/clig/pkg/clib" + "github.com/izumin5210/execx" + "github.com/izumin5210/gex" + "github.com/izumin5210/gex/pkg/tool" + "github.com/pkg/errors" + "github.com/spf13/afero" + "go.uber.org/zap" + + "github.com/izumin5210/grapi/pkg/cli" +) + +var ( + gexCfg *gex.Config + gexCfgMu sync.Mutex + + toolRepo tool.Repository + toolRepoMu sync.Mutex +) + +func ProvideGexConfig( + fs afero.Fs, + exec *execx.Executor, + io *clib.IO, + rootDir cli.RootDir, +) *gex.Config { + gexCfgMu.Lock() + defer gexCfgMu.Unlock() + if gexCfg == nil { + gexCfg = &gex.Config{ + OutWriter: io.Out, + ErrWriter: io.Err, + InReader: io.In, + FS: fs, + Exec: exec, + WorkingDir: rootDir.String(), + Verbose: clib.IsVerbose() || clib.IsDebug(), + Logger: zap.NewStdLog(zap.L()), + } + } + return gexCfg +} + +func ProvideToolRepository(gexCfg *gex.Config) (tool.Repository, error) { + toolRepoMu.Lock() + defer toolRepoMu.Unlock() + if toolRepo == nil { + var err error + toolRepo, err = gexCfg.Create() + if err != nil { + return nil, errors.WithStack(err) + } + } + return toolRepo, nil +} + +// WrapperSet is a provider set that includes gex things and Wrapper instance. +var WrapperSet = wire.NewSet( + ProvideGexConfig, + ProvideToolRepository, + NewWrapper, +) diff --git a/pkg/protoc/wrapper.go b/pkg/protoc/wrapper.go new file mode 100644 index 00000000..f0faf2cf --- /dev/null +++ b/pkg/protoc/wrapper.go @@ -0,0 +1,195 @@ +package protoc + +import ( + "bytes" + "context" + "os" + "path/filepath" + "strings" + "sync" + "text/template" + + "github.com/izumin5210/execx" + "github.com/izumin5210/gex/pkg/tool" + "github.com/pkg/errors" + "github.com/spf13/afero" + "go.uber.org/zap" + + "github.com/izumin5210/grapi/pkg/cli" + "github.com/izumin5210/grapi/pkg/grapicmd/util/fs" +) + +// Wrapper can execute protoc commands for current project's proto files. +type Wrapper interface { + Exec(context.Context) error +} + +type wrapperImpl struct { + cfg *Config + fs afero.Fs + ui cli.UI + exec *execx.Executor + toolRepo tool.Repository + rootDir cli.RootDir + modCache *sync.Map +} + +// NewWrapper creates a new Wrapper instance. +func NewWrapper(cfg *Config, fs afero.Fs, exec *execx.Executor, ui cli.UI, toolRepo tool.Repository, rootDir cli.RootDir) Wrapper { + return &wrapperImpl{ + cfg: cfg, + fs: fs, + ui: ui, + exec: exec, + toolRepo: toolRepo, + rootDir: rootDir, + modCache: new(sync.Map), + } +} + +func (e *wrapperImpl) Exec(ctx context.Context) (err error) { + e.ui.Section("Execute protoc") + + e.ui.Subsection("Install plugins") + err = errors.WithStack(e.installPlugins(ctx)) + if err != nil { + return + } + + e.ui.Subsection("Execute protoc") + err = errors.WithStack(e.execProtocAll(ctx)) + + return +} + +func (e *wrapperImpl) installPlugins(ctx context.Context) error { + return errors.WithStack(e.toolRepo.BuildAll(ctx)) +} + +func (e *wrapperImpl) execProtocAll(ctx context.Context) error { + protoFiles, err := e.cfg.ProtoFiles(e.fs, e.rootDir.String()) + if err != nil { + return errors.WithStack(err) + } + + var errs []error + for _, path := range protoFiles { + err = e.execProtoc(ctx, path) + relPath, _ := filepath.Rel(e.rootDir.String(), path) + if err == nil { + e.ui.ItemSuccess(relPath) + } else { + zap.L().Error("failed to execute protoc", zap.Error(err)) + errs = append(errs, err) + e.ui.ItemFailure(relPath, err) + } + } + + if len(errs) > 0 { + return errors.New("failed to execute protoc") + } + + return nil +} + +func (e *wrapperImpl) execProtoc(ctx context.Context, protoPath string) error { + outDir, err := e.cfg.OutDirOf(e.rootDir.String(), protoPath) + if err != nil { + return errors.WithStack(err) + } + + if err = fs.CreateDirIfNotExists(e.fs, outDir); err != nil { + return errors.WithStack(err) + } + + cmds, err := e.commands(ctx, protoPath) + if err != nil { + return errors.WithStack(err) + } + + path := e.rootDir.BinDir().String() + string(filepath.ListSeparator) + os.Getenv("PATH") + env := append(os.Environ(), "PATH="+path) + + for _, args := range cmds { + cmd := e.exec.CommandContext(ctx, args[0], args[1:]...) + cmd.Env = env + cmd.Dir = e.rootDir.String() + out, err := cmd.CombinedOutput() + if err != nil { + return errors.Wrapf(err, "failed to execute command: %v\n%s", args, string(out)) + } + } + + return nil +} + +func (e *wrapperImpl) commands(ctx context.Context, protoPath string) ([][]string, error) { + cmds := make([][]string, 0, len(e.cfg.Plugins)) + relProtoPath, _ := filepath.Rel(e.rootDir.String(), protoPath) + + outDir, err := e.cfg.OutDirOf(e.rootDir.String(), protoPath) + if err != nil { + return nil, errors.WithStack(err) + } + + isMod := e.modulesEnabled() + + funcMap := template.FuncMap{ + "module": func(in string) (string, error) { + if isMod { + return e.getModulePath(ctx, in) + } + return e.rootDir.Join("vendor", in).String(), nil + }, + } + + for _, p := range e.cfg.Plugins { + args := []string{} + args = append(args, "-I", filepath.Dir(relProtoPath)) + for _, dir := range e.cfg.ImportDirs { + tmpl, err := template.New(dir).Funcs(funcMap).Parse(dir) + if err != nil { + return nil, errors.WithStack(err) + } + buf := new(bytes.Buffer) + err = tmpl.Funcs(funcMap).Execute(buf, nil) + if err != nil { + return nil, errors.WithStack(err) + } + args = append(args, "-I", buf.String()) + } + args = append(args, p.toProtocArg(outDir)) + args = append(args, relProtoPath) + cmds = append(cmds, append([]string{"protoc"}, args...)) + } + + return cmds, nil +} + +func (e *wrapperImpl) getModulePath(ctx context.Context, pkg string) (string, error) { + if v, ok := e.modCache.Load(pkg); ok { + return v.(string), nil + } + buf := new(bytes.Buffer) + cmd := e.exec.CommandContext(ctx, "go", "list", "-f", "{{.Dir}}", "-m", pkg) + cmd.Env = append(os.Environ(), "GO111MODULE=on") + cmd.Dir = e.rootDir.String() + cmd.Stdout = buf + err := cmd.Run() + if err != nil { + return "", errors.WithStack(err) + } + out := strings.TrimSpace(buf.String()) + e.modCache.Store(pkg, out) + + return out, nil +} + +func (e *wrapperImpl) modulesEnabled() bool { + for _, f := range []string{"go.mod", "go.sum"} { + if ok, err := afero.Exists(e.fs, e.rootDir.Join(f).String()); err != nil || !ok { + return false + } + } + return true +} diff --git a/pkg/protoc/wrapper_test.go b/pkg/protoc/wrapper_test.go new file mode 100644 index 00000000..417f0d5b --- /dev/null +++ b/pkg/protoc/wrapper_test.go @@ -0,0 +1,91 @@ +package protoc_test + +import ( + "context" + "os/exec" + "testing" + + "github.com/bradleyjkemp/cupaloy/v2" + "github.com/izumin5210/clig/pkg/clib" + "github.com/izumin5210/execx" + "github.com/izumin5210/gex/pkg/tool" + "github.com/spf13/afero" + + "github.com/izumin5210/grapi/pkg/cli" + "github.com/izumin5210/grapi/pkg/protoc" +) + +func TestWrapper_Exec(t *testing.T) { + dieIf := func(t *testing.T, err error) { + t.Helper() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + } + + calls := [][]string{} + + exec := execx.New(execx.WithFakeProcess( + func(ctx context.Context, cmd *exec.Cmd) error { + switch cmd.Args[0] { + case "go": + if out := cmd.Stdout; out != nil { + _, err := out.Write([]byte("/go/pkg/mod/github.com/grpc-ecosystem/grpc-gateway@v1.8.5\n")) + dieIf(t, err) + } + case "protoc": + calls = append(calls, cmd.Args) + default: + t.Errorf("unexpected command execution: %v", cmd.Args) + } + if out := cmd.Stdout; out != nil { + _, err := out.Write([]byte("\n")) + dieIf(t, err) + } + return nil + }, + )) + + rootDir := cli.RootDir{Path: clib.Path("/go/src/awesomeapp")} + protosDir := rootDir.Join("api", "protos") + + fs := afero.NewMemMapFs() + dieIf(t, fs.MkdirAll(rootDir.BinDir().String(), 0755)) + dieIf(t, fs.MkdirAll(protosDir.String(), 0755)) + dieIf(t, afero.WriteFile(fs, rootDir.Join("go.mod").String(), []byte("module example.com/awesomeapp"), 0644)) + dieIf(t, afero.WriteFile(fs, rootDir.Join("go.sum").String(), []byte(""), 0644)) + dieIf(t, afero.WriteFile(fs, rootDir.Join("api", "should_be_ignored.proto").String(), []byte{}, 0644)) + dieIf(t, afero.WriteFile(fs, rootDir.Join("api", "should_be_ignored_proto").String(), []byte{}, 0644)) + dieIf(t, afero.WriteFile(fs, protosDir.Join("book.proto").String(), []byte{}, 0644)) + dieIf(t, afero.WriteFile(fs, protosDir.Join("types", "users.proto").String(), []byte{}, 0644)) + + cfg := &protoc.Config{ + ImportDirs: []string{ + `{{ module "github.com/grpc-ecosystem/grpc-gateway" }}`, + `{{ module "github.com/grpc-ecosystem/grpc-gateway" }}/third_party/googleapis`, + protosDir.String(), + }, + ProtosDir: "./api/protos", + OutDir: "./api", + Plugins: []*protoc.Plugin{ + {Name: "go", Args: map[string]interface{}{"plugins": "grpc"}}, + {Name: "grpc-gateway", Args: map[string]interface{}{"logtostderr": true}}, + {Name: "swagger", Args: map[string]interface{}{"logtostderr": true}}, + }, + } + + wrapper := protoc.NewWrapper(cfg, fs, exec, cli.NopUI, &fakeToolRepository{}, rootDir) + + err := wrapper.Exec(context.TODO()) + if err != nil { + t.Fatalf("returned %v, want nil", err) + } + + cupaloy.SnapshotT(t, calls) +} + +type fakeToolRepository struct { + tool.Repository +} + +func (*fakeToolRepository) BuildAll(context.Context) error { return nil } diff --git a/pkg/svcgen/app.go b/pkg/svcgen/app.go new file mode 100644 index 00000000..072ece43 --- /dev/null +++ b/pkg/svcgen/app.go @@ -0,0 +1,14 @@ +package svcgen + +import ( + "github.com/izumin5210/grapi/pkg/gencmd" + "github.com/izumin5210/grapi/pkg/protoc" + "github.com/izumin5210/grapi/pkg/svcgen/params" +) + +type CreateAppFunc func(*gencmd.Command) (*App, error) + +type App struct { + ProtocWrapper protoc.Wrapper + ParamsBuilder params.Builder +} diff --git a/pkg/svcgen/params/builder.go b/pkg/svcgen/params/builder.go new file mode 100644 index 00000000..3950e6a5 --- /dev/null +++ b/pkg/svcgen/params/builder.go @@ -0,0 +1,247 @@ +package params + +import ( + "path/filepath" + "sort" + "strings" + + "github.com/pkg/errors" + "github.com/serenize/snaker" + + "github.com/izumin5210/grapi/pkg/cli" + gencmdutil "github.com/izumin5210/grapi/pkg/gencmd/util" +) + +type Builder interface { + Build(path string, resName string, methodNames []string) (*Params, error) +} + +func NewBuilder(rootDir cli.RootDir, protoDir, protoOutDir, serverDir string, pkgName string) Builder { + if protoDir == "" { + protoDir = filepath.Join("api", "protos") + } + if protoOutDir == "" { + protoOutDir = filepath.Join("api") + } + if serverDir == "" { + serverDir = filepath.Join("app", "server") + } + return &builderImpl{ + rootDir: rootDir, + protoDir: protoDir, + protoOutDir: protoOutDir, + serverDir: serverDir, + pkgName: pkgName, + } +} + +type builderImpl struct { + rootDir cli.RootDir + protoDir string + protoOutDir string + serverDir string + pkgName string +} + +func (b *builderImpl) Build(path string, resName string, methodNames []string) (*Params, error) { + protoParams, err := gencmdutil.BuildProtoParams(path, b.rootDir, b.protoOutDir, b.pkgName) + if err != nil { + return nil, errors.WithStack(err) + } + + // path => baz/qux/quux + path = protoParams.Proto.Path + + // quux + name := filepath.Base(path) + + names := gencmdutil.Inflect(name) + + // Quux + serviceName := names.Camel.Singular + // quux + localServiceName := strings.ToLower(string(serviceName[0])) + serviceName[1:] + + // baz/qux + packagePath := filepath.Dir(path) + // qux + packageName := filepath.Base(packagePath) + + if packagePath == "." { + packagePath = filepath.Base(b.serverDir) + packageName = packagePath + } + + protoImports := []string{ + "google/api/annotations.proto", + } + goImports := []string{ + "github.com/izumin5210/grapi/pkg/grapiserver", + "google.golang.org/grpc/codes", + "google.golang.org/grpc/status", + } + + resNames := names + if resName != "" { + resNames = gencmdutil.Inflect(resName) + } + methods := b.buildMethodParams(resNames, methodNames) + + protoImports = append(protoImports, methods.ProtoImports...) + sort.Strings(protoImports) + goImports = append(goImports, methods.GoImports...) + sort.Strings(goImports) + + params := &Params{ + ProtoDir: b.protoDir, + ProtoOutDir: b.protoOutDir, + ServerDir: b.serverDir, + Path: path, + ServiceName: serviceName, + Methods: methods.Methods, + Proto: ProtoParams{ + Package: protoParams.Proto.Package, + Imports: protoImports, + Messages: methods.Messages, + }, + PbGo: PbGoParams{ + PackagePath: protoParams.PbGo.Package, + PackageName: protoParams.PbGo.ImportName, + }, + Go: GoParams{ + Package: packageName, + Imports: goImports, + ServerName: serviceName + "Service" + "Server", + StructName: localServiceName + "Service" + "Server" + "Impl", + }, + } + + return params, nil +} + +func (b *builderImpl) buildMethodParams(name gencmdutil.String, methods []string) ( + params MethodsParams, +) { + id := name.Snake.Singular + "_id" + resource := &MethodMessage{ + Name: name.Camel.Singular, + Fields: []MethodMessageField{{Name: id, Type: "string", Tag: 1}}, + } + + basicMethods := [5]*MethodParams{} + customMethods := []MethodParams{} + basicMessages := [7]*MethodMessage{} + customMessages := []MethodMessage{} + + for _, meth := range methods { + switch strings.ToLower(meth) { + case "list": + methodName := "List" + name.Camel.Plural + reqName := methodName + "Request" + respName := methodName + "Response" + basicMethods[0] = &MethodParams{ + Method: methodName, + requestCommon: reqName, + responseCommon: respName, + HTTP: MethodHTTPParams{Method: "get", Path: name.Snake.Plural}, + } + basicMessages[0] = resource + basicMessages[1] = &MethodMessage{Name: reqName} + basicMessages[2] = &MethodMessage{ + Name: respName, + Fields: []MethodMessageField{{Name: name.Snake.Plural, Type: name.Camel.Singular, Repeated: true, Tag: 1}}, + } + case "get": + methodName := "Get" + name.Camel.Singular + reqName := methodName + "Request" + basicMethods[1] = &MethodParams{ + Method: methodName, + requestCommon: reqName, + responseCommon: resource.Name, + HTTP: MethodHTTPParams{Method: "get", Path: name.Snake.Plural + "/{" + id + "}"}, + } + basicMessages[0] = resource + basicMessages[3] = &MethodMessage{ + Name: reqName, + Fields: []MethodMessageField{{Name: id, Type: "string", Tag: 1}}, + } + case "create": + methodName := "Create" + name.Camel.Singular + reqName := methodName + "Request" + basicMethods[2] = &MethodParams{ + Method: methodName, + requestCommon: reqName, + responseCommon: resource.Name, + HTTP: MethodHTTPParams{Method: "post", Path: name.Snake.Plural, Body: name.Snake.Singular}, + } + basicMessages[0] = resource + basicMessages[4] = &MethodMessage{ + Name: reqName, + Fields: []MethodMessageField{{Name: name.Snake.Singular, Type: name.Camel.Singular, Tag: 1}}, + } + case "update": + methodName := "Update" + name.Camel.Singular + reqName := methodName + "Request" + basicMethods[3] = &MethodParams{ + Method: methodName, + requestCommon: reqName, + responseCommon: resource.Name, + HTTP: MethodHTTPParams{Method: "patch", Path: name.Snake.Plural + "/{" + name.Snake.Singular + "." + id + "}", Body: name.Snake.Singular}, + } + basicMessages[0] = resource + basicMessages[5] = &MethodMessage{ + Name: reqName, + Fields: []MethodMessageField{{Name: name.Snake.Singular, Type: name.Camel.Singular, Tag: 1}}, + } + case "delete": + methodName := "Delete" + name.Camel.Singular + reqName := methodName + "Request" + basicMethods[4] = &MethodParams{ + Method: methodName, + requestCommon: reqName, + responseProto: "google.protobuf.Empty", + responseGo: "empty.Empty", + HTTP: MethodHTTPParams{Method: "delete", Path: name.Snake.Plural + "/{" + id + "}"}, + } + basicMessages[6] = &MethodMessage{ + Name: reqName, + Fields: []MethodMessageField{{Name: id, Type: "string", Tag: 1}}, + } + params.ProtoImports = append(params.ProtoImports, "google/protobuf/empty.proto") + params.GoImports = append(params.GoImports, "github.com/golang/protobuf/ptypes/empty") + default: + methodName := snaker.SnakeToCamel(meth) + reqName := methodName + "Request" + respName := methodName + "Response" + customMethods = append(customMethods, MethodParams{ + Method: methodName, + requestCommon: reqName, + responseCommon: respName, + HTTP: MethodHTTPParams{Method: "get", Path: name.Snake.Plural + "/" + snaker.CamelToSnake(meth)}, + }) + customMessages = append( + customMessages, + MethodMessage{Name: reqName}, + MethodMessage{Name: respName}, + ) + } + } + + for _, meth := range basicMethods { + if meth != nil { + params.Methods = append(params.Methods, *meth) + } + } + for _, msg := range basicMessages { + if msg != nil { + params.Messages = append(params.Messages, *msg) + } + } + for _, meth := range customMethods { + params.Methods = append(params.Methods, meth) + } + for _, msg := range customMessages { + params.Messages = append(params.Messages, msg) + } + return +} diff --git a/pkg/svcgen/params/params.go b/pkg/svcgen/params/params.go new file mode 100644 index 00000000..aeabb026 --- /dev/null +++ b/pkg/svcgen/params/params.go @@ -0,0 +1,96 @@ +package params + +type Params struct { + ProtoDir string + ProtoOutDir string + ServerDir string + Path string + ServiceName string + Methods []MethodParams + Proto ProtoParams + PbGo PbGoParams + Go GoParams +} + +type ProtoParams struct { + Package string + Imports []string + Messages []MethodMessage +} + +type PbGoParams struct { + PackageName string + PackagePath string +} + +type GoParams struct { + Package string + Imports []string + TestImports []string + ServerName string + StructName string +} + +type MethodsParams struct { + Methods []MethodParams + ProtoImports []string + GoImports []string + Messages []MethodMessage +} + +type MethodParams struct { + Method string + HTTP MethodHTTPParams + requestCommon string + requestGo string + requestProto string + responseCommon string + responseGo string + responseProto string +} + +func (p *MethodParams) RequestGo(pkg string) string { + if p.requestGo == "" { + return pkg + "." + p.requestCommon + } + return p.requestGo +} + +func (p *MethodParams) RequestProto() string { + if p.requestProto == "" { + return p.requestCommon + } + return p.requestProto +} + +func (p *MethodParams) ResponseGo(pkg string) string { + if p.responseGo == "" { + return pkg + "." + p.responseCommon + } + return p.responseGo +} + +func (p *MethodParams) ResponseProto() string { + if p.responseProto == "" { + return p.responseCommon + } + return p.responseProto +} + +type MethodMessage struct { + Name string + Fields []MethodMessageField +} + +type MethodMessageField struct { + Name string + Type string + Repeated bool + Tag uint +} + +type MethodHTTPParams struct { + Method string + Path string + Body string +} diff --git a/pkg/svcgen/providers.go b/pkg/svcgen/providers.go new file mode 100644 index 00000000..0e3309fd --- /dev/null +++ b/pkg/svcgen/providers.go @@ -0,0 +1,26 @@ +package svcgen + +import ( + "github.com/google/wire" + + "github.com/izumin5210/grapi/pkg/cli" + "github.com/izumin5210/grapi/pkg/grapicmd" + "github.com/izumin5210/grapi/pkg/protoc" + "github.com/izumin5210/grapi/pkg/svcgen/params" + _ "github.com/izumin5210/grapi/pkg/svcgen/template" +) + +func ProvideParamsBuilder(rootDir cli.RootDir, protocCfg *protoc.Config, grapiCfg *grapicmd.Config) params.Builder { + return params.NewBuilder( + rootDir, + protocCfg.ProtosDir, + protocCfg.OutDir, + grapiCfg.Grapi.ServerDir, + grapiCfg.Package, + ) +} + +var Set = wire.NewSet( + ProvideParamsBuilder, + App{}, +) diff --git a/pkg/svcgen/template/.gitignore b/pkg/svcgen/template/.gitignore new file mode 100644 index 00000000..e69de29b diff --git a/pkg/grapicmd/internal/module/generator/template/service/{{.ProtoDir}}/{{.Path}}.proto.tmpl b/pkg/svcgen/template/_data/{{.ProtoDir}}/{{.Path}}.proto.tmpl similarity index 89% rename from pkg/grapicmd/internal/module/generator/template/service/{{.ProtoDir}}/{{.Path}}.proto.tmpl rename to pkg/svcgen/template/_data/{{.ProtoDir}}/{{.Path}}.proto.tmpl index e8ab09be..9cd6974e 100644 --- a/pkg/grapicmd/internal/module/generator/template/service/{{.ProtoDir}}/{{.Path}}.proto.tmpl +++ b/pkg/svcgen/template/_data/{{.ProtoDir}}/{{.Path}}.proto.tmpl @@ -1,6 +1,9 @@ syntax = "proto3"; -option go_package = "{{ .PbGo.PackageName }}"; + package {{ .Proto.Package }}; + +option go_package = "{{ .PbGo.PackagePath }};{{ .PbGo.PackageName }}"; + {{range .Proto.Imports}} import "{{.}}"; {{- end}} diff --git a/pkg/grapicmd/internal/module/generator/template/service/{{.ServerDir}}/{{.Path}}_server.go.tmpl b/pkg/svcgen/template/_data/{{.ServerDir}}/{{.Path}}_server.go.tmpl similarity index 76% rename from pkg/grapicmd/internal/module/generator/template/service/{{.ServerDir}}/{{.Path}}_server.go.tmpl rename to pkg/svcgen/template/_data/{{.ServerDir}}/{{.Path}}_server.go.tmpl index 308e89b8..8801f816 100644 --- a/pkg/grapicmd/internal/module/generator/template/service/{{.ServerDir}}/{{.Path}}_server.go.tmpl +++ b/pkg/svcgen/template/_data/{{.ServerDir}}/{{.Path}}_server.go.tmpl @@ -9,11 +9,14 @@ import ( {{.PbGo.PackageName}} "{{ .PbGo.PackagePath }}" ) -// New{{.Go.ServerName}} creates a new {{.Go.ServerName}} instance. -func New{{.Go.ServerName}}() interface { +// {{.Go.ServerName}} is a composite interface of {{.PbGo.PackageName }}.{{.Go.ServerName}} and grapiserver.Server. +type {{.Go.ServerName}} interface { {{.PbGo.PackageName }}.{{.Go.ServerName}} grapiserver.Server -} { +} + +// New{{.Go.ServerName}} creates a new {{.Go.ServerName}} instance. +func New{{.Go.ServerName}}() {{.Go.ServerName}} { return &{{.Go.StructName}}{} } diff --git a/pkg/grapicmd/internal/module/generator/template/service/{{.ServerDir}}/{{.Path}}_server_register_funcs.go.tmpl b/pkg/svcgen/template/_data/{{.ServerDir}}/{{.Path}}_server_register_funcs.go.tmpl similarity index 91% rename from pkg/grapicmd/internal/module/generator/template/service/{{.ServerDir}}/{{.Path}}_server_register_funcs.go.tmpl rename to pkg/svcgen/template/_data/{{.ServerDir}}/{{.Path}}_server_register_funcs.go.tmpl index b9b1ad61..1f820020 100644 --- a/pkg/grapicmd/internal/module/generator/template/service/{{.ServerDir}}/{{.Path}}_server_register_funcs.go.tmpl +++ b/pkg/svcgen/template/_data/{{.ServerDir}}/{{.Path}}_server_register_funcs.go.tmpl @@ -1,3 +1,5 @@ +// Code generated by github.com/izumin5210/grapi. DO NOT EDIT. + package {{.Go.Package }} import ( diff --git a/pkg/grapicmd/internal/module/generator/template/service/{{.ServerDir}}/{{.Path}}_server_test.go.tmpl b/pkg/svcgen/template/_data/{{.ServerDir}}/{{.Path}}_server_test.go.tmpl similarity index 92% rename from pkg/grapicmd/internal/module/generator/template/service/{{.ServerDir}}/{{.Path}}_server_test.go.tmpl rename to pkg/svcgen/template/_data/{{.ServerDir}}/{{.Path}}_server_test.go.tmpl index aa7cbdc7..14207074 100644 --- a/pkg/grapicmd/internal/module/generator/template/service/{{.ServerDir}}/{{.Path}}_server_test.go.tmpl +++ b/pkg/svcgen/template/_data/{{.ServerDir}}/{{.Path}}_server_test.go.tmpl @@ -3,7 +3,8 @@ package {{.Go.Package }} import ( "context" "testing" -{{range .Go.TestImports}} + +{{- range .Go.TestImports}} "{{.}}" {{- end}} @@ -20,6 +21,8 @@ func Test_{{$go.ServerName}}_{{.Method}}(t *testing.T) { resp, err := svr.{{.Method}}(ctx, req) + t.SkipNow() + if err != nil { t.Errorf("returned an error %v", err) } diff --git a/pkg/svcgen/template/gen.go b/pkg/svcgen/template/gen.go new file mode 100644 index 00000000..ba18632b --- /dev/null +++ b/pkg/svcgen/template/gen.go @@ -0,0 +1,3 @@ +//go:generate statik -src ./_data -dest .. -p template -f -m + +package template diff --git a/pkg/svcgen/template/statik.go b/pkg/svcgen/template/statik.go new file mode 100644 index 00000000..57219db3 --- /dev/null +++ b/pkg/svcgen/template/statik.go @@ -0,0 +1,13 @@ +// Code generated by statik. DO NOT EDIT. + +// Package statik contains static assets. +package template + +import ( + "github.com/rakyll/statik/fs" +) + +func init() { + data := "PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\"\x00 \x00{{.ProtoDir}}/{{.Path}}.proto.tmplUT\x05\x00\x01\x80Cm8\x8cP\xbbn\xc30\x0c\x9c\xab\xaf <%C\xdc\xa1[\x02/\x1d\xfa\x18R\x18I\xf6B\x89Y\xc5h,\xa9\x92R\xd4\x10\xf8\xef\x05e\xd9p3\xd5\x13u<\xdf\x1d\xcf\xf7:\xc8\x1f\xa8\xa0\xb0\xce\x04\xf3Pl\x84\xb0\xf2\xf4)\x15B\x8cP\xd6\x8c\x96uF\x886B\x18\x1bZ\xa3A\x99\xf7\x91XA\x91\xb8\xc7\xe7\x89Z\xcbpf\xfa-\xfe&;\x96a\x9b\x18\x9d\xd4\nG\x8f\xd7\xce\x1a\x17<\x91h\xd3\xc4\x9aeb\xc6\xb8\x02\xd4\x0d\x91\x10\x1e\xddw{\x1a\xa2\xed\x879+\xee\xc7\xcd]\xe2g\xe9-\x86\xb3iX\x14\xc0\xd9\x13\xc4\x98!\"X\xc4X\xee\xf0\xeb\x8a>\xa4\x04DKp\x18\xaeN\xfb\xbc\xf3\xd6h\x8f\xd32\n\x00\x80|\xfeB\x19\xa3.XJ\xdb\x96\xe7\x10\xec\x12\xaaL\x00vy9\x1c\xea\xc9j\x0d\xc5\xfd\x88q1D\xc5\xc4\\A\xfb\x01\xc3\xea\xd14}J\xca\xdf\xd14\xfd:u0\xdb\xcd\x7f\x1b\x1a\xe1\x17m\x04\x00\xcdj\xa2\xdbn\xb7\xe8\xbdT\xc8=t\xc3\xc8\x19\xb99\xa2\x94zV\xd9S\x8b\x97\xdc\xd8\x98n\x87\x16e\xc0\xc1\xcf\xe5\x07+\x1cz\x9b\x14&\xad*\xa1RQ\xca\x94\x12],(\xbbI\xf3\x8c\x86\x98\xc6\xba^\xfe\n\x84\xcf\x0f\x04\x11E\x08\xef\x8f\x16w\x9f\xd2/\xc7:\xe6\xc2\xf8\xb0\x9dK)\x94\xa5\xb8\xc6r\x19\xdf\x89O\xb6Kg\x91\x07n<>f\x8d\x1b\x87<\xf0\xdc\x19c\xa3\xf9\x19\xcb\x89\xc9\xcf\xf3w\x05G\x8f\x89)\x7f\xd0\xe3D\x9e\xb7\x16\xd9\xf7\xf6\x9eZ4s\x8f\x1f\xad\xf1TnZ\x81\x9c\xb3\xaeM\x89l6\xf8u\xff\xe5\xfe\x0e;\xcbx!F?\x8cg\x1a\xc80u\xf250\xd3\x9fW\xf0\xacx\xf2\xf2k\xe26\xdav\xe4\xe5os\xd5\xbeB=K\xfd\xb1\x13\xfc\xc9N\xe7\xee\xa2\x86\x9e\xdf\xd5m\xce\x8fL\x973\xfa\x1f\x00\x00\xff\xffPK\x07\x08\x1an\x9c\xd3\x89\x01\x00\x00j\x03\x00\x00PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x006\x00 \x00{{.ServerDir}}/{{.Path}}_server_register_funcs.go.tmplUT\x05\x00\x01\x80Cm8\x94Q\xcd\xce\xd30\x10<\x7f~\x8aUNIU\x1c@\xe2 \xf2!\xe0@[\xd1J\x9c]wq-b;\xda\xacKJ\x94wGN\x0c\x14\x15\x84z\xb2\xf7gfvv\xeb\x1a\x9apB0\xe8\x91\x14\xe3 \x8eW0\x96\xcf\xf1(up\xb5\xfd\x1e\x9d\xf5o^\xbfzY\x1bR\x9d\x95\xf0\xbc\x85\xcd\xf6\x00o\x9f?\x1c\xa4\x10\x9d\xd2_\x95A\x18G\xf9.\xc8]\x8e\xa6I\x08\xeb\xba@\x0c\xa5x*t\xf0\x8c\x03\x17B<\x157\xdc\x86:\xfd\x02u\xe8\xaf=c\x0e\x8db\xfc\xa6\xae5E\xcf\xd6a\x91\x10!\x98\x16\xa5 \xad\xf2F\x062sg\"\x1bG\xb9;\xfe\x96\xdd(\x87\xd3\x04\xc58\xc2\x1f\xf9\x9d\xe23LS!*!\xea\x1a>\xa1\xb1=#}\xb6|\xde#]\x90\xc0\xba\xaeE\x87\x9e{\x98]\xf6sZ.Uy\x0f\x90\xe2K\xf4\x1a\xca\x1eV\x8b\xf1=S\xd4\xbc\x0cP\xfdE\xa1L#\xef/\x04\xab\xf4\xc9\xc4\x15\x8c\xff\xf0\xf0K2\xb3\xcf\xedK\xe9'\xd3\x1a\xfaJLw\x86\xde+\x7fj\x1fr\x94\x11\x0fX\xca\x88R\xf3\x00\xf9\xb4\xb2Y\xde5\xb88\xc0*_o\x91\xfb\x18\x87u\xea\xf3\xd9|\xd3Z\xf4\xdc\x04\xef+@\xa2@i\x0d\x84\x1c\xc9\xc3\x7f\xb7\x91\x18\xad\xce\x85\x1c\xdc\xcc3\xeb/ji;?\x02\x00\x00\xff\xffPK\x07\x084\x1fAoU\x01\x00\x00\xde\x02\x00\x00PK\x03\x04\x14\x00\x08\x00\x08\x00\x00\x00!(\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00,\x00 \x00{{.ServerDir}}/{{.Path}}_server_test.go.tmplUT\x05\x00\x01\x80Cm8d\x90Ao\xdb0\x0c\x85\xcf\xd2\xaf\xe0\x84n\xb0\x87V?`@.\x03\x86`\x87\x05\xc1\xda{\xe1\xda\xb4c\xa4\x95\x1c\x8av\x03\x10\xfa\xef\x03\x15o]\x90\x9bM\xbd\x8f\xef\xf1MM{l\x06\x04\x11\xbf\x8d~\xbf\xfe\xe5lE\xc6\x1e\xfc/\xe4C\xecR\xcev|\x9b\"1T\xd6\xb86\x06\xc63;k\x1cc\xe21\x0c\xceZ\x91\x07\xa0&\x0c\x08\xba\xe8 \x13\xff,\x84\xb2\xc6\x89\xf8\x9c]\x11a\xe8r\xb6\xd6\x88\xf8\xfd\xcb\x87\xe7\xaey\xc3\x9c\xc1\x89\xc0\xd5|\xdf\xf0\x01\x94\xad\xad\xc8\xdd\x10\xe1\xdbF\x1d\xe0\xa1d\xbc\x9b^\xb6\x97\x912\xeb\xf0_\x90\x8f\xf4\xfd\x1cZ\xd0P\xcfe\x89\x7fDZ\x90.\x9e\xcf\"\xab2\xe7\x8a\xe1\xebz\x93\x7f\xaaA\xacI\x0b\xe9\xfe\x1d\xbe\xdf\x92Um\xadi\xf9\xac\x82\xb5\x14\xff\xbdi\x8f\x03\xc59tUm\x0d\xe1I\x1f\xbf\x88\xf8\xdfx\x9a1\xf16B }}\xb7h#\x84i\xba\x07\xa4b\x98\x16\xf2\xff\x07k\xf9|\x0f\x84'\xb5d\xffx\x1c\xa7]|/\x01\xc6\xbe0\x9f6\x10\xc6W\x8dl\xd8\xff \x8a\xd4W\x8e\x90g\n\xd8A\x13T\x14 >/\xaex\xd4\xd6\xe4\x0b\xac\xb6\xb0\xb9\xa1\x15NS\x0c !\x1d\xe2\xfc\xdaA\x88\xac\"WP-\x1aC\xb7V\xfe\xf7\xebO\x00\x00\x00\xff\xffPK\x07\x08\xa73igR\x01\x00\x00O\x02\x00\x00PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(7\x19\x19\x13B\x01\x00\x00\xae\x02\x00\x00\"\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x00\x00\x00\x00{{.ProtoDir}}/{{.Path}}.proto.tmplUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(\x1an\x9c\xd3\x89\x01\x00\x00j\x03\x00\x00'\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x9b\x01\x00\x00{{.ServerDir}}/{{.Path}}_server.go.tmplUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(4\x1fAoU\x01\x00\x00\xde\x02\x00\x006\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x82\x03\x00\x00{{.ServerDir}}/{{.Path}}_server_register_funcs.go.tmplUT\x05\x00\x01\x80Cm8PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x00\x00!(\xa73igR\x01\x00\x00O\x02\x00\x00,\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81D\x05\x00\x00{{.ServerDir}}/{{.Path}}_server_test.go.tmplUT\x05\x00\x01\x80Cm8PK\x05\x06\x00\x00\x00\x00\x04\x00\x04\x00\x87\x01\x00\x00\xf9\x06\x00\x00\x00\x00" + fs.Register(data) +} diff --git a/pkg/svcgen/testing/run.go b/pkg/svcgen/testing/run.go new file mode 100644 index 00000000..ec419288 --- /dev/null +++ b/pkg/svcgen/testing/run.go @@ -0,0 +1,103 @@ +package testing + +import ( + "go/build" + "testing" + + "github.com/bradleyjkemp/cupaloy/v2" + "github.com/izumin5210/grapi/pkg/cli" + "github.com/izumin5210/grapi/pkg/gencmd" + "github.com/izumin5210/grapi/pkg/grapicmd/util/fs" + "github.com/spf13/afero" +) + +type Ctx struct { + GOPATH string + RootDir cli.RootDir + CreateCmd func(*testing.T, afero.Fs, Case) gencmd.Executor + Cases []Case +} + +type Case struct { + Test string + GArgs []string + DArgs []string + Files []string + SkippedFiles map[string]struct{} + ProtoDir string + ProtoOutDir string + ServerDir string + PkgName string +} + +func Run(t *testing.T, ctx *Ctx) { + t.Helper() + + defer func(c build.Context) { fs.BuildContext = c }(fs.BuildContext) + fs.BuildContext = build.Context{GOPATH: ctx.GOPATH} + + for _, tc := range ctx.Cases { + t.Run(tc.Test, func(t *testing.T) { + fs := afero.NewMemMapFs() + afero.WriteFile(fs, ctx.RootDir.Join("grapi.toml").String(), []byte{}, 0755) + + t.Run("generate", func(t *testing.T) { + cmd := ctx.CreateCmd(t, fs, tc) + cmd.Command().SetArgs(append([]string{"generate"}, tc.GArgs...)) + err := cmd.Execute() + + if err != nil { + t.Errorf("returned an error: %+v", err) + } + + for _, file := range tc.Files { + t.Run(file, func(t *testing.T) { + if _, ok := tc.SkippedFiles[file]; ok { + ok, err := afero.Exists(fs, file) + + if err != nil { + t.Errorf("returned an error: %v", err) + } + + if ok { + t.Error("should not exist") + } + } else { + data, err := afero.ReadFile(fs, ctx.RootDir.Join(file).String()) + + if err != nil { + t.Errorf("returned an error: %v", err) + } + + cupaloy.SnapshotT(t, string(data)) + } + }) + } + }) + + t.Run("destroy", func(t *testing.T) { + cmd := ctx.CreateCmd(t, fs, tc) + cmd.Command().SetArgs(append([]string{"destroy"}, tc.DArgs...)) + err := cmd.Execute() + + if err != nil { + t.Errorf("returned an error: %+v", err) + } + + for _, file := range tc.Files { + t.Run(file, func(t *testing.T) { + ok, err := afero.Exists(fs, ctx.RootDir.Join(file).String()) + + if err != nil { + t.Errorf("Exists(fs, %q) returned an error: %v", file, err) + } + + if ok { + t.Errorf("%q should not exist", file) + } + }) + } + }) + }) + } +} diff --git a/pkg/svcgen/testing/wire.go b/pkg/svcgen/testing/wire.go new file mode 100644 index 00000000..472ff6cd --- /dev/null +++ b/pkg/svcgen/testing/wire.go @@ -0,0 +1,21 @@ +//+build wireinject + +package testing + +import ( + "github.com/google/wire" + + "github.com/izumin5210/grapi/pkg/cli" + "github.com/izumin5210/grapi/pkg/gencmd" + "github.com/izumin5210/grapi/pkg/protoc" + "github.com/izumin5210/grapi/pkg/svcgen" +) + +func NewTestApp(*gencmd.Command, protoc.Wrapper, cli.UI) (*svcgen.App, error) { + wire.Build( + gencmd.Set, + svcgen.ProvideParamsBuilder, + svcgen.App{}, + ) + return nil, nil +} diff --git a/pkg/svcgen/testing/wire_gen.go b/pkg/svcgen/testing/wire_gen.go new file mode 100644 index 00000000..541ddff6 --- /dev/null +++ b/pkg/svcgen/testing/wire_gen.go @@ -0,0 +1,30 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate wire +//+build !wireinject + +package testing + +import ( + "github.com/izumin5210/grapi/pkg/cli" + "github.com/izumin5210/grapi/pkg/gencmd" + "github.com/izumin5210/grapi/pkg/grapicmd" + "github.com/izumin5210/grapi/pkg/protoc" + "github.com/izumin5210/grapi/pkg/svcgen" +) + +// Injectors from wire.go: + +func NewTestApp(command *gencmd.Command, wrapper protoc.Wrapper, ui cli.UI) (*svcgen.App, error) { + ctx := gencmd.ProvideCtx(command) + grapicmdCtx := gencmd.ProvideGrapiCtx(ctx) + rootDir := grapicmd.ProvideRootDir(grapicmdCtx) + config := grapicmd.ProvideProtocConfig(grapicmdCtx) + grapicmdConfig := grapicmd.ProvideConfig(grapicmdCtx) + builder := svcgen.ProvideParamsBuilder(rootDir, config, grapicmdConfig) + app := &svcgen.App{ + ProtocWrapper: wrapper, + ParamsBuilder: builder, + } + return app, nil +} diff --git a/pkg/svcgen/wire.go b/pkg/svcgen/wire.go new file mode 100644 index 00000000..6b9f94b7 --- /dev/null +++ b/pkg/svcgen/wire.go @@ -0,0 +1,21 @@ +//+build wireinject + +package svcgen + +import ( + "github.com/google/wire" + + "github.com/izumin5210/grapi/pkg/cli" + "github.com/izumin5210/grapi/pkg/gencmd" + "github.com/izumin5210/grapi/pkg/protoc" +) + +func NewApp(*gencmd.Command) (*App, error) { + wire.Build( + Set, + gencmd.Set, + cli.UIInstance, + protoc.WrapperSet, + ) + return nil, nil +} diff --git a/pkg/svcgen/wire_gen.go b/pkg/svcgen/wire_gen.go new file mode 100644 index 00000000..43376044 --- /dev/null +++ b/pkg/svcgen/wire_gen.go @@ -0,0 +1,43 @@ +// Code generated by Wire. DO NOT EDIT. + +//go:generate wire +//+build !wireinject + +package svcgen + +import ( + "github.com/izumin5210/grapi/pkg/cli" + "github.com/izumin5210/grapi/pkg/gencmd" + "github.com/izumin5210/grapi/pkg/grapicmd" + "github.com/izumin5210/grapi/pkg/protoc" +) + +import ( + _ "github.com/izumin5210/grapi/pkg/svcgen/template" +) + +// Injectors from wire.go: + +func NewApp(command *gencmd.Command) (*App, error) { + ctx := gencmd.ProvideCtx(command) + grapicmdCtx := gencmd.ProvideGrapiCtx(ctx) + config := grapicmd.ProvideProtocConfig(grapicmdCtx) + fs := grapicmd.ProvideFS(grapicmdCtx) + executor := grapicmd.ProvideExec(grapicmdCtx) + io := grapicmd.ProvideIO(grapicmdCtx) + ui := cli.UIInstance(io) + rootDir := grapicmd.ProvideRootDir(grapicmdCtx) + gexConfig := protoc.ProvideGexConfig(fs, executor, io, rootDir) + repository, err := protoc.ProvideToolRepository(gexConfig) + if err != nil { + return nil, err + } + wrapper := protoc.NewWrapper(config, fs, executor, ui, repository, rootDir) + grapicmdConfig := grapicmd.ProvideConfig(grapicmdCtx) + builder := ProvideParamsBuilder(rootDir, config, grapicmdConfig) + app := &App{ + ProtocWrapper: wrapper, + ParamsBuilder: builder, + } + return app, nil +} diff --git a/testing/.gitignore b/testing/.gitignore deleted file mode 100644 index c1a21e90..00000000 --- a/testing/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -*.so -/vendor -/bin -/tmp diff --git a/testing/Gopkg.lock b/testing/Gopkg.lock deleted file mode 100644 index 890a80b6..00000000 --- a/testing/Gopkg.lock +++ /dev/null @@ -1,149 +0,0 @@ -# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. - - -[[projects]] - branch = "master" - name = "github.com/golang/glog" - packages = ["."] - revision = "23def4e6c14b4da8ac2ed8007337bc5eb5007998" - -[[projects]] - name = "github.com/golang/protobuf" - packages = [ - "jsonpb", - "proto", - "protoc-gen-go", - "protoc-gen-go/descriptor", - "protoc-gen-go/generator", - "protoc-gen-go/grpc", - "protoc-gen-go/plugin", - "ptypes", - "ptypes/any", - "ptypes/duration", - "ptypes/struct", - "ptypes/timestamp" - ] - revision = "925541529c1fa6821df4e44ce2723319eb2be768" - version = "v1.0.0" - -[[projects]] - branch = "master" - name = "github.com/grpc-ecosystem/go-grpc-middleware" - packages = ["."] - revision = "aed189ae50cf2ee326c1de8c083f3187e574e0d8" - -[[projects]] - name = "github.com/grpc-ecosystem/grpc-gateway" - packages = [ - "protoc-gen-grpc-gateway", - "protoc-gen-grpc-gateway/descriptor", - "protoc-gen-grpc-gateway/generator", - "protoc-gen-grpc-gateway/gengateway", - "protoc-gen-grpc-gateway/httprule", - "protoc-gen-swagger", - "protoc-gen-swagger/genswagger", - "protoc-gen-swagger/options", - "runtime", - "runtime/internal", - "utilities" - ] - revision = "07f5e79768022f9a3265235f0db4ac8c3f675fec" - version = "v1.3.1" - -[[projects]] - branch = "master" - name = "github.com/izumin5210/grapi" - packages = [ - "pkg/grapiserver", - "pkg/grapiserver/internal" - ] - revision = "b01e710255e4d296d7b3920d0159dbdf4c91b7ed" - -[[projects]] - name = "github.com/pkg/errors" - packages = ["."] - revision = "645ef00459ed84a119197bfb8d8205042c6df63d" - version = "v0.8.0" - -[[projects]] - branch = "master" - name = "golang.org/x/net" - packages = [ - "context", - "http2", - "http2/hpack", - "idna", - "internal/timeseries", - "lex/httplex", - "trace" - ] - revision = "6078986fec03a1dcc236c34816c71b0e05018fda" - -[[projects]] - name = "golang.org/x/text" - packages = [ - "collate", - "collate/build", - "internal/colltab", - "internal/gen", - "internal/tag", - "internal/triegen", - "internal/ucd", - "language", - "secure/bidirule", - "transform", - "unicode/bidi", - "unicode/cldr", - "unicode/norm", - "unicode/rangetable" - ] - revision = "f21a4dfb5e38f5895301dc265a8def02365cc3d0" - version = "v0.3.0" - -[[projects]] - branch = "master" - name = "google.golang.org/genproto" - packages = [ - "googleapis/api/annotations", - "googleapis/rpc/status" - ] - revision = "ab0870e398d5dd054b868c0db1481ab029b9a9f2" - -[[projects]] - name = "google.golang.org/grpc" - packages = [ - ".", - "balancer", - "balancer/base", - "balancer/roundrobin", - "codes", - "connectivity", - "credentials", - "encoding", - "encoding/proto", - "grpclb/grpc_lb_v1/messages", - "grpclog", - "internal", - "keepalive", - "metadata", - "naming", - "peer", - "reflection", - "reflection/grpc_reflection_v1alpha", - "resolver", - "resolver/dns", - "resolver/passthrough", - "stats", - "status", - "tap", - "transport" - ] - revision = "8e4536a86ab602859c20df5ebfd0bd4228d08655" - version = "v1.10.0" - -[solve-meta] - analyzer-name = "dep" - analyzer-version = 1 - inputs-digest = "7700d77807c6f5a7c34e5a5a6f4b90c028b59ef4a1e6f2a3496a7cb66a5e1548" - solver-name = "gps-cdcl" - solver-version = 1 diff --git a/testing/Gopkg.toml b/testing/Gopkg.toml deleted file mode 100644 index 3c4a6881..00000000 --- a/testing/Gopkg.toml +++ /dev/null @@ -1,18 +0,0 @@ -required = [ - "github.com/golang/protobuf/protoc-gen-go", - "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway", - "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger", -] - -[prune] - go-tests = true - unused-packages = true - - [[prune.project]] - name = "github.com/grpc-ecosystem/grpc-gateway" - non-go = false - unused-packages = false - -[[constraint]] - branch = "master" - name = "github.com/izumin5210/grapi" diff --git a/testing/app/run.go b/testing/app/run.go deleted file mode 100644 index c9c90169..00000000 --- a/testing/app/run.go +++ /dev/null @@ -1,16 +0,0 @@ -package app - -import ( - "github.com/izumin5210/grapi/pkg/grapiserver" -) - -// Run starts the grapiserver. -func Run() error { - s := grapiserver.New( - grapiserver.WithDefaultLogger(), - grapiserver.WithServers( - // TODO - ), - ) - return s.Serve() -} diff --git a/testing/cmd/server/run.go b/testing/cmd/server/run.go deleted file mode 100644 index 547f3748..00000000 --- a/testing/cmd/server/run.go +++ /dev/null @@ -1,19 +0,0 @@ -package main - -import ( - "os" - - "github.com/izumin5210/grapi/testing/app" -) - -func main() { - os.Exit(run()) -} - -func run() int { - err := app.Run() - if err != nil { - return 1 - } - return 0 -} diff --git a/testing/grapi.toml b/testing/grapi.toml deleted file mode 100644 index 6eec4edd..00000000 --- a/testing/grapi.toml +++ /dev/null @@ -1,25 +0,0 @@ -[grapi] -server_dir = "./app/server" - -[protoc] -protos_dir = "./api/protos" -out_dir = "./api" -import_dirs = [ - "./vendor/github.com/grpc-ecosystem/grpc-gateway", - "./vendor/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis", -] - - [[protoc.plugins]] - path = "./vendor/github.com/golang/protobuf/protoc-gen-go" - name = "go" - args = { plugins = "grpc" } - - [[protoc.plugins]] - path = "./vendor/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway" - name = "grpc-gateway" - args = { logtostderr = true } - - [[protoc.plugins]] - path = "./vendor/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger" - name = "swagger" - args = { logtostderr = true } diff --git a/tools.go b/tools.go new file mode 100644 index 00000000..fa75c87f --- /dev/null +++ b/tools.go @@ -0,0 +1,21 @@ +// Code generated by github.com/izumin5210/gex. DO NOT EDIT. + +// +build tools + +package tools + +// tool dependencies +import ( + _ "github.com/golang/mock/mockgen" + _ "github.com/google/wire/cmd/wire" + _ "github.com/izumin5210/gex/cmd/gex" + _ "github.com/rakyll/statik" +) + +// If you want to use tools, please run the following command: +// go generate ./tools.go +// +//go:generate go build -v -o=./bin/mockgen github.com/golang/mock/mockgen +//go:generate go build -v -o=./bin/wire github.com/google/wire/cmd/wire +//go:generate go build -v -o=./bin/gex github.com/izumin5210/gex/cmd/gex +//go:generate go build -v -o=./bin/statik github.com/rakyll/statik