Compare commits
18 Commits
Author | SHA1 | Date | |
---|---|---|---|
2ec1af2884 | |||
a63ea1ec4d | |||
74fecbec12 | |||
506f19310f | |||
59ee7bc1de | |||
|
763ca7ff90 | ||
|
af4f014e20 | ||
4329841a0b | |||
452b8de530 | |||
db8b8dbdd2 | |||
|
c69b0656a4 | ||
3512d60b3a | |||
2112172891 | |||
5d13b84c7a | |||
ee4d83458f | |||
dd62c380f2 | |||
e4f2867419 | |||
c5f68efd4c |
19
.github/dependabot.yml
vendored
Normal file
19
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# To get started with Dependabot version updates, you'll need to specify which
|
||||||
|
# package ecosystems to update and where the package manifests are located.
|
||||||
|
# Please see the documentation for all configuration options:
|
||||||
|
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||||
|
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
|
||||||
|
# Maintain dependencies for GitHub Actions
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
||||||
|
|
||||||
|
# Maintain dependencies for Golang
|
||||||
|
- package-ecosystem: "gomod"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "daily"
|
20
.github/renovate.json
vendored
20
.github/renovate.json
vendored
@@ -1,20 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": [
|
|
||||||
"config:base"
|
|
||||||
],
|
|
||||||
"postUpdateOptions": ["gomodTidy"],
|
|
||||||
"packageRules": [
|
|
||||||
{
|
|
||||||
"matchUpdateTypes": ["minor", "patch", "pin", "digest"],
|
|
||||||
"automerge": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"groupName": "all deps",
|
|
||||||
"separateMajorMinor": true,
|
|
||||||
"groupSlug": "all",
|
|
||||||
"packagePatterns": [
|
|
||||||
"*"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
75
.github/workflows/codeql-analysis.yml
vendored
Normal file
75
.github/workflows/codeql-analysis.yml
vendored
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
# For most projects, this workflow file will not need changing; you simply need
|
||||||
|
# to commit it to your repository.
|
||||||
|
#
|
||||||
|
# You may wish to alter this file to override the set of languages analyzed,
|
||||||
|
# or to provide custom queries or build logic.
|
||||||
|
#
|
||||||
|
# ******** NOTE ********
|
||||||
|
# We have attempted to detect the languages in your repository. Please check
|
||||||
|
# the `language` matrix defined below to confirm you have the correct set of
|
||||||
|
# supported CodeQL languages.
|
||||||
|
#
|
||||||
|
name: "CodeQL"
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_run:
|
||||||
|
workflows: ["prbuild"]
|
||||||
|
types:
|
||||||
|
- completed
|
||||||
|
push:
|
||||||
|
branches: [ master ]
|
||||||
|
pull_request:
|
||||||
|
# The branches below must be a subset of the branches above
|
||||||
|
branches: [ master ]
|
||||||
|
schedule:
|
||||||
|
- cron: '34 1 * * 0'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
analyze:
|
||||||
|
name: Analyze
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
actions: read
|
||||||
|
contents: read
|
||||||
|
security-events: write
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
language: [ 'go' ]
|
||||||
|
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
|
||||||
|
# Learn more:
|
||||||
|
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
# Initializes the CodeQL tools for scanning.
|
||||||
|
- name: Initialize CodeQL
|
||||||
|
uses: github/codeql-action/init@v1
|
||||||
|
with:
|
||||||
|
languages: ${{ matrix.language }}
|
||||||
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
|
# By default, queries listed here will override any specified in a config file.
|
||||||
|
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||||
|
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||||
|
|
||||||
|
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||||
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
|
- name: Autobuild
|
||||||
|
uses: github/codeql-action/autobuild@v1
|
||||||
|
|
||||||
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
|
# 📚 https://git.io/JvXDl
|
||||||
|
|
||||||
|
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||||
|
# and modify them (or add more) to build your code if your project
|
||||||
|
# uses a compiled language
|
||||||
|
|
||||||
|
#- run: |
|
||||||
|
# make bootstrap
|
||||||
|
# make release
|
||||||
|
|
||||||
|
- name: Perform CodeQL Analysis
|
||||||
|
uses: github/codeql-action/analyze@v1
|
66
.github/workflows/dependabot-automerge.yml
vendored
Normal file
66
.github/workflows/dependabot-automerge.yml
vendored
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
name: "prautomerge"
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_run:
|
||||||
|
workflows: ["prbuild"]
|
||||||
|
types:
|
||||||
|
- completed
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
Dependabot-Automerge:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
# Contains workaround to execute if dependabot updates the PR by checking for the base branch in the linked PR
|
||||||
|
# The the github.event.workflow_run.event value is 'push' and not 'pull_request'
|
||||||
|
# dont work with multiple workflows when last returns success
|
||||||
|
if: >-
|
||||||
|
github.event.workflow_run.conclusion == 'success'
|
||||||
|
&& github.actor == 'dependabot[bot]'
|
||||||
|
&& github.event.sender.login == 'dependabot[bot]'
|
||||||
|
&& github.event.sender.type == 'Bot'
|
||||||
|
&& (github.event.workflow_run.event == 'pull_request'
|
||||||
|
|| (github.event.workflow_run.event == 'push' && github.event.workflow_run.pull_requests[0].base.ref == github.event.repository.default_branch ))
|
||||||
|
steps:
|
||||||
|
- name: Approve Changes and Merge changes if label 'dependencies' is set
|
||||||
|
uses: actions/github-script@v5
|
||||||
|
with:
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
script: |
|
||||||
|
console.log(context.payload.workflow_run);
|
||||||
|
|
||||||
|
var labelNames = await github.paginate(
|
||||||
|
github.issues.listLabelsOnIssue,
|
||||||
|
{
|
||||||
|
repo: context.repo.repo,
|
||||||
|
owner: context.repo.owner,
|
||||||
|
issue_number: context.payload.workflow_run.pull_requests[0].number,
|
||||||
|
},
|
||||||
|
(response) => response.data.map(
|
||||||
|
(label) => label.name
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
console.log(labelNames);
|
||||||
|
|
||||||
|
if (labelNames.includes('dependencies')) {
|
||||||
|
console.log('Found label');
|
||||||
|
|
||||||
|
await github.pulls.createReview({
|
||||||
|
repo: context.repo.repo,
|
||||||
|
owner: context.repo.owner,
|
||||||
|
pull_number: context.payload.workflow_run.pull_requests[0].number,
|
||||||
|
event: 'APPROVE'
|
||||||
|
});
|
||||||
|
console.log('Approved PR');
|
||||||
|
|
||||||
|
await github.pulls.merge({
|
||||||
|
repo: context.repo.repo,
|
||||||
|
owner: context.repo.owner,
|
||||||
|
pull_number: context.payload.workflow_run.pull_requests[0].number,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Merged PR');
|
||||||
|
}
|
2
.gitignore
vendored
2
.gitignore
vendored
@@ -34,5 +34,5 @@ _cgo_export.*
|
|||||||
*~
|
*~
|
||||||
*.swp
|
*.swp
|
||||||
*.swo
|
*.swo
|
||||||
/protoc-gen-micro
|
/protoc-gen-go-micro
|
||||||
|
|
||||||
|
@@ -10,7 +10,7 @@ This project is a generator plugin for the Google Protocol Buffers compiler (`pr
|
|||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
```console
|
```console
|
||||||
$> protoc --micro_out=debug=true,components="micro|http":. input.proto
|
$> protoc --go_micro_out=debug=true,components="micro|http":. input.proto
|
||||||
```
|
```
|
||||||
|
|
||||||
| Option | Default Value | Accepted Values | Description
|
| Option | Default Value | Accepted Values | Description
|
||||||
@@ -22,5 +22,5 @@ $> protoc --micro_out=debug=true,components="micro|http":. input.proto
|
|||||||
## Install
|
## Install
|
||||||
|
|
||||||
* Install the **go** compiler and tools from https://golang.org/doc/install
|
* Install the **go** compiler and tools from https://golang.org/doc/install
|
||||||
* Install **protoc-gen-go**: `go get google.golang.org/protobuf/cmd/protoc-gen-go`
|
* Install **protoc-gen-go**: `go install google.golang.org/protobuf/cmd/protoc-gen-go`
|
||||||
* Install **protoc-gen-go-micro**: `go get github.com/unistack-org/protoc-gen-go-micro/v3`
|
* Install **protoc-gen-go-micro**: `go install go.unistack.org/protoc-gen-go-micro/v3`
|
||||||
|
75
ast.go
75
ast.go
@@ -10,52 +10,63 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/fatih/structtag"
|
"github.com/fatih/structtag"
|
||||||
tag_options "github.com/unistack-org/micro-proto/tag"
|
tag_options "go.unistack.org/micro-proto/v3/tag"
|
||||||
"google.golang.org/protobuf/compiler/protogen"
|
"google.golang.org/protobuf/compiler/protogen"
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var astFields = make(map[string]map[string]map[string]*structtag.Tags) // map proto file with proto message ast struct
|
||||||
astFields = make(map[string]map[string]map[string]*structtag.Tags) // map proto file with proto message ast struct
|
|
||||||
)
|
func (g *Generator) astFill(file *protogen.File, message *protogen.Message) error {
|
||||||
|
for _, field := range message.Fields {
|
||||||
|
if field.Desc.Options() == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !proto.HasExtension(field.Desc.Options(), tag_options.E_Tags) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
opts := proto.GetExtension(field.Desc.Options(), tag_options.E_Tags)
|
||||||
|
if opts != nil {
|
||||||
|
fpath := filepath.Join(g.tagPath, file.GeneratedFilenamePrefix+".pb.go")
|
||||||
|
mp, ok := astFields[fpath]
|
||||||
|
if !ok {
|
||||||
|
mp = make(map[string]map[string]*structtag.Tags)
|
||||||
|
}
|
||||||
|
nmp, ok := mp[message.GoIdent.GoName]
|
||||||
|
if !ok {
|
||||||
|
nmp = make(map[string]*structtag.Tags)
|
||||||
|
}
|
||||||
|
tags, err := structtag.Parse(opts.(string))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
nmp[field.GoName] = tags
|
||||||
|
mp[message.GoIdent.GoName] = nmp
|
||||||
|
astFields[fpath] = mp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, nmessage := range message.Messages {
|
||||||
|
if err := g.astFill(file, nmessage); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (g *Generator) astGenerate(plugin *protogen.Plugin) error {
|
func (g *Generator) astGenerate(plugin *protogen.Plugin) error {
|
||||||
if g.tagPath == "" {
|
if g.tagPath == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, file := range plugin.Files {
|
for _, file := range plugin.Files {
|
||||||
if !file.Generate {
|
if !file.Generate {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, message := range file.Messages {
|
for _, message := range file.Messages {
|
||||||
for _, field := range message.Fields {
|
if err := g.astFill(file, message); err != nil {
|
||||||
if field.Desc.Options() == nil {
|
return err
|
||||||
continue
|
|
||||||
}
|
|
||||||
if !proto.HasExtension(field.Desc.Options(), tag_options.E_Tags) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
opts := proto.GetExtension(field.Desc.Options(), tag_options.E_Tags)
|
|
||||||
if opts != nil {
|
|
||||||
fpath := filepath.Join(g.tagPath, file.GeneratedFilenamePrefix+".pb.go")
|
|
||||||
mp, ok := astFields[fpath]
|
|
||||||
if !ok {
|
|
||||||
mp = make(map[string]map[string]*structtag.Tags)
|
|
||||||
}
|
|
||||||
nmp, ok := mp[message.GoIdent.GoName]
|
|
||||||
if !ok {
|
|
||||||
nmp = make(map[string]*structtag.Tags)
|
|
||||||
}
|
|
||||||
tags, err := structtag.Parse(opts.(string))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
nmp[field.GoName] = tags
|
|
||||||
mp[message.GoIdent.GoName] = nmp
|
|
||||||
astFields[fpath] = mp
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -11,7 +11,7 @@ import "google/protobuf/wrappers.proto";
|
|||||||
|
|
||||||
service Example {
|
service Example {
|
||||||
rpc Call(CallReq) returns (CallRsp) {
|
rpc Call(CallReq) returns (CallRsp) {
|
||||||
option (micro.openapiv2.openapiv2_operation) = {
|
option (micro.openapiv3.openapiv3_operation) = {
|
||||||
operation_id: "Call";
|
operation_id: "Call";
|
||||||
responses: {
|
responses: {
|
||||||
key: "default";
|
key: "default";
|
||||||
|
7
go.mod
7
go.mod
@@ -1,10 +1,13 @@
|
|||||||
module github.com/unistack-org/protoc-gen-go-micro/v3
|
module go.unistack.org/protoc-gen-go-micro/v3
|
||||||
|
|
||||||
go 1.16
|
go 1.16
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/fatih/structtag v1.2.0
|
github.com/fatih/structtag v1.2.0
|
||||||
github.com/unistack-org/micro-proto v0.0.5
|
go.unistack.org/micro-proto/v3 v3.1.0
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||||
google.golang.org/protobuf v1.27.1
|
google.golang.org/protobuf v1.27.1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//replace go.unistack.org/micro/v3 => ../micro
|
||||||
|
//replace go.unistack.org/micro-proto => ../micro-proto
|
||||||
|
5
go.sum
5
go.sum
@@ -3,12 +3,11 @@ github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4
|
|||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/unistack-org/micro-proto v0.0.5 h1:DIC97Hufa2nGjuvTsfToD9laEOKddWMRTzeCfBwJ1j8=
|
go.unistack.org/micro-proto/v3 v3.1.0 h1:q39FwjFiRZn+Ux/tt+d3bJTmDtsQQWa+3SLYVo1vLfA=
|
||||||
github.com/unistack-org/micro-proto v0.0.5/go.mod h1:EuI7UlfGXmT1hy6WacULib9LbNgRnDYQvTCFoLgKM2I=
|
go.unistack.org/micro-proto/v3 v3.1.0/go.mod h1:DpRhYCBXlmSJ/AAXTmntvlh7kQkYU6eFvlmYAx4BQS8=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
|
||||||
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
|
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
|
||||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||||
|
1
http.go
1
http.go
@@ -29,6 +29,7 @@ func (g *Generator) httpGenerate(component string, plugin *protogen.Plugin, genC
|
|||||||
|
|
||||||
gfile.Import(contextPackage)
|
gfile.Import(contextPackage)
|
||||||
gfile.Import(microApiPackage)
|
gfile.Import(microApiPackage)
|
||||||
|
|
||||||
if genClient {
|
if genClient {
|
||||||
gfile.Import(microClientPackage)
|
gfile.Import(microClientPackage)
|
||||||
gfile.Import(microClientHttpPackage)
|
gfile.Import(microClientHttpPackage)
|
||||||
|
29
main.go
29
main.go
@@ -10,10 +10,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
flagDebug = flag.Bool("debug", false, "")
|
flagDebug = flag.Bool("debug", false, "debug output")
|
||||||
flagStandalone = flag.Bool("standalone", false, "")
|
flagStandalone = flag.Bool("standalone", false, "generate file to standalone dir")
|
||||||
flagComponents = flag.String("components", "micro|rpc|http|client|server", "")
|
flagComponents = flag.String("components", "micro|rpc|http|client|server|openapiv3", "specify components to generate")
|
||||||
flagTagPath = flag.String("tag_path", "", "")
|
flagTagPath = flag.String("tag_path", "", "tag rewriting dir")
|
||||||
|
flagOpenapiFile = flag.String("openapi_file", "apidocs.swagger.json", "openapi file name")
|
||||||
|
flagHelp = flag.Bool("help", false, "display help message")
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -21,16 +23,24 @@ func main() {
|
|||||||
ParamFunc: flag.CommandLine.Set,
|
ParamFunc: flag.CommandLine.Set,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if *flagHelp {
|
||||||
|
flag.PrintDefaults()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
g := &Generator{}
|
g := &Generator{}
|
||||||
|
|
||||||
opts.Run(g.Generate)
|
opts.Run(g.Generate)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Generator struct {
|
type Generator struct {
|
||||||
components string
|
components string
|
||||||
standalone bool
|
standalone bool
|
||||||
debug bool
|
debug bool
|
||||||
tagPath string
|
tagPath string
|
||||||
|
openapiFile string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *Generator) Generate(plugin *protogen.Plugin) error {
|
func (g *Generator) Generate(plugin *protogen.Plugin) error {
|
||||||
@@ -40,6 +50,7 @@ func (g *Generator) Generate(plugin *protogen.Plugin) error {
|
|||||||
g.debug = *flagDebug
|
g.debug = *flagDebug
|
||||||
g.components = *flagComponents
|
g.components = *flagComponents
|
||||||
g.tagPath = *flagTagPath
|
g.tagPath = *flagTagPath
|
||||||
|
g.openapiFile = *flagOpenapiFile
|
||||||
plugin.SupportedFeatures = uint64(pluginpb.CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL)
|
plugin.SupportedFeatures = uint64(pluginpb.CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL)
|
||||||
|
|
||||||
var genClient bool
|
var genClient bool
|
||||||
@@ -73,6 +84,8 @@ func (g *Generator) Generate(plugin *protogen.Plugin) error {
|
|||||||
err = g.gorillaGenerate(component, plugin)
|
err = g.gorillaGenerate(component, plugin)
|
||||||
case "chi":
|
case "chi":
|
||||||
err = g.chiGenerate(component, plugin)
|
err = g.chiGenerate(component, plugin)
|
||||||
|
case "openapiv3":
|
||||||
|
err = g.openapiv3Generate(component, plugin)
|
||||||
default:
|
default:
|
||||||
err = fmt.Errorf("unknown component: %s", component)
|
err = fmt.Errorf("unknown component: %s", component)
|
||||||
}
|
}
|
||||||
|
673
openapiv3.go
Normal file
673
openapiv3.go
Normal file
@@ -0,0 +1,673 @@
|
|||||||
|
// Copyright 2020 Google LLC. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"regexp"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
annotations "go.unistack.org/micro-proto/v3/api"
|
||||||
|
v3 "go.unistack.org/micro-proto/v3/openapiv3"
|
||||||
|
"google.golang.org/protobuf/compiler/protogen"
|
||||||
|
jsonpb "google.golang.org/protobuf/encoding/protojson"
|
||||||
|
"google.golang.org/protobuf/proto"
|
||||||
|
"google.golang.org/protobuf/reflect/protoreflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
type openapiv3Generator struct {
|
||||||
|
plugin *protogen.Plugin
|
||||||
|
|
||||||
|
requiredSchemas []string // Names of schemas that need to be generated.
|
||||||
|
generatedSchemas []string // Names of schemas that have already been generated.
|
||||||
|
linterRulePattern *regexp.Regexp
|
||||||
|
namePattern *regexp.Regexp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Generator) openapiv3Generate(component string, plugin *protogen.Plugin) error {
|
||||||
|
og := &openapiv3Generator{
|
||||||
|
plugin: plugin,
|
||||||
|
requiredSchemas: make([]string, 0),
|
||||||
|
generatedSchemas: make([]string, 0),
|
||||||
|
linterRulePattern: regexp.MustCompile(`\(-- .* --\)`),
|
||||||
|
namePattern: regexp.MustCompile("{(.*)=(.*)}"),
|
||||||
|
}
|
||||||
|
|
||||||
|
d := og.buildDocumentV3(plugin)
|
||||||
|
|
||||||
|
bytes, err := (jsonpb.MarshalOptions{Indent: " "}).Marshal(d)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to marshal: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
outputFile := og.plugin.NewGeneratedFile(g.openapiFile, "")
|
||||||
|
if _, err := outputFile.Write(bytes); err != nil {
|
||||||
|
return fmt.Errorf("failed to write: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildDocumentV3 builds an OpenAPIv3 document for a plugin request.
|
||||||
|
func (g *openapiv3Generator) buildDocumentV3(plugin *protogen.Plugin) *v3.Document {
|
||||||
|
d := &v3.Document{}
|
||||||
|
d.Openapi = "3.0.3"
|
||||||
|
d.Info = &v3.Info{
|
||||||
|
Title: "",
|
||||||
|
Version: "0.0.1",
|
||||||
|
Description: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, file := range plugin.Files {
|
||||||
|
if !proto.HasExtension(file.Desc.Options(), v3.E_Openapiv3Swagger) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ext := proto.GetExtension(file.Desc.Options(), v3.E_Openapiv3Swagger)
|
||||||
|
if ext == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if doc, ok := ext.(*v3.Document); ok && doc != nil {
|
||||||
|
if doc.Openapi != "" {
|
||||||
|
d.Openapi = doc.Openapi
|
||||||
|
}
|
||||||
|
d.Info = proto.Clone(doc.Info).(*v3.Info)
|
||||||
|
d.Servers = append(d.Servers, doc.Servers...)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
d.Paths = &v3.Paths{}
|
||||||
|
d.Components = &v3.Components{
|
||||||
|
Schemas: &v3.SchemasOrReferences{
|
||||||
|
AdditionalProperties: []*v3.NamedSchemaOrReference{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, file := range g.plugin.Files {
|
||||||
|
g.addPathsToDocumentV3(d, file)
|
||||||
|
}
|
||||||
|
for len(g.requiredSchemas) > 0 {
|
||||||
|
count := len(g.requiredSchemas)
|
||||||
|
for _, file := range g.plugin.Files {
|
||||||
|
g.addSchemasToDocumentV3(d, file)
|
||||||
|
}
|
||||||
|
g.requiredSchemas = g.requiredSchemas[count:len(g.requiredSchemas)]
|
||||||
|
}
|
||||||
|
// Sort the paths.
|
||||||
|
{
|
||||||
|
pairs := d.Paths.Path
|
||||||
|
sort.Slice(pairs, func(i, j int) bool {
|
||||||
|
return pairs[i].Name < pairs[j].Name
|
||||||
|
})
|
||||||
|
d.Paths.Path = pairs
|
||||||
|
}
|
||||||
|
// Sort the schemas.
|
||||||
|
{
|
||||||
|
pairs := d.Components.Schemas.AdditionalProperties
|
||||||
|
sort.Slice(pairs, func(i, j int) bool {
|
||||||
|
return pairs[i].Name < pairs[j].Name
|
||||||
|
})
|
||||||
|
d.Components.Schemas.AdditionalProperties = pairs
|
||||||
|
}
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
// filterCommentString removes line breaks and linter rules from comments.
|
||||||
|
func (g *openapiv3Generator) filterCommentString(c protogen.Comments) string {
|
||||||
|
comment := string(c)
|
||||||
|
comment = strings.Replace(comment, "\n", "", -1)
|
||||||
|
comment = g.linterRulePattern.ReplaceAllString(comment, "")
|
||||||
|
return strings.TrimSpace(comment)
|
||||||
|
}
|
||||||
|
|
||||||
|
// addPathsToDocumentV3 adds paths from a specified file descriptor.
|
||||||
|
func (g *openapiv3Generator) addPathsToDocumentV3(d *v3.Document, file *protogen.File) {
|
||||||
|
for _, service := range file.Services {
|
||||||
|
comment := g.filterCommentString(service.Comments.Leading)
|
||||||
|
if d.Info.Title == "" {
|
||||||
|
d.Info.Title = service.GoName
|
||||||
|
}
|
||||||
|
if d.Info.Description == "" {
|
||||||
|
d.Info.Description = comment
|
||||||
|
}
|
||||||
|
for _, method := range service.Methods {
|
||||||
|
comment := g.filterCommentString(method.Comments.Leading)
|
||||||
|
inputMessage := method.Input
|
||||||
|
outputMessage := method.Output
|
||||||
|
operationID := service.GoName + "_" + method.GoName
|
||||||
|
eopt := proto.GetExtension(method.Desc.Options(), v3.E_Openapiv3Operation)
|
||||||
|
if eopt != nil && eopt != v3.E_Openapiv3Operation.InterfaceOf(v3.E_Openapiv3Operation.Zero()) {
|
||||||
|
opt := eopt.(*v3.Operation)
|
||||||
|
if opt.OperationId != "" {
|
||||||
|
operationID = opt.OperationId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xt := annotations.E_Http
|
||||||
|
extension := proto.GetExtension(method.Desc.Options(), xt)
|
||||||
|
var path string
|
||||||
|
var methodName string
|
||||||
|
var body string
|
||||||
|
if extension != nil && extension != xt.InterfaceOf(xt.Zero()) {
|
||||||
|
rule := extension.(*annotations.HttpRule)
|
||||||
|
body = rule.Body
|
||||||
|
switch pattern := rule.Pattern.(type) {
|
||||||
|
case *annotations.HttpRule_Get:
|
||||||
|
path = pattern.Get
|
||||||
|
methodName = "GET"
|
||||||
|
case *annotations.HttpRule_Post:
|
||||||
|
path = pattern.Post
|
||||||
|
methodName = "POST"
|
||||||
|
case *annotations.HttpRule_Put:
|
||||||
|
path = pattern.Put
|
||||||
|
methodName = "PUT"
|
||||||
|
case *annotations.HttpRule_Delete:
|
||||||
|
path = pattern.Delete
|
||||||
|
methodName = "DELETE"
|
||||||
|
case *annotations.HttpRule_Patch:
|
||||||
|
path = pattern.Patch
|
||||||
|
methodName = "PATCH"
|
||||||
|
case *annotations.HttpRule_Custom:
|
||||||
|
path = "custom-unsupported"
|
||||||
|
default:
|
||||||
|
path = "unknown-unsupported"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if methodName != "" {
|
||||||
|
op, path2 := g.buildOperationV3(
|
||||||
|
file, method, operationID, comment, path, body, inputMessage, outputMessage)
|
||||||
|
g.addOperationV3(d, op, path2, methodName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildOperationV3 constructs an operation for a set of values.
|
||||||
|
func (g *openapiv3Generator) buildOperationV3(
|
||||||
|
file *protogen.File,
|
||||||
|
method *protogen.Method,
|
||||||
|
operationID string,
|
||||||
|
description string,
|
||||||
|
path string,
|
||||||
|
bodyField string,
|
||||||
|
inputMessage *protogen.Message,
|
||||||
|
outputMessage *protogen.Message,
|
||||||
|
) (*v3.Operation, string) {
|
||||||
|
// coveredParameters tracks the parameters that have been used in the body or path.
|
||||||
|
coveredParameters := make([]string, 0)
|
||||||
|
if bodyField != "" {
|
||||||
|
coveredParameters = append(coveredParameters, bodyField)
|
||||||
|
}
|
||||||
|
// Initialize the list of operation parameters.
|
||||||
|
parameters := []*v3.ParameterOrReference{}
|
||||||
|
// Build a list of header parameters.
|
||||||
|
eopt := proto.GetExtension(method.Desc.Options(), v3.E_Openapiv3Operation)
|
||||||
|
if eopt != nil && eopt != v3.E_Openapiv3Operation.InterfaceOf(v3.E_Openapiv3Operation.Zero()) {
|
||||||
|
opt := eopt.(*v3.Operation)
|
||||||
|
parameters = append(parameters, opt.Parameters...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build a list of path parameters.
|
||||||
|
pathParameters := make([]string, 0)
|
||||||
|
if matches := g.namePattern.FindStringSubmatch(path); matches != nil {
|
||||||
|
// Add the "name=" "name" value to the list of covered parameters.
|
||||||
|
coveredParameters = append(coveredParameters, matches[1])
|
||||||
|
// Convert the path from the starred form to use named path parameters.
|
||||||
|
starredPath := matches[2]
|
||||||
|
parts := strings.Split(starredPath, "/")
|
||||||
|
// The starred path is assumed to be in the form "things/*/otherthings/*".
|
||||||
|
// We want to convert it to "things/{thing}/otherthings/{otherthing}".
|
||||||
|
for i := 0; i < len(parts)-1; i += 2 {
|
||||||
|
section := parts[i]
|
||||||
|
parameter := singular(section)
|
||||||
|
parts[i+1] = "{" + parameter + "}"
|
||||||
|
pathParameters = append(pathParameters, parameter)
|
||||||
|
}
|
||||||
|
// Rewrite the path to use the path parameters.
|
||||||
|
newPath := strings.Join(parts, "/")
|
||||||
|
path = strings.Replace(path, matches[0], newPath, 1)
|
||||||
|
}
|
||||||
|
// Add the path parameters to the operation parameters.
|
||||||
|
for _, pathParameter := range pathParameters {
|
||||||
|
parameters = append(parameters,
|
||||||
|
&v3.ParameterOrReference{
|
||||||
|
Oneof: &v3.ParameterOrReference_Parameter{
|
||||||
|
Parameter: &v3.Parameter{
|
||||||
|
Name: pathParameter,
|
||||||
|
In: "path",
|
||||||
|
Required: true,
|
||||||
|
Description: "The " + pathParameter + " id.",
|
||||||
|
Schema: &v3.SchemaOrReference{
|
||||||
|
Oneof: &v3.SchemaOrReference_Schema{
|
||||||
|
Schema: &v3.Schema{
|
||||||
|
Type: "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// Add any unhandled fields in the request message as query parameters.
|
||||||
|
if bodyField != "*" {
|
||||||
|
for _, field := range inputMessage.Fields {
|
||||||
|
fieldName := string(field.Desc.Name())
|
||||||
|
if !contains(coveredParameters, fieldName) {
|
||||||
|
// Get the field description from the comments.
|
||||||
|
fieldDescription := g.filterCommentString(field.Comments.Leading)
|
||||||
|
parameters = append(parameters,
|
||||||
|
&v3.ParameterOrReference{
|
||||||
|
Oneof: &v3.ParameterOrReference_Parameter{
|
||||||
|
Parameter: &v3.Parameter{
|
||||||
|
Name: fieldName,
|
||||||
|
In: "query",
|
||||||
|
Description: fieldDescription,
|
||||||
|
Required: false,
|
||||||
|
Schema: &v3.SchemaOrReference{
|
||||||
|
Oneof: &v3.SchemaOrReference_Schema{
|
||||||
|
Schema: &v3.Schema{
|
||||||
|
Type: "string",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Create the response.
|
||||||
|
responses := &v3.Responses{
|
||||||
|
ResponseOrReference: []*v3.NamedResponseOrReference{
|
||||||
|
&v3.NamedResponseOrReference{
|
||||||
|
Name: "200",
|
||||||
|
Value: &v3.ResponseOrReference{
|
||||||
|
Oneof: &v3.ResponseOrReference_Response{
|
||||||
|
Response: &v3.Response{
|
||||||
|
Description: "OK",
|
||||||
|
Content: g.responseContentForMessage(outputMessage),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
// Create the operation.
|
||||||
|
op := &v3.Operation{
|
||||||
|
Summary: description,
|
||||||
|
OperationId: operationID,
|
||||||
|
Parameters: parameters,
|
||||||
|
Responses: responses,
|
||||||
|
}
|
||||||
|
// If a body field is specified, we need to pass a message as the request body.
|
||||||
|
if bodyField != "" {
|
||||||
|
var bodyFieldScalarTypeName string
|
||||||
|
var bodyFieldMessageTypeName string
|
||||||
|
if bodyField == "*" {
|
||||||
|
// Pass the entire request message as the request body.
|
||||||
|
bodyFieldMessageTypeName = g.fullMessageTypeName(inputMessage)
|
||||||
|
} else {
|
||||||
|
// If body refers to a message field, use that type.
|
||||||
|
for _, field := range inputMessage.Fields {
|
||||||
|
if string(field.Desc.Name()) == bodyField {
|
||||||
|
switch field.Desc.Kind() {
|
||||||
|
case protoreflect.StringKind:
|
||||||
|
bodyFieldScalarTypeName = "string"
|
||||||
|
case protoreflect.MessageKind:
|
||||||
|
bodyFieldMessageTypeName = g.fullMessageTypeName(field.Message)
|
||||||
|
default:
|
||||||
|
log.Printf("unsupported field type %+v", field.Desc)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var requestSchema *v3.SchemaOrReference
|
||||||
|
if bodyFieldScalarTypeName != "" {
|
||||||
|
requestSchema = &v3.SchemaOrReference{
|
||||||
|
Oneof: &v3.SchemaOrReference_Schema{
|
||||||
|
Schema: &v3.Schema{
|
||||||
|
Type: bodyFieldScalarTypeName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else if bodyFieldMessageTypeName != "" {
|
||||||
|
requestSchema = &v3.SchemaOrReference{
|
||||||
|
Oneof: &v3.SchemaOrReference_Reference{
|
||||||
|
Reference: &v3.Reference{
|
||||||
|
XRef: g.schemaReferenceForTypeName(bodyFieldMessageTypeName),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
op.RequestBody = &v3.RequestBodyOrReference{
|
||||||
|
Oneof: &v3.RequestBodyOrReference_RequestBody{
|
||||||
|
RequestBody: &v3.RequestBody{
|
||||||
|
Required: true,
|
||||||
|
Content: &v3.MediaTypes{
|
||||||
|
AdditionalProperties: []*v3.NamedMediaType{
|
||||||
|
&v3.NamedMediaType{
|
||||||
|
Name: "application/json",
|
||||||
|
Value: &v3.MediaType{
|
||||||
|
Schema: requestSchema,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return op, path
|
||||||
|
}
|
||||||
|
|
||||||
|
// addOperationV3 adds an operation to the specified path/method.
|
||||||
|
func (g *openapiv3Generator) addOperationV3(d *v3.Document, op *v3.Operation, path string, methodName string) {
|
||||||
|
var selectedPathItem *v3.NamedPathItem
|
||||||
|
for _, namedPathItem := range d.Paths.Path {
|
||||||
|
if namedPathItem.Name == path {
|
||||||
|
selectedPathItem = namedPathItem
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If we get here, we need to create a path item.
|
||||||
|
if selectedPathItem == nil {
|
||||||
|
selectedPathItem = &v3.NamedPathItem{Name: path, Value: &v3.PathItem{}}
|
||||||
|
d.Paths.Path = append(d.Paths.Path, selectedPathItem)
|
||||||
|
}
|
||||||
|
// Set the operation on the specified method.
|
||||||
|
switch methodName {
|
||||||
|
case http.MethodGet:
|
||||||
|
selectedPathItem.Value.Get = op
|
||||||
|
case http.MethodPost:
|
||||||
|
selectedPathItem.Value.Post = op
|
||||||
|
case http.MethodPut:
|
||||||
|
selectedPathItem.Value.Put = op
|
||||||
|
case http.MethodDelete:
|
||||||
|
selectedPathItem.Value.Delete = op
|
||||||
|
case http.MethodPatch:
|
||||||
|
selectedPathItem.Value.Patch = op
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// schemaReferenceForTypeName returns an OpenAPI JSON Reference to the schema that represents a type.
|
||||||
|
func (g *openapiv3Generator) schemaReferenceForTypeName(typeName string) string {
|
||||||
|
if !contains(g.requiredSchemas, typeName) {
|
||||||
|
g.requiredSchemas = append(g.requiredSchemas, typeName)
|
||||||
|
}
|
||||||
|
parts := strings.Split(typeName, ".")
|
||||||
|
lastPart := parts[len(parts)-1]
|
||||||
|
return "#/components/schemas/" + lastPart
|
||||||
|
}
|
||||||
|
|
||||||
|
// itemsItemForTypeName is a helper constructor.
|
||||||
|
func (g *openapiv3Generator) itemsItemForTypeName(typeName string) *v3.ItemsItem {
|
||||||
|
return &v3.ItemsItem{SchemaOrReference: []*v3.SchemaOrReference{&v3.SchemaOrReference{
|
||||||
|
Oneof: &v3.SchemaOrReference_Schema{
|
||||||
|
Schema: &v3.Schema{
|
||||||
|
Type: typeName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// itemsItemForReference is a helper constructor.
|
||||||
|
func (g *openapiv3Generator) itemsItemForReference(xref string) *v3.ItemsItem {
|
||||||
|
return &v3.ItemsItem{SchemaOrReference: []*v3.SchemaOrReference{&v3.SchemaOrReference{
|
||||||
|
Oneof: &v3.SchemaOrReference_Reference{
|
||||||
|
Reference: &v3.Reference{
|
||||||
|
XRef: xref,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fullMessageTypeName builds the full type name of a message.
|
||||||
|
func (g *openapiv3Generator) fullMessageTypeName(message *protogen.Message) string {
|
||||||
|
return "." + string(message.Desc.ParentFile().Package()) + "." + string(message.Desc.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *openapiv3Generator) responseContentForMessage(outputMessage *protogen.Message) *v3.MediaTypes {
|
||||||
|
typeName := g.fullMessageTypeName(outputMessage)
|
||||||
|
|
||||||
|
if typeName == ".google.protobuf.Empty" {
|
||||||
|
return &v3.MediaTypes{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if typeName == ".google.api.HttpBody" || typeName == ".micro.codec.Frame" {
|
||||||
|
return &v3.MediaTypes{
|
||||||
|
AdditionalProperties: []*v3.NamedMediaType{
|
||||||
|
&v3.NamedMediaType{
|
||||||
|
Name: "application/octet-stream",
|
||||||
|
Value: &v3.MediaType{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &v3.MediaTypes{
|
||||||
|
AdditionalProperties: []*v3.NamedMediaType{
|
||||||
|
&v3.NamedMediaType{
|
||||||
|
Name: "application/json",
|
||||||
|
Value: &v3.MediaType{
|
||||||
|
Schema: &v3.SchemaOrReference{
|
||||||
|
Oneof: &v3.SchemaOrReference_Reference{
|
||||||
|
Reference: &v3.Reference{
|
||||||
|
XRef: g.schemaReferenceForTypeName(g.fullMessageTypeName(outputMessage)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// addSchemasToDocumentV3 adds info from one file descriptor.
|
||||||
|
func (g *openapiv3Generator) addSchemasToDocumentV3(d *v3.Document, file *protogen.File) {
|
||||||
|
// For each message, generate a definition.
|
||||||
|
for _, message := range file.Messages {
|
||||||
|
typeName := g.fullMessageTypeName(message)
|
||||||
|
// Only generate this if we need it and haven't already generated it.
|
||||||
|
if !contains(g.requiredSchemas, typeName) ||
|
||||||
|
contains(g.generatedSchemas, typeName) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
g.generatedSchemas = append(g.generatedSchemas, typeName)
|
||||||
|
// Get the message description from the comments.
|
||||||
|
messageDescription := g.filterCommentString(message.Comments.Leading)
|
||||||
|
// Build an array holding the fields of the message.
|
||||||
|
definitionProperties := &v3.Properties{
|
||||||
|
AdditionalProperties: make([]*v3.NamedSchemaOrReference, 0),
|
||||||
|
}
|
||||||
|
for _, field := range message.Fields {
|
||||||
|
// Check the field annotations to see if this is a readonly field.
|
||||||
|
outputOnly := false
|
||||||
|
extension := proto.GetExtension(field.Desc.Options(), annotations.E_FieldBehavior)
|
||||||
|
if extension != nil {
|
||||||
|
switch v := extension.(type) {
|
||||||
|
case []annotations.FieldBehavior:
|
||||||
|
for _, vv := range v {
|
||||||
|
if vv == annotations.FieldBehavior_OUTPUT_ONLY {
|
||||||
|
outputOnly = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
log.Printf("unsupported extension type %T", extension)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Get the field description from the comments.
|
||||||
|
fieldDescription := g.filterCommentString(field.Comments.Leading)
|
||||||
|
// The field is either described by a reference or a schema.
|
||||||
|
XRef := ""
|
||||||
|
fieldSchema := &v3.Schema{
|
||||||
|
Description: fieldDescription,
|
||||||
|
}
|
||||||
|
if outputOnly {
|
||||||
|
fieldSchema.ReadOnly = true
|
||||||
|
}
|
||||||
|
if field.Desc.IsList() {
|
||||||
|
fieldSchema.Type = "array"
|
||||||
|
switch field.Desc.Kind() {
|
||||||
|
case protoreflect.MessageKind:
|
||||||
|
fieldSchema.Items = g.itemsItemForReference(
|
||||||
|
g.schemaReferenceForTypeName(
|
||||||
|
g.fullMessageTypeName(field.Message)))
|
||||||
|
case protoreflect.StringKind:
|
||||||
|
fieldSchema.Items = g.itemsItemForTypeName("string")
|
||||||
|
case protoreflect.Int32Kind,
|
||||||
|
protoreflect.Sint32Kind,
|
||||||
|
protoreflect.Uint32Kind,
|
||||||
|
protoreflect.Int64Kind,
|
||||||
|
protoreflect.Sint64Kind,
|
||||||
|
protoreflect.Uint64Kind,
|
||||||
|
protoreflect.Sfixed32Kind,
|
||||||
|
protoreflect.Fixed32Kind,
|
||||||
|
protoreflect.Sfixed64Kind,
|
||||||
|
protoreflect.Fixed64Kind:
|
||||||
|
fieldSchema.Items = g.itemsItemForTypeName("integer")
|
||||||
|
case protoreflect.EnumKind:
|
||||||
|
fieldSchema.Items = g.itemsItemForTypeName("integer")
|
||||||
|
case protoreflect.BoolKind:
|
||||||
|
fieldSchema.Items = g.itemsItemForTypeName("boolean")
|
||||||
|
case protoreflect.FloatKind, protoreflect.DoubleKind:
|
||||||
|
fieldSchema.Items = g.itemsItemForTypeName("number")
|
||||||
|
case protoreflect.BytesKind:
|
||||||
|
fieldSchema.Items = g.itemsItemForTypeName("string")
|
||||||
|
default:
|
||||||
|
log.Printf("(TODO) Unsupported array type: %+v", g.fullMessageTypeName(field.Message))
|
||||||
|
}
|
||||||
|
} else if field.Desc.IsMap() &&
|
||||||
|
field.Desc.MapKey().Kind() == protoreflect.StringKind &&
|
||||||
|
field.Desc.MapValue().Kind() == protoreflect.StringKind {
|
||||||
|
fieldSchema.Type = "object"
|
||||||
|
} else {
|
||||||
|
k := field.Desc.Kind()
|
||||||
|
switch k {
|
||||||
|
case protoreflect.MessageKind:
|
||||||
|
typeName := g.fullMessageTypeName(field.Message)
|
||||||
|
switch typeName {
|
||||||
|
case ".google.protobuf.Timestamp":
|
||||||
|
// Timestamps are serialized as strings
|
||||||
|
fieldSchema.Type = "string"
|
||||||
|
fieldSchema.Format = "RFC3339"
|
||||||
|
case ".google.type.Date":
|
||||||
|
// Dates are serialized as strings
|
||||||
|
fieldSchema.Type = "string"
|
||||||
|
fieldSchema.Format = "date"
|
||||||
|
case ".google.type.DateTime":
|
||||||
|
// DateTimes are serialized as strings
|
||||||
|
fieldSchema.Type = "string"
|
||||||
|
fieldSchema.Format = "date-time"
|
||||||
|
default:
|
||||||
|
// The field is described by a reference.
|
||||||
|
XRef = g.schemaReferenceForTypeName(typeName)
|
||||||
|
}
|
||||||
|
case protoreflect.StringKind:
|
||||||
|
fieldSchema.Type = "string"
|
||||||
|
case protoreflect.Int32Kind,
|
||||||
|
protoreflect.Sint32Kind,
|
||||||
|
protoreflect.Uint32Kind,
|
||||||
|
protoreflect.Int64Kind,
|
||||||
|
protoreflect.Sint64Kind,
|
||||||
|
protoreflect.Uint64Kind,
|
||||||
|
protoreflect.Sfixed32Kind,
|
||||||
|
protoreflect.Fixed32Kind,
|
||||||
|
protoreflect.Sfixed64Kind,
|
||||||
|
protoreflect.Fixed64Kind:
|
||||||
|
fieldSchema.Type = "integer"
|
||||||
|
fieldSchema.Format = k.String()
|
||||||
|
case protoreflect.EnumKind:
|
||||||
|
fieldSchema.Type = "integer"
|
||||||
|
fieldSchema.Format = "enum"
|
||||||
|
case protoreflect.BoolKind:
|
||||||
|
fieldSchema.Type = "boolean"
|
||||||
|
case protoreflect.FloatKind, protoreflect.DoubleKind:
|
||||||
|
fieldSchema.Type = "number"
|
||||||
|
fieldSchema.Format = k.String()
|
||||||
|
case protoreflect.BytesKind:
|
||||||
|
fieldSchema.Type = "string"
|
||||||
|
fieldSchema.Format = "bytes"
|
||||||
|
default:
|
||||||
|
log.Printf("(TODO) Unsupported field type: %+v", g.fullMessageTypeName(field.Message))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var value *v3.SchemaOrReference
|
||||||
|
if XRef != "" {
|
||||||
|
value = &v3.SchemaOrReference{
|
||||||
|
Oneof: &v3.SchemaOrReference_Reference{
|
||||||
|
Reference: &v3.Reference{
|
||||||
|
XRef: XRef,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
value = &v3.SchemaOrReference{
|
||||||
|
Oneof: &v3.SchemaOrReference_Schema{
|
||||||
|
Schema: fieldSchema,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
definitionProperties.AdditionalProperties = append(
|
||||||
|
definitionProperties.AdditionalProperties,
|
||||||
|
&v3.NamedSchemaOrReference{
|
||||||
|
Name: string(field.Desc.Name()),
|
||||||
|
Value: value,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// Add the schema to the components.schema list.
|
||||||
|
d.Components.Schemas.AdditionalProperties = append(d.Components.Schemas.AdditionalProperties,
|
||||||
|
&v3.NamedSchemaOrReference{
|
||||||
|
Name: string(message.Desc.Name()),
|
||||||
|
Value: &v3.SchemaOrReference{
|
||||||
|
Oneof: &v3.SchemaOrReference_Schema{
|
||||||
|
Schema: &v3.Schema{
|
||||||
|
Description: messageDescription,
|
||||||
|
Properties: definitionProperties,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// contains returns true if an array contains a specified string.
|
||||||
|
func contains(s []string, e string) bool {
|
||||||
|
for _, a := range s {
|
||||||
|
if a == e {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// singular produces the singular form of a collection name.
|
||||||
|
func singular(plural string) string {
|
||||||
|
if strings.HasSuffix(plural, "ves") {
|
||||||
|
return strings.TrimSuffix(plural, "ves") + "f"
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(plural, "ies") {
|
||||||
|
return strings.TrimSuffix(plural, "ies") + "y"
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(plural, "s") {
|
||||||
|
return strings.TrimSuffix(plural, "s")
|
||||||
|
}
|
||||||
|
return plural
|
||||||
|
}
|
Binary file not shown.
118
util.go
118
util.go
@@ -2,27 +2,27 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
api_options "github.com/unistack-org/micro-proto/api"
|
api_options "go.unistack.org/micro-proto/v3/api"
|
||||||
openapiv2_options "github.com/unistack-org/micro-proto/openapiv2"
|
v2 "go.unistack.org/micro-proto/v3/openapiv2"
|
||||||
|
v3 "go.unistack.org/micro-proto/v3/openapiv3"
|
||||||
"google.golang.org/protobuf/compiler/protogen"
|
"google.golang.org/protobuf/compiler/protogen"
|
||||||
"google.golang.org/protobuf/proto"
|
"google.golang.org/protobuf/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var httpMethodMap = map[string]string{
|
||||||
httpMethodMap = map[string]string{
|
http.MethodGet: "MethodGet",
|
||||||
"GET": "MethodGet",
|
http.MethodHead: "MethodHead",
|
||||||
"HEAD": "MethodHead",
|
http.MethodPost: "MethodPost",
|
||||||
"POST": "MethodPost",
|
http.MethodPut: "MethodPut",
|
||||||
"PUT": "MethodPut",
|
http.MethodPatch: "MethodPatch",
|
||||||
"PATCH": "MethodPatch",
|
http.MethodDelete: "MethodDelete",
|
||||||
"DELETE": "MethodDelete",
|
http.MethodConnect: "MethodConnect",
|
||||||
"CONNECT": "MethodConnect",
|
http.MethodOptions: "MethodOptions",
|
||||||
"OPTIONS": "MethodOptions",
|
http.MethodTrace: "MethodTrace",
|
||||||
"TRACE": "MethodTrace",
|
}
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func unexport(s string) string {
|
func unexport(s string) string {
|
||||||
return strings.ToLower(s[:1]) + s[1:]
|
return strings.ToLower(s[:1]) + s[1:]
|
||||||
@@ -30,7 +30,7 @@ func unexport(s string) string {
|
|||||||
|
|
||||||
func generateServiceClient(gfile *protogen.GeneratedFile, service *protogen.Service) {
|
func generateServiceClient(gfile *protogen.GeneratedFile, service *protogen.Service) {
|
||||||
serviceName := service.GoName
|
serviceName := service.GoName
|
||||||
//if rule, ok := getMicroApiService(service); ok {
|
// if rule, ok := getMicroApiService(service); ok {
|
||||||
// gfile.P("// client wrappers ", strings.Join(rule.ClientWrappers, ", "))
|
// gfile.P("// client wrappers ", strings.Join(rule.ClientWrappers, ", "))
|
||||||
// }
|
// }
|
||||||
gfile.P("type ", unexport(serviceName), "Client struct {")
|
gfile.P("type ", unexport(serviceName), "Client struct {")
|
||||||
@@ -51,10 +51,10 @@ func generateServiceClientMethods(gfile *protogen.GeneratedFile, service *protog
|
|||||||
generateClientFuncSignature(gfile, serviceName, method)
|
generateClientFuncSignature(gfile, serviceName, method)
|
||||||
|
|
||||||
if http && method.Desc.Options() != nil {
|
if http && method.Desc.Options() != nil {
|
||||||
if proto.HasExtension(method.Desc.Options(), openapiv2_options.E_Openapiv2Operation) {
|
if proto.HasExtension(method.Desc.Options(), v2.E_Openapiv2Operation) {
|
||||||
opts := proto.GetExtension(method.Desc.Options(), openapiv2_options.E_Openapiv2Operation)
|
opts := proto.GetExtension(method.Desc.Options(), v2.E_Openapiv2Operation)
|
||||||
if opts != nil {
|
if opts != nil {
|
||||||
r := opts.(*openapiv2_options.Operation)
|
r := opts.(*v2.Operation)
|
||||||
gfile.P("errmap := make(map[string]interface{}, ", len(r.Responses.ResponseCode), ")")
|
gfile.P("errmap := make(map[string]interface{}, ", len(r.Responses.ResponseCode), ")")
|
||||||
for _, rsp := range r.Responses.ResponseCode {
|
for _, rsp := range r.Responses.ResponseCode {
|
||||||
if schema := rsp.Value.GetJsonReference(); schema != nil {
|
if schema := rsp.Value.GetJsonReference(); schema != nil {
|
||||||
@@ -62,15 +62,19 @@ func generateServiceClientMethods(gfile *protogen.GeneratedFile, service *protog
|
|||||||
if strings.HasPrefix(ref, "."+string(service.Desc.ParentFile().Package())+".") {
|
if strings.HasPrefix(ref, "."+string(service.Desc.ParentFile().Package())+".") {
|
||||||
ref = strings.TrimPrefix(ref, "."+string(service.Desc.ParentFile().Package())+".")
|
ref = strings.TrimPrefix(ref, "."+string(service.Desc.ParentFile().Package())+".")
|
||||||
}
|
}
|
||||||
gfile.P(`errmap["`, rsp.Name, `"] = &`, ref, "{}")
|
if ref == "micro.codec.Frame" || ref == ".micro.codec.Frame" {
|
||||||
|
gfile.P(`errmap["`, rsp.Name, `"] = &`, microCodecPackage.Ident("Frame"), "{}")
|
||||||
|
} else {
|
||||||
|
gfile.P(`errmap["`, rsp.Name, `"] = &`, ref, "{}")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
gfile.P("opts = append(opts,")
|
gfile.P("opts = append(opts,")
|
||||||
gfile.P(microClientHttpPackage.Ident("ErrorMap"), "(errmap),")
|
gfile.P(microClientHttpPackage.Ident("ErrorMap"), "(errmap),")
|
||||||
gfile.P(")")
|
gfile.P(")")
|
||||||
}
|
}
|
||||||
|
|
||||||
if proto.HasExtension(method.Desc.Options(), api_options.E_Http) {
|
if proto.HasExtension(method.Desc.Options(), api_options.E_Http) {
|
||||||
gfile.P("opts = append(opts,")
|
gfile.P("opts = append(opts,")
|
||||||
endpoints, _ := generateEndpoints(method)
|
endpoints, _ := generateEndpoints(method)
|
||||||
@@ -86,7 +90,42 @@ func generateServiceClientMethods(gfile *protogen.GeneratedFile, service *protog
|
|||||||
}
|
}
|
||||||
gfile.P(")")
|
gfile.P(")")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parameters := make(map[string]map[string]string)
|
||||||
|
// Build a list of header parameters.
|
||||||
|
eopt := proto.GetExtension(method.Desc.Options(), v3.E_Openapiv3Operation)
|
||||||
|
if eopt != nil && eopt != v3.E_Openapiv3Operation.InterfaceOf(v3.E_Openapiv3Operation.Zero()) {
|
||||||
|
opt := eopt.(*v3.Operation)
|
||||||
|
for _, paramOrRef := range opt.Parameters {
|
||||||
|
parameter := paramOrRef.GetParameter()
|
||||||
|
if parameter == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if parameter.In != "header" && parameter.In != "cookie" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
in, ok := parameters[parameter.In]
|
||||||
|
if !ok {
|
||||||
|
in = make(map[string]string)
|
||||||
|
parameters[parameter.In] = in
|
||||||
|
}
|
||||||
|
in[parameter.Name] = fmt.Sprintf("%v", parameter.Required)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(parameters) > 0 {
|
||||||
|
gfile.P("opts = append(opts,")
|
||||||
|
for pk, pv := range parameters {
|
||||||
|
params := make([]string, 0, len(pv)/2)
|
||||||
|
for k, v := range pv {
|
||||||
|
params = append(params, k, v)
|
||||||
|
}
|
||||||
|
gfile.P(microClientHttpPackage.Ident(strings.Title(pk)), `("`, strings.Join(params, `" ,"`), `"),`)
|
||||||
|
}
|
||||||
|
gfile.P(")")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if rule, ok := getMicroApiMethod(method); ok {
|
if rule, ok := getMicroApiMethod(method); ok {
|
||||||
if rule.Timeout > 0 {
|
if rule.Timeout > 0 {
|
||||||
gfile.P("opts = append(opts, ", microClientPackage.Ident("WithRequestTimeout"), "(", timePackage.Ident("Second"), "*", rule.Timeout, "))")
|
gfile.P("opts = append(opts, ", microClientPackage.Ident("WithRequestTimeout"), "(", timePackage.Ident("Second"), "*", rule.Timeout, "))")
|
||||||
@@ -207,6 +246,39 @@ func generateServiceServerMethods(gfile *protogen.GeneratedFile, service *protog
|
|||||||
gfile.P("return h.", serviceName, "Server.", method.GoName, "(ctx, &", unexport(serviceName), method.GoName, "Stream{stream})")
|
gfile.P("return h.", serviceName, "Server.", method.GoName, "(ctx, &", unexport(serviceName), method.GoName, "Stream{stream})")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
parameters := make(map[string]map[string]string)
|
||||||
|
// Build a list of header parameters.
|
||||||
|
eopt := proto.GetExtension(method.Desc.Options(), v3.E_Openapiv3Operation)
|
||||||
|
if eopt != nil && eopt != v3.E_Openapiv3Operation.InterfaceOf(v3.E_Openapiv3Operation.Zero()) {
|
||||||
|
opt := eopt.(*v3.Operation)
|
||||||
|
for _, paramOrRef := range opt.Parameters {
|
||||||
|
parameter := paramOrRef.GetParameter()
|
||||||
|
if parameter == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if parameter.In != "header" && parameter.In != "cookie" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
in, ok := parameters[parameter.In]
|
||||||
|
if !ok {
|
||||||
|
in = make(map[string]string)
|
||||||
|
parameters[parameter.In] = in
|
||||||
|
}
|
||||||
|
in[parameter.Name] = fmt.Sprintf("%v", parameter.Required)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(parameters) > 0 {
|
||||||
|
gfile.P(microServerHttpPackage.Ident("FillRequest"), `(req, `)
|
||||||
|
for pk, pv := range parameters {
|
||||||
|
params := make([]string, 0, len(pv)/2)
|
||||||
|
for k, v := range pv {
|
||||||
|
params = append(params, k, v)
|
||||||
|
}
|
||||||
|
gfile.P(microServerHttpPackage.Ident(strings.Title(pk)), `("`, strings.Join(params, `" ,"`), `"),`)
|
||||||
|
}
|
||||||
|
gfile.P(")")
|
||||||
|
}
|
||||||
gfile.P("return h.", serviceName, "Server.", method.GoName, "(ctx, req, rsp)")
|
gfile.P("return h.", serviceName, "Server.", method.GoName, "(ctx, req, rsp)")
|
||||||
}
|
}
|
||||||
gfile.P("}")
|
gfile.P("}")
|
||||||
@@ -459,7 +531,7 @@ func generateServiceEndpoints(gfile *protogen.GeneratedFile, service *protogen.S
|
|||||||
if proto.HasExtension(method.Desc.Options(), api_options.E_Http) {
|
if proto.HasExtension(method.Desc.Options(), api_options.E_Http) {
|
||||||
endpoints, streaming := generateEndpoints(method)
|
endpoints, streaming := generateEndpoints(method)
|
||||||
for _, endpoint := range endpoints {
|
for _, endpoint := range endpoints {
|
||||||
gfile.P(microApiPackage.Ident("Endpoint"), "{")
|
gfile.P("{")
|
||||||
generateEndpoint(gfile, serviceName, method.GoName, endpoint, streaming)
|
generateEndpoint(gfile, serviceName, method.GoName, endpoint, streaming)
|
||||||
gfile.P("},")
|
gfile.P("},")
|
||||||
}
|
}
|
||||||
@@ -579,6 +651,4 @@ func generateEndpoint(gfile *protogen.GeneratedFile, serviceName string, methodN
|
|||||||
gfile.P("Stream: true,")
|
gfile.P("Stream: true,")
|
||||||
}
|
}
|
||||||
gfile.P(`Handler: "rpc",`)
|
gfile.P(`Handler: "rpc",`)
|
||||||
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
12
variables.go
12
variables.go
@@ -11,11 +11,13 @@ var (
|
|||||||
gorillaMuxPackage = protogen.GoImportPath("github.com/gorilla/mux")
|
gorillaMuxPackage = protogen.GoImportPath("github.com/gorilla/mux")
|
||||||
chiPackage = protogen.GoImportPath("github.com/go-chi/chi/v5")
|
chiPackage = protogen.GoImportPath("github.com/go-chi/chi/v5")
|
||||||
chiMiddlewarePackage = protogen.GoImportPath("github.com/go-chi/chi/v5/middleware")
|
chiMiddlewarePackage = protogen.GoImportPath("github.com/go-chi/chi/v5/middleware")
|
||||||
microApiPackage = protogen.GoImportPath("github.com/unistack-org/micro/v3/api")
|
microApiPackage = protogen.GoImportPath("go.unistack.org/micro/v3/api")
|
||||||
microClientPackage = protogen.GoImportPath("github.com/unistack-org/micro/v3/client")
|
microClientPackage = protogen.GoImportPath("go.unistack.org/micro/v3/client")
|
||||||
microServerPackage = protogen.GoImportPath("github.com/unistack-org/micro/v3/server")
|
microServerPackage = protogen.GoImportPath("go.unistack.org/micro/v3/server")
|
||||||
microClientHttpPackage = protogen.GoImportPath("github.com/unistack-org/micro-client-http/v3")
|
microClientHttpPackage = protogen.GoImportPath("go.unistack.org/micro-client-http/v3")
|
||||||
|
microServerHttpPackage = protogen.GoImportPath("go.unistack.org/micro-server-http/v3")
|
||||||
|
microCodecPackage = protogen.GoImportPath("go.unistack.org/micro/v3/codec")
|
||||||
timePackage = protogen.GoImportPath("time")
|
timePackage = protogen.GoImportPath("time")
|
||||||
deprecationComment = "// Deprecated: Do not use."
|
deprecationComment = "// Deprecated: Do not use."
|
||||||
versionComment = "v3.4.2"
|
versionComment = "v3.5.3"
|
||||||
)
|
)
|
||||||
|
Reference in New Issue
Block a user