Summary
gofr wrap grpc server -proto=<file> produces broken Go code: every request-wrapper type/method renders as a Go struct literal (e.g. {GetCredentialsRequest GetCredentialsRequest}Wrapper) instead of the expected name (GetCredentialsRequestWrapper). The generated request_*.go and the corresponding wrapper site in <service>_gofr.go will not compile.
This affects both wrap grpc server and (likely, by inspection) wrap grpc client.
Reproduces on the latest release: v0.8.1.
Repro
// example.proto
syntax = "proto3";
package example;
option go_package = "example/proto";
service ExampleService {
rpc GetThing(GetThingRequest) returns (GetThingResponse);
}
message GetThingRequest {}
message GetThingResponse {}
# In a directory containing example.proto + go.mod with gofr.dev v1.56.x
protoc --go_out=. --go-grpc_out=. example.proto
gofr wrap grpc server -proto=example.proto
Expected
request_gofr.go contains:
type GetThingRequestWrapper struct {
ctx context.Context
*GetThingRequest
}
func (h *GetThingRequestWrapper) Context() context.Context { return h.ctx }
// ... etc
Actual
request_gofr.go contains:
type {GetThingRequest GetThingRequest}Wrapper struct {
ctx context.Context
*{GetThingRequest GetThingRequest}
}
func (h *{GetThingRequest GetThingRequest}Wrapper) Context() context.Context {
return h.ctx
}
// ... etc
go build ./... fails with:
syntax error: unexpected {, expected name
syntax error: unexpected {, expected type
Root cause
wrap/template.go ranges over .Requests, which is a []ServiceRequest of structs:
// wrap/grpc.go
type ServiceRequest struct {
Request string
RawRequest string
}
But the template treats the loop variable as a string:
// wrap/template.go (current)
{{- range $request := .Requests }}
type {{ $request }}Wrapper struct {
ctx context.Context
*{{ $request }}
}
// ...
text/template falls back to fmt's default struct format {field1 field2}, so {{ $request }} renders as {GetThingRequest GetThingRequest} (both Request and RawRequest happen to be the same identifier in this case).
Fix
Use {{ $request.Request }} (or .Request after range $request := …) at every site in the request-wrapper block:
{{- range $request := .Requests }}
type {{ $request.Request }}Wrapper struct {
ctx context.Context
*{{ $request.Request }}
}
func (h *{{ $request.Request }}Wrapper) Context() context.Context {
return h.ctx
}
func (h *{{ $request.Request }}Wrapper) Param(s string) string { return "" }
// ... and the same in PathParam / Bind / HostName / Params
The Bind impl also uses h.{{ $request }} — same fix:
hValue := reflect.ValueOf(h.{{ $request.Request }}).Elem()
I'm happy to send a PR if helpful — straightforward template-string change.
Environment
- gofr-cli: v0.8.1 (latest release at filing time)
- Installed via
go install gofr.dev/cli/gofr@latest
- Module path declared in binary metadata:
gofr.dev/cli/gofr v0.8.1
- Note:
gofr version in v0.8.1 prints v0.7.1 (looks like the version constant in version.go wasn't bumped on the v0.8.x release — minor unrelated polish).
- Target project uses gofr.dev v1.56.1
- Also reproduced against gofr.dev v1.39.0 (per the
v0.7.0 versions stamp the template emits)
Why this matters
gofr wrap grpc is the documented path for adding gRPC handlers in gofr-stack services. With the current template, any project that has more than one Request type in its proto can't regenerate cleanly — adding a new RPC requires hand-editing the generated wrapper because re-running the cli replaces correct files with non-compiling output.
In our case (zopnight microservices, ~15 RPCs on one service), this blocked a notification-config refactor until I hand-restored the previous wrapper files and added the new RPC's wrapper manually.
Summary
gofr wrap grpc server -proto=<file>produces broken Go code: every request-wrapper type/method renders as a Go struct literal (e.g.{GetCredentialsRequest GetCredentialsRequest}Wrapper) instead of the expected name (GetCredentialsRequestWrapper). The generatedrequest_*.goand the corresponding wrapper site in<service>_gofr.gowill not compile.This affects both
wrap grpc serverand (likely, by inspection)wrap grpc client.Reproduces on the latest release: v0.8.1.
Repro
# In a directory containing example.proto + go.mod with gofr.dev v1.56.x protoc --go_out=. --go-grpc_out=. example.proto gofr wrap grpc server -proto=example.protoExpected
request_gofr.gocontains:Actual
request_gofr.gocontains:go build ./...fails with:Root cause
wrap/template.goranges over.Requests, which is a[]ServiceRequestof structs:But the template treats the loop variable as a string:
text/templatefalls back tofmt's default struct format{field1 field2}, so{{ $request }}renders as{GetThingRequest GetThingRequest}(bothRequestandRawRequesthappen to be the same identifier in this case).Fix
Use
{{ $request.Request }}(or.Requestafterrange $request := …) at every site in the request-wrapper block:{{- range $request := .Requests }} type {{ $request.Request }}Wrapper struct { ctx context.Context *{{ $request.Request }} } func (h *{{ $request.Request }}Wrapper) Context() context.Context { return h.ctx } func (h *{{ $request.Request }}Wrapper) Param(s string) string { return "" } // ... and the same in PathParam / Bind / HostName / ParamsThe
Bindimpl also usesh.{{ $request }}— same fix:I'm happy to send a PR if helpful — straightforward template-string change.
Environment
go install gofr.dev/cli/gofr@latestgofr.dev/cli/gofr v0.8.1gofr versionin v0.8.1 printsv0.7.1(looks like the version constant inversion.gowasn't bumped on the v0.8.x release — minor unrelated polish).v0.7.0versions stamp the template emits)Why this matters
gofr wrap grpcis the documented path for adding gRPC handlers in gofr-stack services. With the current template, any project that has more than one Request type in its proto can't regenerate cleanly — adding a new RPC requires hand-editing the generated wrapper because re-running the cli replaces correct files with non-compiling output.In our case (zopnight microservices, ~15 RPCs on one service), this blocked a notification-config refactor until I hand-restored the previous wrapper files and added the new RPC's wrapper manually.