Skip to content

wrap grpc generates broken request wrappers — {X X}Wrapper template literal leaks into output (v0.8.1) #75

@aryanmehrotra

Description

@aryanmehrotra

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions