Skip to content

Fix shared type generation, external union conversions, and gRPC clients - #4010

Merged
raphael merged 4 commits into
v3from
external-union-conversions
Sep 28, 2026
Merged

raphael merged 4 commits into
v3from
external-union-conversions

Conversation

@raphael

@raphael raphael commented Sep 26, 2026 •

Copy link
Copy Markdown
Member

Goa could choose different Go packages or names for the same reused type, omit imports needed by generated conversions, and fail to preserve valid values across transport conversions. This change makes the complete generation choose shared type ownership and keeps declarations, imports, and their callers on that decision.

A type placed with struct:pkg:path now supplies the package for its unlocated authored descendants. An explicitly located child retains its own package and supplies it to its descendants. Ambiguous ownership and required generated-package import cycles fail before source emission. Imported design declarations are not annotated or mutated. Plugins can query the retained original attribute and Go layout with service.Plan.UserTypeLayout, and register imports for either a complete value or just its type reference through GeneratedImportPlan.

HTTP conversion helpers now retain the complete source and target layouts selected by the enclosing conversion. When one inline union is reused beneath two generated owners, each helper uses that owner's actual declaration and constructor. TransformPlan.HelperLayouts selects those existing layouts by helper ID; it rejects a mismatched root or path. GoTypePlan.PreferredImportName exposes the planned package-name preference before import aliases become final. HTTP uses that preference with the selected owner; gRPC distinguishes imports needed only for a named reference from imports needed by a complete conversion. Copied built-in errors retain goa.ServiceError and its complete field layout, so generated HTTP clients construct the declared error type. These queries do not add design annotations or runtime lookup.

HTTP and JSON-RPC mapped fields now use the same names and requiredness in declarations and conversion planning. For example, detail:message keeps service field Detail and body field Message, with requiredness and defaults belonging to detail. The HTTP planner enables the new GoTypePlanOptions.MappedFields option; ordinary service planning keeps its existing behavior. Existing keyed options literals remain valid. Generator plugins using positional literals must change them to keyed literals. The JSON body, error envelope and validation contract are unchanged.

ConvertTo and CreateFrom use authored OneOf branches and external unions' public Kind, AsX, and SetX methods. Planning checks the method and type contracts; emitted conversions preserve exact external named scalar and collection types, including nested and optional values. Conversions that would have to name an inaccessible external Go type now fail during planning, with the type, location and direction in the error. Legal read-only conversions keep private scalar and collection-entry values when generated code does not need to name their types. This reuses the existing transform and package-name plans and does not inspect private union storage.

Generated HTTP headers use the underlying scalar kind for chained aliases while keeping the named service type and inherited validation. Generated protobuf-to-service conversions construct empty required arrays and maps after the wire discards collection presence; required JSON properties and singular protobuf presence retain their validation. Generated gRPC clients keep a stream after initial-send EOF so Recv or CloseAndRecv can read the server status. Stream opening also avoids the invoker's unary metadata capture options, while preserving caller-provided options and asynchronous opening.

Regenerate the complete generated tree with the updated Goa module and generator. Shared child declarations may move packages, so update handwritten Go imports and direct generated-type references where needed. This adds generator APIs but no DSL API, runtime mode, dependency, protobuf field/tag change, or persisted-data migration. These corrections do not require a coordinated server/client rollout. Restore the prior generator/module, design and generated tree together to roll back. Review package_placement.go, the retained import and helper-layout operations, http/codegen/wire_catalog.go, external conversion planning, HTTP alias shaping, and the two gRPC template changes first.

Final focused checks passed: external accessibility and readable-value controls (5 tests/19 subtests), external union and conversion preservation (6 tests/37 subtests), the generated direct/recursive collection transport fixture, and repository lint. The collection fixture compiles generated services and transports, crosses actual protobuf bytes in both directions, and retains required JSON, scalar/message presence, length and element checks. A separate baseline reproduced all five inaccessible-name compiler failures before the correction; the legal read-only controls passed before and after it. The unchanged original HTTP/gRPC/JSON-RPC relocation compilation test also passes. Final focused checks pass all ten enclosing-owner/constructor/roundtrip cases and forty built-in/custom-error, client-output and type-layout cases. The final complete core-codegen, HTTP and generator package run passes 2,160 tests/subtests. The complete JSON-RPC package passes 106 tests/subtests, with its existing optional check skipped; the original designed-error runtime test and all goldens remain unchanged. The mapped-field controls add 16 passing cases covering required/default/pointer rules, explicit names, shared children, recursion and all four HTTP request/response directions. The gRPC package passed the earlier broader run. Full CI results for the updated PR head are pending.

@raphael raphael changed the title Support external unions in ConvertTo and CreateFrom Fix shared type generation, external union conversions, and gRPC clients Sep 27, 2026
@raphael
raphael merged commit e82f6c1 into v3 Sep 28, 2026
7 checks passed
@raphael
raphael deleted the external-union-conversions branch September 28, 2026 07:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant