|
| 1 | +# Changelog |
| 2 | + |
| 3 | +## Unreleased |
| 4 | + |
| 5 | +### New Features |
| 6 | + |
| 7 | +#### External Proto Import Support (`--proto_path` / `-I`) |
| 8 | + |
| 9 | +Added support for importing proto files from external directories via `-I` / `--proto_path` flags, with full transitive dependency resolution. |
| 10 | + |
| 11 | +**Affected files:** |
| 12 | +- `generator/gen.go` — Added `ProtoPaths` field to `ZRpcContext`; added `resolveImportedProtos()` to populate `ImportedProtos` before code generation. |
| 13 | +- `generator/genpb.go` — Added `buildProtocCmd()` to automatically discover and append transitively imported proto files to the `protoc` command; added `relativeToProtoPath()` to compute correct relative paths for protoc output. |
| 14 | +- `parser/import.go` — New file (major addition). Implements `ResolveImports()` for recursive transitive import resolution, `ParseImportedProtos()` for extracting `go_package` / `package` metadata from imported protos, and `BuildProtoPackageMap()` for O(1) lookup by proto package name. |
| 15 | +- `parser/proto.go` — Added `ImportedProtos []ImportedProto` field to the `Proto` struct. |
| 16 | +- `cli/cli.go` — Passes `ProtoPaths` from `RPCNew` to `ZRpcContext`. |
| 17 | +- `cli/zrpc.go` — Passes `VarStringSliceProtoPath` to `ZRpcContext.ProtoPaths`. |
| 18 | + |
| 19 | +**Before vs After:** |
| 20 | + |
| 21 | +| | Before | After | |
| 22 | +|---|---|---| |
| 23 | +| Proto imports from external dirs | ❌ Not supported, all types must be in the same file | ✅ Use `-I ./ext_protos` to add search paths | |
| 24 | +| Transitive imports (A → B → C) | ❌ Only direct imports recognized | ✅ Recursively resolves all transitive dependencies | |
| 25 | +| Imported proto `.pb.go` generation | ❌ Manual, must run protoc separately for each file | ✅ Automatic, imported protos appended to protoc command | |
| 26 | +| Proto search paths | ❌ Only source file directory | ✅ Multiple `-I` paths, same as protoc | |
| 27 | + |
| 28 | +**Behavior:** |
| 29 | +- Transitively walks all `import` declarations in proto files, skipping `google/*` well-known types. |
| 30 | +- Searches each `-I` directory for imported files, silently skipping system-level protos not found in user paths. |
| 31 | +- Appends discovered proto files to the `protoc` command so their `.pb.go` files are generated alongside the main proto. |
| 32 | + |
| 33 | +--- |
| 34 | + |
| 35 | +#### Cross-Package Type Resolution |
| 36 | + |
| 37 | +When an imported proto has a **different** `go_package` from the main proto, goctl now automatically generates the correct Go import paths and qualified type references in server, logic, and client code. |
| 38 | + |
| 39 | +**Affected files:** |
| 40 | +- `generator/typeref.go` — New file. Core type resolution engine: |
| 41 | + - `resolveRPCTypeRef()` — Resolves proto RPC types (simple, same-package dotted, cross-package dotted, Google WKT) to Go type references with correct import paths. |
| 42 | + - `resolveCallTypeRef()` — Variant for client code generation with type alias support. |
| 43 | + - `googleWKTTable` — Mapping table for all 16 Google well-known types to their Go equivalents. |
| 44 | +- `generator/genserver.go` — `genFunctions()` now calls `resolveRPCTypeRef()` for request/response types and collects extra import paths. |
| 45 | +- `generator/genlogic.go` — `genLogicFunction()` uses `resolveRPCTypeRef()`; added `addLogicImports()` to conditionally include main pb import and cross-package imports. |
| 46 | +- `generator/gencall.go` — `genFunction()` and `getInterfaceFuncs()` use `resolveCallTypeRef()` for type aliases and extra imports; added `buildExtraImportLines()` helper. |
| 47 | +- `generator/call.tpl` — Added `{{.extraImports}}` placeholder for cross-package import lines. |
| 48 | + |
| 49 | +**Before vs After:** |
| 50 | + |
| 51 | +| Proto type | Before | After | |
| 52 | +|---|---|---| |
| 53 | +| `GetReq` (same file) | `pb.GetReq` | `pb.GetReq` (unchanged) | |
| 54 | +| `ext.ExtReq` (same `go_package`) | ❌ Error: "request type must defined in" | ✅ `pb.ExtReq` — merged into main package | |
| 55 | +| `common.TypesReq` (different `go_package`) | ❌ Error: "request type must defined in" | ✅ `common.TypesReq` + auto-generated `import "example.com/demo/pb/common"` | |
| 56 | +| `google.protobuf.Empty` | ❌ Error: "request type must defined in" | ✅ `emptypb.Empty` + auto-generated import | |
| 57 | + |
| 58 | +**Behavior:** |
| 59 | +- Simple types (e.g., `GetReq`) resolve to `pb.GetReq` with no extra import. |
| 60 | +- Same-package dotted types (e.g., `ext.ExtReq` where `ext` has the same `go_package`) resolve to `pb.ExtReq`. |
| 61 | +- Cross-package dotted types (e.g., `common.TypesReq` where `common` has a different `go_package`) resolve to `common.TypesReq` with the correct Go import path added automatically. |
| 62 | + |
| 63 | +--- |
| 64 | + |
| 65 | +#### Google Well-Known Types as RPC Parameters |
| 66 | + |
| 67 | +Google protobuf well-known types can now be used directly as RPC request/response types (not just as message fields). |
| 68 | + |
| 69 | +**Affected files:** |
| 70 | +- `generator/typeref.go` — `resolveGoogleWKT()` + `googleWKTTable` handles all standard types. |
| 71 | + |
| 72 | +**Before vs After:** |
| 73 | + |
| 74 | +| Proto Type | Before (as RPC param) | After (as RPC param) | |
| 75 | +|---|---|---| |
| 76 | +| `google.protobuf.Empty` | ❌ Error | ✅ `emptypb.Empty` | |
| 77 | +| `google.protobuf.Timestamp` | ❌ Error | ✅ `timestamppb.Timestamp` | |
| 78 | +| `google.protobuf.Duration` | ❌ Error | ✅ `durationpb.Duration` | |
| 79 | +| `google.protobuf.Any` | ❌ Error | ✅ `anypb.Any` | |
| 80 | +| `google.protobuf.Struct` | ❌ Error | ✅ `structpb.Struct` | |
| 81 | +| `google.protobuf.FieldMask` | ❌ Error | ✅ `fieldmaskpb.FieldMask` | |
| 82 | +| `google.protobuf.*Value` | ❌ Error | ✅ `wrapperspb.*Value` | |
| 83 | + |
| 84 | +> Note: These types were already usable as **message fields** before. This change allows them as **RPC request/response types** directly. |
| 85 | +
|
| 86 | +**Supported types:** |
| 87 | + |
| 88 | +| Proto Type | Go Type | |
| 89 | +|---|---| |
| 90 | +| `google.protobuf.Empty` | `emptypb.Empty` | |
| 91 | +| `google.protobuf.Timestamp` | `timestamppb.Timestamp` | |
| 92 | +| `google.protobuf.Duration` | `durationpb.Duration` | |
| 93 | +| `google.protobuf.Any` | `anypb.Any` | |
| 94 | +| `google.protobuf.Struct` | `structpb.Struct` | |
| 95 | +| `google.protobuf.Value` | `structpb.Value` | |
| 96 | +| `google.protobuf.ListValue` | `structpb.ListValue` | |
| 97 | +| `google.protobuf.FieldMask` | `fieldmaskpb.FieldMask` | |
| 98 | +| `google.protobuf.*Value` (wrappers) | `wrapperspb.*Value` | |
| 99 | + |
| 100 | +--- |
| 101 | + |
| 102 | +### Breaking Changes |
| 103 | + |
| 104 | +#### Dotted Type Names Now Allowed in RPC Definitions |
| 105 | + |
| 106 | +Previously, goctl rejected any RPC request/response type containing a dot (e.g., `base.Req`), requiring all types to be defined in the same proto file. This restriction has been removed. |
| 107 | + |
| 108 | +**Before vs After:** |
| 109 | + |
| 110 | +| Proto Definition | Before | After | |
| 111 | +|---|---|---| |
| 112 | +| `rpc Fetch(base.Req) returns (base.Reply)` | ❌ Parse error: "request type must defined in xxx.proto" | ✅ Parsed successfully, `base.Req` resolved via imported proto | |
| 113 | +| `rpc Ping(google.protobuf.Empty) returns (Reply)` | ❌ Parse error: "request type must defined in xxx.proto" | ✅ Parsed successfully, resolved to `emptypb.Empty` | |
| 114 | + |
| 115 | +**Affected files:** |
| 116 | +- `parser/service.go` — Removed the validation loop that rejected dotted type names with `"request type must defined in"` / `"returns type must defined in"` errors. |
| 117 | +- `parser/parser_test.go` — `TestDefaultProtoParseCaseInvalidRequestType` and `TestDefaultProtoParseCaseInvalidResponseType` renamed and updated to verify that dotted types now parse successfully. |
0 commit comments