grpc-rest 0.1.24 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/CI.yml +2 -41
- data/.rubocop.yml +3 -0
- data/CHANGELOG +6 -1
- data/Gemfile +3 -1
- data/Gemfile.lock +21 -10
- data/README.md +7 -8
- data/bin/protoc-gen-rails +6 -0
- data/grpc-rest.gemspec +4 -1
- data/lib/generator/controller.rb.erb +27 -0
- data/lib/generator/grpc.rb.erb +9 -0
- data/lib/generator/method.rb +96 -0
- data/lib/generator/output.rb +28 -0
- data/lib/generator/plugin_pb.rb +25 -0
- data/lib/generator/service.rb +36 -0
- data/lib/generator.rb +88 -0
- data/lib/grpc_rest/version.rb +3 -1
- data/lib/grpc_rest.rb +40 -38
- data/{protoc-gen-rails/testdata/base/app/controllers/my_service_controller.rb → spec/__snapshots__/service.snap} +33 -27
- data/spec/base_interceptor_spec.rb +6 -3
- data/spec/{protoc-gen-openapiv2 → gen/protoc-gen-openapiv2}/options/annotations_pb.rb +5 -4
- data/spec/{protoc-gen-openapiv2 → gen/protoc-gen-openapiv2}/options/openapiv2_pb.rb +30 -29
- data/spec/gen/test_pb.rb +15 -0
- data/spec/gen/test_service_pb.rb +23 -0
- data/spec/gen/test_service_services_pb.rb +26 -0
- data/spec/generator_spec.rb +49 -0
- data/spec/grpc_rest_spec.rb +71 -25
- data/spec/gruf_spec.rb +4 -3
- data/spec/spec_helper.rb +8 -6
- data/spec/test_service_pb.rb +1 -1
- data/spec/test_service_services_pb.rb +2 -1
- data/spec/testdata/base/app/controllers/my_service_controller.rb +65 -0
- data/spec/testdata/no_service/.keep +0 -0
- data/{protoc-gen-rails → spec}/testdata/test_service.proto +1 -0
- metadata +90 -26
- data/protoc-gen-rails/.goreleaser.yml +0 -27
- data/protoc-gen-rails/LICENSE +0 -21
- data/protoc-gen-rails/go.mod +0 -25
- data/protoc-gen-rails/go.sum +0 -49
- data/protoc-gen-rails/internal/output.go +0 -165
- data/protoc-gen-rails/internal/parse.go +0 -101
- data/protoc-gen-rails/internal/utils.go +0 -57
- data/protoc-gen-rails/main.go +0 -104
- data/protoc-gen-rails/main_test.go +0 -158
- /data/{protoc-gen-rails/testdata/no_service/.keep → spec/__snapshots__/no_service.snap} +0 -0
- /data/{protoc-gen-rails → spec}/google-deps/google/api/annotations.proto +0 -0
- /data/{protoc-gen-rails → spec}/google-deps/google/api/http.proto +0 -0
- /data/{protoc-gen-rails → spec}/google-deps/protoc-gen-openapiv2/options/annotations.proto +0 -0
- /data/{protoc-gen-rails → spec}/google-deps/protoc-gen-openapiv2/options/openapiv2.proto +0 -0
- /data/{protoc-gen-rails → spec}/testdata/base/config/routes/grpc.rb +0 -0
- /data/{protoc-gen-rails → spec}/testdata/test.proto +0 -0
@@ -1,165 +0,0 @@
|
|
1
|
-
package internal
|
2
|
-
|
3
|
-
import (
|
4
|
-
"bytes"
|
5
|
-
"fmt"
|
6
|
-
"strings"
|
7
|
-
"text/template"
|
8
|
-
|
9
|
-
"github.com/iancoleman/strcase"
|
10
|
-
"google.golang.org/protobuf/types/descriptorpb"
|
11
|
-
)
|
12
|
-
|
13
|
-
type FileResult struct {
|
14
|
-
Name string
|
15
|
-
Content string
|
16
|
-
}
|
17
|
-
|
18
|
-
type controller struct {
|
19
|
-
ControllerName string
|
20
|
-
Methods []method
|
21
|
-
ServiceName string
|
22
|
-
FullServiceName string
|
23
|
-
MethodName string
|
24
|
-
}
|
25
|
-
|
26
|
-
type method struct {
|
27
|
-
Name string
|
28
|
-
RequestType string
|
29
|
-
Path string
|
30
|
-
PathInfo []PathInfo
|
31
|
-
Body string
|
32
|
-
HttpMethod string
|
33
|
-
RestOptions string
|
34
|
-
}
|
35
|
-
|
36
|
-
func rubyRestOptions(restOptions map[string]bool) string {
|
37
|
-
var opts []string
|
38
|
-
for opt, val := range restOptions {
|
39
|
-
if val {
|
40
|
-
opts = append(opts, fmt.Sprintf("%v: true", opt))
|
41
|
-
}
|
42
|
-
}
|
43
|
-
return "{" + strings.Join(opts, ", ") + "}"
|
44
|
-
}
|
45
|
-
|
46
|
-
type Route struct {
|
47
|
-
MethodName string
|
48
|
-
Path string
|
49
|
-
Controller string
|
50
|
-
HttpMethod string
|
51
|
-
}
|
52
|
-
|
53
|
-
var controllerTemplate = `
|
54
|
-
####### THIS FILE IS AUTOMATICALLY GENERATED BY protoc-gen-rails. DO NOT MODIFY. #######
|
55
|
-
|
56
|
-
require 'grpc_rest'
|
57
|
-
class {{.ControllerName}}Controller < ActionController::Base
|
58
|
-
protect_from_forgery with: :null_session
|
59
|
-
|
60
|
-
rescue_from StandardError do |e|
|
61
|
-
render json: GrpcRest.error_msg(e), status: :internal_server_error
|
62
|
-
end
|
63
|
-
rescue_from GRPC::BadStatus do |e|
|
64
|
-
render json: GrpcRest.error_msg(e), status: GrpcRest.grpc_http_status(e.code)
|
65
|
-
end
|
66
|
-
rescue_from ActionDispatch::Http::Parameters::ParseError, Google::Protobuf::TypeError do |e|
|
67
|
-
render json: GrpcRest.error_msg(e), status: :bad_request
|
68
|
-
end
|
69
|
-
METHOD_PARAM_MAP = {
|
70
|
-
{{range .Methods }}
|
71
|
-
"{{.Name}}" => [
|
72
|
-
{{range .PathInfo -}}
|
73
|
-
{name: "{{.Name}}", val: {{if .HasValPattern}}"{{.ValPattern}}"{{else}}nil{{end}}, split_name:{{.SplitName}}},
|
74
|
-
{{end -}}
|
75
|
-
],
|
76
|
-
{{end -}}
|
77
|
-
}.freeze
|
78
|
-
{{$fullServiceName := .FullServiceName -}}
|
79
|
-
{{range .Methods }}
|
80
|
-
def {{.Name}}
|
81
|
-
fields = {{.RequestType}}.descriptor.to_a.map(&:name)
|
82
|
-
parameters = request.parameters.to_h.deep_transform_keys(&:underscore)
|
83
|
-
grpc_request = GrpcRest.init_request({{.RequestType}}, parameters.slice(*fields))
|
84
|
-
GrpcRest.assign_params(grpc_request, METHOD_PARAM_MAP["{{.Name}}"], "{{.Body}}", request.parameters)
|
85
|
-
render json: GrpcRest.send_request("{{$fullServiceName}}", "{{.Name}}", grpc_request, {{.RestOptions}})
|
86
|
-
end
|
87
|
-
{{end}}
|
88
|
-
end
|
89
|
-
`
|
90
|
-
|
91
|
-
func ProcessService(service *descriptorpb.ServiceDescriptorProto, pkg string) (FileResult, []Route, error) {
|
92
|
-
var routes []Route
|
93
|
-
data := controller{
|
94
|
-
Methods: []method{},
|
95
|
-
ServiceName: Classify(service.GetName()),
|
96
|
-
ControllerName: Demodulize(service.GetName()),
|
97
|
-
FullServiceName: Classify(pkg + "." + service.GetName()),
|
98
|
-
}
|
99
|
-
for _, m := range service.GetMethod() {
|
100
|
-
opts, err := ExtractAPIOptions(m)
|
101
|
-
if err != nil {
|
102
|
-
return FileResult{}, routes, err
|
103
|
-
}
|
104
|
-
restOpts, err := ExtractRestOptions(m)
|
105
|
-
if err != nil {
|
106
|
-
return FileResult{}, routes, err
|
107
|
-
}
|
108
|
-
httpMethod, path, err := MethodAndPath(opts.Pattern)
|
109
|
-
pathInfo, err := ParsedPath(path)
|
110
|
-
if err != nil {
|
111
|
-
return FileResult{}, routes, err
|
112
|
-
}
|
113
|
-
controllerMethod := method{
|
114
|
-
Name: strcase.ToSnake(m.GetName()),
|
115
|
-
RequestType: Classify(m.GetInputType()),
|
116
|
-
Path: path,
|
117
|
-
RestOptions: rubyRestOptions(restOpts),
|
118
|
-
HttpMethod: httpMethod,
|
119
|
-
Body: opts.Body,
|
120
|
-
PathInfo: pathInfo,
|
121
|
-
}
|
122
|
-
data.Methods = append(data.Methods, controllerMethod)
|
123
|
-
routes = append(routes, Route{
|
124
|
-
HttpMethod: strings.ToLower(httpMethod),
|
125
|
-
Path: SanitizePath(path),
|
126
|
-
Controller: strcase.ToSnake(data.ControllerName),
|
127
|
-
MethodName: strcase.ToSnake(m.GetName()),
|
128
|
-
})
|
129
|
-
}
|
130
|
-
resultTemplate, err := template.New("controller").Parse(controllerTemplate)
|
131
|
-
if err != nil {
|
132
|
-
return FileResult{}, routes, fmt.Errorf("can't parse controller template: %w", err)
|
133
|
-
}
|
134
|
-
var resultContent bytes.Buffer
|
135
|
-
err = resultTemplate.Execute(&resultContent, data)
|
136
|
-
if err != nil {
|
137
|
-
return FileResult{}, routes, fmt.Errorf("can't execute controller template: %w", err)
|
138
|
-
}
|
139
|
-
return FileResult{
|
140
|
-
Content: resultContent.String(),
|
141
|
-
Name: fmt.Sprintf("app/controllers/%s_controller.rb", strcase.ToSnake(data.ControllerName)),
|
142
|
-
}, routes, nil
|
143
|
-
}
|
144
|
-
|
145
|
-
var routeTemplate = `# frozen_string_literal: true
|
146
|
-
|
147
|
-
####### THIS FILE IS AUTOMATICALLY GENERATED BY protoc-gen-rails. DO NOT MODIFY. #######
|
148
|
-
|
149
|
-
{{range . -}}
|
150
|
-
{{.HttpMethod}} '{{.Path}}' => '{{.Controller}}#{{.MethodName}}'
|
151
|
-
{{end -}}
|
152
|
-
`
|
153
|
-
|
154
|
-
func OutputRoutes(routes []Route) (string, error) {
|
155
|
-
resultTemplate, err := template.New("routes").Parse(routeTemplate)
|
156
|
-
if err != nil {
|
157
|
-
return "", err
|
158
|
-
}
|
159
|
-
var resultContent bytes.Buffer
|
160
|
-
err = resultTemplate.Execute(&resultContent, routes)
|
161
|
-
if err != nil {
|
162
|
-
return "", err
|
163
|
-
}
|
164
|
-
return resultContent.String(), nil
|
165
|
-
}
|
@@ -1,101 +0,0 @@
|
|
1
|
-
package internal
|
2
|
-
|
3
|
-
import (
|
4
|
-
"encoding/json"
|
5
|
-
"fmt"
|
6
|
-
options "google.golang.org/genproto/googleapis/api/annotations"
|
7
|
-
options2 "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options"
|
8
|
-
"google.golang.org/protobuf/proto"
|
9
|
-
"google.golang.org/protobuf/types/descriptorpb"
|
10
|
-
"regexp"
|
11
|
-
"strings"
|
12
|
-
)
|
13
|
-
|
14
|
-
func MethodAndPath(pattern any) (string, string, error) {
|
15
|
-
|
16
|
-
switch typedPattern := pattern.(type) {
|
17
|
-
case *options.HttpRule_Get:
|
18
|
-
return "GET", typedPattern.Get, nil
|
19
|
-
case *options.HttpRule_Post:
|
20
|
-
return "POST", typedPattern.Post, nil
|
21
|
-
case *options.HttpRule_Put:
|
22
|
-
return "PUT", typedPattern.Put, nil
|
23
|
-
case *options.HttpRule_Delete:
|
24
|
-
return "DELETE", typedPattern.Delete, nil
|
25
|
-
case *options.HttpRule_Patch:
|
26
|
-
return "PATCH", typedPattern.Patch, nil
|
27
|
-
case *options.HttpRule_Custom:
|
28
|
-
return typedPattern.Custom.Kind, typedPattern.Custom.Path, nil
|
29
|
-
default:
|
30
|
-
return "", "", fmt.Errorf("unknown pattern type %T", pattern)
|
31
|
-
}
|
32
|
-
}
|
33
|
-
|
34
|
-
type PathInfo struct {
|
35
|
-
Name string
|
36
|
-
ValPattern string
|
37
|
-
SplitName string
|
38
|
-
HasValPattern bool
|
39
|
-
}
|
40
|
-
|
41
|
-
func ParsedPath(path string) ([]PathInfo, error) {
|
42
|
-
var infos []PathInfo
|
43
|
-
re := regexp.MustCompile("\\{(.*?)}")
|
44
|
-
matches := re.FindAllString(path, -1)
|
45
|
-
for _, match := range matches {
|
46
|
-
name := match[1:len(match)-1]
|
47
|
-
val := ""
|
48
|
-
equal := strings.Index(match, "=")
|
49
|
-
if equal != -1 {
|
50
|
-
val = name[equal:]
|
51
|
-
name = name[0:equal-1]
|
52
|
-
}
|
53
|
-
splitName := strings.Split(name, ".")
|
54
|
-
jsonSplit, err := json.Marshal(splitName)
|
55
|
-
if err != nil {
|
56
|
-
return nil, fmt.Errorf("error marshalling splitName: %w", err)
|
57
|
-
}
|
58
|
-
infos = append(infos, PathInfo{
|
59
|
-
Name: name,
|
60
|
-
ValPattern: val,
|
61
|
-
SplitName: string(jsonSplit),
|
62
|
-
HasValPattern: val != "",
|
63
|
-
})
|
64
|
-
}
|
65
|
-
return infos, nil
|
66
|
-
}
|
67
|
-
|
68
|
-
func ExtractAPIOptions(meth *descriptorpb.MethodDescriptorProto) (*options.HttpRule, error) {
|
69
|
-
if meth.Options == nil {
|
70
|
-
return nil, nil
|
71
|
-
}
|
72
|
-
if !proto.HasExtension(meth.Options, options.E_Http) {
|
73
|
-
return nil, nil
|
74
|
-
}
|
75
|
-
ext := proto.GetExtension(meth.Options, options.E_Http)
|
76
|
-
opts, ok := ext.(*options.HttpRule)
|
77
|
-
if !ok {
|
78
|
-
return nil, fmt.Errorf("extension is %T; want an HttpRule", ext)
|
79
|
-
}
|
80
|
-
return opts, nil
|
81
|
-
}
|
82
|
-
|
83
|
-
func ExtractRestOptions(meth *descriptorpb.MethodDescriptorProto) (map[string]bool, error) {
|
84
|
-
if meth.Options == nil {
|
85
|
-
return nil, nil
|
86
|
-
}
|
87
|
-
if !proto.HasExtension(meth.Options, options2.E_Openapiv2Operation) {
|
88
|
-
return nil, nil
|
89
|
-
}
|
90
|
-
ext := proto.GetExtension(meth.Options, options2.E_Openapiv2Operation)
|
91
|
-
operation, ok := ext.(*options2.Operation)
|
92
|
-
if !ok {
|
93
|
-
return nil, fmt.Errorf("extension is %T; want an Operation", ext)
|
94
|
-
}
|
95
|
-
operationExt := operation.GetExtensions()
|
96
|
-
emitDefaults := operationExt["x-grpc-rest-emit-defaults"].GetBoolValue()
|
97
|
-
return map[string]bool{
|
98
|
-
"emit_defaults": emitDefaults,
|
99
|
-
}, nil
|
100
|
-
}
|
101
|
-
|
@@ -1,57 +0,0 @@
|
|
1
|
-
package internal
|
2
|
-
|
3
|
-
import (
|
4
|
-
"fmt"
|
5
|
-
"github.com/iancoleman/strcase"
|
6
|
-
"os"
|
7
|
-
"regexp"
|
8
|
-
"strings"
|
9
|
-
)
|
10
|
-
|
11
|
-
func LogMsg(msg string, args ...any) {
|
12
|
-
fmt.Fprintf(os.Stderr, fmt.Sprintf(msg, args...))
|
13
|
-
fmt.Fprintln(os.Stderr)
|
14
|
-
}
|
15
|
-
|
16
|
-
func FilePathify(s string) string {
|
17
|
-
var result []string
|
18
|
-
s = strings.Trim(s, ".")
|
19
|
-
tokens := strings.Split(s, ".")
|
20
|
-
for _, token := range tokens {
|
21
|
-
result = append(result, strcase.ToSnake(token))
|
22
|
-
}
|
23
|
-
return strings.Join(result, "/")
|
24
|
-
}
|
25
|
-
|
26
|
-
func Classify(s string) string {
|
27
|
-
var result []string
|
28
|
-
s = strings.Trim(s, ".")
|
29
|
-
tokens := strings.Split(s, ".")
|
30
|
-
for _, token := range tokens {
|
31
|
-
result = append(result, strcase.ToCamel(token))
|
32
|
-
}
|
33
|
-
return strings.Join(result, "::")
|
34
|
-
}
|
35
|
-
|
36
|
-
func Demodulize(s string) string {
|
37
|
-
tokens := strings.Split(s, ".")
|
38
|
-
return tokens[len(tokens)-1]
|
39
|
-
}
|
40
|
-
|
41
|
-
func SanitizePath(s string) string {
|
42
|
-
re := regexp.MustCompile("\\{(.*?)}")
|
43
|
-
matches := re.FindAllStringSubmatch(s, -1)
|
44
|
-
for _, match := range matches {
|
45
|
-
repl := match[1]
|
46
|
-
equal := strings.Index(match[1], "=")
|
47
|
-
if equal != -1 {
|
48
|
-
repl = repl[0:equal]
|
49
|
-
}
|
50
|
-
dot := strings.Index(repl, ".")
|
51
|
-
if dot != -1 {
|
52
|
-
repl = repl[dot+1:]
|
53
|
-
}
|
54
|
-
s = strings.Replace(s, match[0], "*"+repl, 1)
|
55
|
-
}
|
56
|
-
return s
|
57
|
-
}
|
data/protoc-gen-rails/main.go
DELETED
@@ -1,104 +0,0 @@
|
|
1
|
-
package main
|
2
|
-
|
3
|
-
import (
|
4
|
-
"fmt"
|
5
|
-
"github.com/flipp-oss/protoc-gen-rails/internal"
|
6
|
-
"google.golang.org/protobuf/proto"
|
7
|
-
"google.golang.org/protobuf/types/descriptorpb"
|
8
|
-
"google.golang.org/protobuf/types/pluginpb"
|
9
|
-
"io"
|
10
|
-
"log"
|
11
|
-
"os"
|
12
|
-
"slices"
|
13
|
-
)
|
14
|
-
|
15
|
-
var routes = []internal.Route{}
|
16
|
-
|
17
|
-
func processService(service *descriptorpb.ServiceDescriptorProto, pkg string) (internal.FileResult, error) {
|
18
|
-
result, serviceRoutes, err := internal.ProcessService(service, pkg)
|
19
|
-
if err != nil {
|
20
|
-
return internal.FileResult{}, err
|
21
|
-
}
|
22
|
-
routes = slices.Concat(routes, serviceRoutes)
|
23
|
-
return result, nil
|
24
|
-
}
|
25
|
-
|
26
|
-
func routeFile() (internal.FileResult, error) {
|
27
|
-
content, err := internal.OutputRoutes(routes)
|
28
|
-
if err != nil {
|
29
|
-
return internal.FileResult{}, err
|
30
|
-
}
|
31
|
-
return internal.FileResult{
|
32
|
-
Name: "config/routes/grpc.rb",
|
33
|
-
Content: content,
|
34
|
-
}, nil
|
35
|
-
}
|
36
|
-
|
37
|
-
func main() {
|
38
|
-
req, err := ReadRequest()
|
39
|
-
if err != nil {
|
40
|
-
log.Fatalf("%s", fmt.Errorf("error reading request: %w", err))
|
41
|
-
}
|
42
|
-
files := []internal.FileResult{}
|
43
|
-
for _, file := range req.GetProtoFile() {
|
44
|
-
for _, service := range file.GetService() {
|
45
|
-
fileResult, err := processService(service, file.GetPackage())
|
46
|
-
if err != nil {
|
47
|
-
log.Fatalf("%s", fmt.Errorf("error processing service %v: %w", service.GetName(), err))
|
48
|
-
}
|
49
|
-
files = append(files, fileResult)
|
50
|
-
}
|
51
|
-
}
|
52
|
-
if len(files) > 0 {
|
53
|
-
routeOutput, err := routeFile()
|
54
|
-
if err != nil {
|
55
|
-
log.Fatalf("%s", fmt.Errorf("error processing routes: %w", err))
|
56
|
-
}
|
57
|
-
files = append(files, routeOutput)
|
58
|
-
}
|
59
|
-
|
60
|
-
// process registry
|
61
|
-
writeResponse(files)
|
62
|
-
}
|
63
|
-
|
64
|
-
func ReadRequest() (*pluginpb.CodeGeneratorRequest, error) {
|
65
|
-
in, err := io.ReadAll(os.Stdin)
|
66
|
-
if err != nil {
|
67
|
-
return nil, err
|
68
|
-
}
|
69
|
-
req := &pluginpb.CodeGeneratorRequest{}
|
70
|
-
err = proto.Unmarshal(in, req)
|
71
|
-
if err != nil {
|
72
|
-
return nil, err
|
73
|
-
}
|
74
|
-
return req, nil
|
75
|
-
}
|
76
|
-
|
77
|
-
func generateResponse(files []internal.FileResult) *pluginpb.CodeGeneratorResponse {
|
78
|
-
feature := uint64(pluginpb.CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL)
|
79
|
-
respFiles := make([]*pluginpb.CodeGeneratorResponse_File, len(files))
|
80
|
-
for i, file := range files {
|
81
|
-
respFiles[i] = &pluginpb.CodeGeneratorResponse_File{
|
82
|
-
Name: &file.Name,
|
83
|
-
Content: &file.Content,
|
84
|
-
}
|
85
|
-
|
86
|
-
}
|
87
|
-
return &pluginpb.CodeGeneratorResponse{
|
88
|
-
SupportedFeatures: &feature,
|
89
|
-
File: respFiles,
|
90
|
-
}
|
91
|
-
}
|
92
|
-
|
93
|
-
func writeResponse(files []internal.FileResult) {
|
94
|
-
response := generateResponse(files)
|
95
|
-
out, err := proto.Marshal(response)
|
96
|
-
if err != nil {
|
97
|
-
log.Fatalf("%s", fmt.Errorf("error marshalling response: %w", err))
|
98
|
-
}
|
99
|
-
_, err = os.Stdout.Write(out)
|
100
|
-
if err != nil {
|
101
|
-
log.Fatalf("%s", fmt.Errorf("error writing response: %w", err))
|
102
|
-
}
|
103
|
-
}
|
104
|
-
|
@@ -1,158 +0,0 @@
|
|
1
|
-
package main
|
2
|
-
|
3
|
-
import (
|
4
|
-
"fmt"
|
5
|
-
"github.com/stretchr/testify/assert"
|
6
|
-
"os"
|
7
|
-
"os/exec"
|
8
|
-
"path/filepath"
|
9
|
-
"strings"
|
10
|
-
"testing"
|
11
|
-
)
|
12
|
-
|
13
|
-
// Overall approach taken from https://github.com/mix-php/mix/blob/master/src/grpc/protoc-gen-mix/plugin_test.go
|
14
|
-
|
15
|
-
// When the environment variable RUN_AS_PROTOC_GEN_RAILS is set, we skip running
|
16
|
-
// tests and instead act as protoc-gen-rails. This allows the test binary to
|
17
|
-
// pass itself to protoc.
|
18
|
-
func init() {
|
19
|
-
if os.Getenv("RUN_AS_PROTOC_GEN_RAILS") != "" {
|
20
|
-
main()
|
21
|
-
os.Exit(0)
|
22
|
-
}
|
23
|
-
}
|
24
|
-
|
25
|
-
func fileNames(directory string, appendDirectory bool, extension string) ([]string, error) {
|
26
|
-
var files []os.FileInfo
|
27
|
-
var fullPaths []string
|
28
|
-
err := filepath.Walk(directory, func(path string, info os.FileInfo, err error) error {
|
29
|
-
if extension != "" && filepath.Ext(path) != extension {
|
30
|
-
return nil
|
31
|
-
}
|
32
|
-
if info.IsDir() {
|
33
|
-
return nil
|
34
|
-
}
|
35
|
-
files = append(files, info)
|
36
|
-
if info.Name() != ".keep" {
|
37
|
-
fullPaths = append(fullPaths, path)
|
38
|
-
}
|
39
|
-
if err != nil {
|
40
|
-
fmt.Println("ERROR:", err)
|
41
|
-
}
|
42
|
-
return nil
|
43
|
-
})
|
44
|
-
if err != nil {
|
45
|
-
return nil, fmt.Errorf("can't read %s directory: %w", directory, err)
|
46
|
-
}
|
47
|
-
if appendDirectory {
|
48
|
-
return fullPaths, nil
|
49
|
-
}
|
50
|
-
var names []string
|
51
|
-
for _, file := range fullPaths {
|
52
|
-
names = append(names, strings.Replace(file, directory, "", 1)[1:])
|
53
|
-
}
|
54
|
-
return names, nil
|
55
|
-
}
|
56
|
-
|
57
|
-
func runTest(t *testing.T, directory string, options map[string]string) {
|
58
|
-
workdir, _ := os.Getwd()
|
59
|
-
tmpdir, err := os.MkdirTemp(workdir, "proto-test.")
|
60
|
-
if err != nil {
|
61
|
-
t.Fatal(err)
|
62
|
-
}
|
63
|
-
defer os.RemoveAll(tmpdir)
|
64
|
-
|
65
|
-
args := []string{
|
66
|
-
"-I.",
|
67
|
-
"--rails_out=" + tmpdir,
|
68
|
-
}
|
69
|
-
names, err := fileNames(workdir + "/testdata", false, ".proto")
|
70
|
-
if err != nil {
|
71
|
-
t.Fatal(fmt.Errorf("testData fileNames %w", err))
|
72
|
-
}
|
73
|
-
for _, name := range names {
|
74
|
-
args = append(args, fmt.Sprintf("testdata/%v", name))
|
75
|
-
}
|
76
|
-
for k, v := range options {
|
77
|
-
args = append(args, "--rails_opt=" + k + "=" + v)
|
78
|
-
}
|
79
|
-
protoc(t, args)
|
80
|
-
|
81
|
-
testDir := workdir + "/testdata/" + directory
|
82
|
-
if os.Getenv("UPDATE_SNAPSHOTS") != "" {
|
83
|
-
cmd := exec.Command("/bin/sh", "-c", fmt.Sprintf("cp -R %v/* %v", tmpdir, testDir))
|
84
|
-
cmd.Run()
|
85
|
-
} else {
|
86
|
-
assertEqualFiles(t, testDir, tmpdir)
|
87
|
-
}
|
88
|
-
}
|
89
|
-
|
90
|
-
func Test_Base(t *testing.T) {
|
91
|
-
runTest(t, "base", map[string]string{})
|
92
|
-
}
|
93
|
-
|
94
|
-
func Test_NoServices(t *testing.T) {
|
95
|
-
workdir, _ := os.Getwd()
|
96
|
-
tmpdir, err := os.MkdirTemp(workdir, "proto-test.")
|
97
|
-
if err != nil {
|
98
|
-
t.Fatal(err)
|
99
|
-
}
|
100
|
-
defer os.RemoveAll(tmpdir)
|
101
|
-
|
102
|
-
args := []string{
|
103
|
-
"-I.",
|
104
|
-
"--rails_out=" + tmpdir,
|
105
|
-
}
|
106
|
-
args = append(args, fmt.Sprintf("testdata/test.proto"))
|
107
|
-
protoc(t, args)
|
108
|
-
|
109
|
-
testDir := workdir + "/testdata/no_service"
|
110
|
-
assertEqualFiles(t, testDir, tmpdir)
|
111
|
-
}
|
112
|
-
|
113
|
-
func assertEqualFiles(t *testing.T, original, generated string) {
|
114
|
-
names, err := fileNames(original, false, "")
|
115
|
-
if err != nil {
|
116
|
-
t.Fatal(fmt.Errorf("original fileNames %w", err))
|
117
|
-
}
|
118
|
-
generatedNames, err := fileNames(generated, false, "")
|
119
|
-
if err != nil {
|
120
|
-
t.Fatal(fmt.Errorf("generated fileNames %w", err))
|
121
|
-
}
|
122
|
-
assert.Equal(t, names, generatedNames)
|
123
|
-
// put back subdirectories
|
124
|
-
names, _ = fileNames(original, true, "")
|
125
|
-
generatedNames, _ = fileNames(generated, true, "")
|
126
|
-
for i, name := range names {
|
127
|
-
originalData, err := os.ReadFile(name)
|
128
|
-
if err != nil {
|
129
|
-
t.Fatal("Can't find original file for comparison")
|
130
|
-
}
|
131
|
-
|
132
|
-
generatedData, err := os.ReadFile(generatedNames[i])
|
133
|
-
if err != nil {
|
134
|
-
t.Fatal("Can't find generated file for comparison")
|
135
|
-
}
|
136
|
-
r := strings.NewReplacer("\r\n", "", "\n", "")
|
137
|
-
assert.Equal(t, r.Replace(string(originalData)), r.Replace(string(generatedData)))
|
138
|
-
}
|
139
|
-
}
|
140
|
-
|
141
|
-
func protoc(t *testing.T, args []string) {
|
142
|
-
cmd := exec.Command("protoc", "--proto_path=.", "--proto_path=./google-deps", "--plugin=protoc-gen-rails=" + os.Args[0])
|
143
|
-
cmd.Args = append(cmd.Args, args...)
|
144
|
-
cmd.Env = append(os.Environ(), "RUN_AS_PROTOC_GEN_RAILS=1")
|
145
|
-
out, err := cmd.CombinedOutput()
|
146
|
-
|
147
|
-
if len(out) > 0 || err != nil {
|
148
|
-
t.Log("RUNNING: ", strings.Join(cmd.Args, " "))
|
149
|
-
}
|
150
|
-
|
151
|
-
if len(out) > 0 {
|
152
|
-
t.Log(string(out))
|
153
|
-
}
|
154
|
-
|
155
|
-
if err != nil {
|
156
|
-
t.Fatalf("protoc: %v", err)
|
157
|
-
}
|
158
|
-
}
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|