Skip to content

feat(generators): expose underlying OpenFeature client on generated clients#236

Open
kriscoleman wants to merge 3 commits into
open-feature:mainfrom
kriscoleman:feat/expose-underlying-client
Open

feat(generators): expose underlying OpenFeature client on generated clients#236
kriscoleman wants to merge 3 commits into
open-feature:mainfrom
kriscoleman:feat/expose-underlying-client

Conversation

@kriscoleman

Copy link
Copy Markdown
Collaborator

Summary

  • Exposes the underlying OpenFeature SDK client on all generated clients, allowing users to perform ad-hoc flag evaluations beyond what is defined in their manifest
  • Each generator exposes the client in an idiomatic way for its language/framework:
    • Go: Exported Client package-level variable
    • Node.js: readonly client: Client property on GeneratedClient interface
    • Java: getOpenFeatureClient() method on GeneratedClient interface
    • C#: public IFeatureClient Client property on GeneratedClient
    • React: Re-exports useOpenFeatureClient hook from @openfeature/react-sdk
    • Angular: client getter on GeneratedFeatureFlagService returning the underlying FeatureFlagService
    • Python: Already publicly accessible via self.client (no change needed)
    • NestJS: Inherits client property from Node.js GeneratedClient (no change needed)

Test plan

  • All existing golden file tests pass
  • Go, Node.js, and C# integration tests updated to verify client accessibility
  • Verify generated Go code compiles with generated.Client access
  • Verify generated Node.js code works with client.client for ad-hoc evaluations
  • Verify generated Java code compiles with getOpenFeatureClient() method
  • Verify generated C# code compiles with Client property access
  • Verify React re-export of useOpenFeatureClient works in a React app
  • Verify Angular client getter works in an Angular app
  • Verify Python self.client continues to work as expected
  • Verify NestJS inherits client property correctly

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the code generation templates for Angular, C#, Go, Java, Node.js, and React to expose the underlying OpenFeature client, enabling ad-hoc flag evaluations. Review feedback identifies potential naming collisions in the Go and Node.js generators where the generic name 'client' might conflict with user-defined flags. It is recommended to use more specific identifiers, such as 'OpenFeatureClient' or 'openFeatureClient', to avoid compilation errors and ensure the generated code remains robust regardless of the flag manifest content.

Comment thread internal/generators/golang/golang.tmpl
Comment thread internal/generators/golang/golang.tmpl
Comment thread internal/generators/golang/golang.tmpl
Comment thread test/go-integration/test.go
Comment thread internal/generators/nodejs/nodejs.tmpl
Comment thread internal/generators/nodejs/nodejs.tmpl
Comment thread test/nodejs-integration/test.ts
@kriscoleman kriscoleman force-pushed the feat/expose-underlying-client branch from ad09654 to 87b6d22 Compare May 12, 2026 13:34
@kriscoleman kriscoleman requested a review from beeme1mr May 12, 2026 14:07
@kriscoleman

kriscoleman commented May 12, 2026

Copy link
Copy Markdown
Collaborator Author

let's drop last commit and keep with client, keep it a reserved word. We might be able to warn people if a flag matches. The real client should win out in a collision.

we discussed making it optional but the conditionals would get a bit messy

done.

@kriscoleman kriscoleman force-pushed the feat/expose-underlying-client branch from 87b6d22 to cac1f24 Compare May 12, 2026 16:42
…lients

Allow users to access the underlying OpenFeature SDK client for ad-hoc
flag evaluations beyond what is defined in the manifest.

Signed-off-by: Kris Coleman <kriscodeman@gmail.com>
…ion warning

Flag keys that transform to a reserved generator symbol (Client in Go,
client in Node.js) are excluded from the generated output and a warning
is emitted at generation time. Documents the reserved symbols per
generator in the generators README.

Signed-off-by: Kris Coleman <kriscodeman@gmail.com>
@kriscoleman kriscoleman force-pushed the feat/expose-underlying-client branch from cac1f24 to 879a742 Compare June 23, 2026 13:13
@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

All language generators (Angular, C#, Go, Java, Node.js, React) are updated to expose the underlying OpenFeature client for ad-hoc flag evaluations alongside the manifest-defined typed methods. Go, Node.js, and React generators add reserved-name collision detection that filters and warns on flags whose transformed keys collide with the newly reserved exported symbol names. A shared FilterReservedFlags utility function enables consistent filtering across generators. Golden files, integration tests, and README documentation are updated accordingly.

Changes

Expose Underlying OpenFeature Client Across All Generators

Layer / File(s) Summary
Shared reserved-name filtering utility
internal/generators/reserved.go, internal/generators/reserved_test.go
A new FilterReservedFlags function filters a flagset by applying a caller-provided key transformation and excluding any flags whose transformed symbol collides with a reserved name, logging a warning per collision. Table-driven tests cover collision scenarios for Camel-case (Go), lower-camel (Node.js), and hook-style (React) naming conventions.
Generator implementations: reserved-name filtering
internal/generators/golang/golang.go, internal/generators/nodejs/nodejs.go, internal/generators/react/react.go
Go, Node.js, and React generators each define a reservedNames map (Go: Client; Node.js: client; React: useOpenFeatureClient), import strcase for key transformation, and call generators.FilterReservedFlags in Generate() to exclude flags whose keys transform to reserved symbols.
Generator templates: expose underlying client
internal/generators/golang/golang.tmpl, internal/generators/nodejs/nodejs.tmpl, internal/generators/angular/angular.tmpl, internal/generators/csharp/csharp.tmpl, internal/generators/java/java.tmpl, internal/generators/react/react.tmpl
Templates across all six generators are updated to expose the underlying OpenFeature client: Go promotes client to exported Client; Node.js adds readonly client: Client to the GeneratedClient interface and factory return; Angular adds a client getter; C# adds a public Client property; Java adds getOpenFeatureClient() to the interface and its implementation; React re-exports useOpenFeatureClient.
Golden test data
internal/cmd/testdata/success_*.golden
All six golden files are updated to match the new template output, reflecting the exported client symbols, updated interface members, and flag evaluation calls for each language.
Integration tests
test/go-integration/test.go, test/nodejs-integration/test.ts, test/csharp-integration/Program.cs
Go, Node.js, and C# integration tests add runtime assertions that the generated client exposes the underlying OpenFeature client via generated.Client, client.client, and client.Client respectively.
Documentation
internal/generators/README.md
New "Reserved Keywords" section documents reserved exported symbols per generator, the flag-key transformation functions used to detect collisions, the exclude-and-warn behavior on key collision, and guidance for generator authors to use FilterReservedFlags in Generate().

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.11% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main change: exposing the underlying OpenFeature client on generated clients across all language generators.
Description check ✅ Passed The description is well-detailed and directly related to the changeset, explaining how each generator exposes the client idiomatically and including a comprehensive test plan.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 markdownlint-cli2 (0.22.1)
internal/generators/README.md

markdownlint-cli2 v0.22.1 (markdownlint v0.40.0)
Error: Unable to use configuration file '/coderabbit-0.markdownlint-cli2.jsonc'; ENOENT: no such file or directory, open '/coderabbit-0.markdownlint-cli2.jsonc'
at throwForConfigurationFile (file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2.mjs:48:9)
at readOptionsOrConfig (file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2.mjs:169:5)
at async main (file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2.mjs:927:21)
at async file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2-bin.mjs:14:22 {
[cause]: Error: ENOENT: no such file or directory, open '/coderabbit-0.markdownlint-cli2.jsonc'
at async open (node:internal/fs/promises:640:25)
at async Object.readFile (node:internal/fs/promises:1287:14)
at async readOptionsOrConfig (file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2.mjs:141:17)
at async main (file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2.mjs:927:21)
at async file:///usr/local/lib/node_modules/markdownlint-cli2/markdownlint-cli2-bin.mjs:14:22 {
errno: -2,
code: 'ENOENT',
syscall: 'open',
path: '/coderabbit-0.markdownlint-cli2.jsonc'
}
}


Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/generators/react/react.tmpl`:
- Around line 9-11: The Generate() method in the React generator needs to
implement reserved-name filtering to prevent collisions with the re-exported
useOpenFeatureClient symbol. Create a reservedNames map that includes at minimum
"useOpenFeatureClient", then call FilterFlagset() to filter the flagset before
passing it to the template rendering, following the same pattern implemented in
the golang.go and nodejs.go generators.

In `@internal/generators/README.md`:
- Around line 71-73: The fenced code block containing the warning message about
the "client" flag is missing a language identifier, which triggers the
markdownlint MD040 rule. Add the language identifier "text" after the opening
triple backticks on the line before "Flag "client" transforms to "Client"" to
properly specify the code block language and resolve the linting issue.

In `@test/go-integration/test.go`:
- Around line 108-110: The current assertion on line 109 only verifies that
generated.Client exists at compile-time by assigning it to the blank identifier.
Replace the blank identifier assignment with an explicit nil check using a
conditional or assertion statement that will cause the test to fail if
generated.Client is nil at runtime, ensuring the underlying client is actually
exposed and accessible during test execution.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 982d8ac5-af8f-4798-b230-9e644bc5d9c5

📥 Commits

Reviewing files that changed from the base of the PR and between 784196c and 879a742.

📒 Files selected for processing (18)
  • internal/cmd/testdata/success_angular.golden
  • internal/cmd/testdata/success_csharp.golden
  • internal/cmd/testdata/success_go.golden
  • internal/cmd/testdata/success_java.golden
  • internal/cmd/testdata/success_nodejs.golden
  • internal/cmd/testdata/success_react.golden
  • internal/generators/README.md
  • internal/generators/angular/angular.tmpl
  • internal/generators/csharp/csharp.tmpl
  • internal/generators/golang/golang.go
  • internal/generators/golang/golang.tmpl
  • internal/generators/java/java.tmpl
  • internal/generators/nodejs/nodejs.go
  • internal/generators/nodejs/nodejs.tmpl
  • internal/generators/react/react.tmpl
  • test/csharp-integration/Program.cs
  • test/go-integration/test.go
  • test/nodejs-integration/test.ts

Comment thread internal/generators/react/react.tmpl
Comment thread internal/generators/README.md
Comment thread test/go-integration/test.go
The React generator re-exports the useOpenFeatureClient hook but did not
guard against a flag key transforming into that same name. A flag key such
as "openFeatureClient" would generate `export const useOpenFeatureClient`,
duplicating the re-exported symbol and breaking the generated TypeScript.

React now filters reserved symbols like the Go and Node.js generators do.
The duplicated inline filter loops are consolidated into a shared
generators.FilterReservedFlags helper covered by a unit test.

C# and Java intentionally do not reserve a name: their exposed client
symbols (a `Client` property and a no-arg `getOpenFeatureClient()`) cannot
collide with the `<Pascal>Async`/`<camel>(ctx)` members generated from flag
keys, so a guard there would falsely exclude valid flags.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Kris Coleman <kriscodeman@gmail.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
internal/generators/react/react.go (1)

47-52: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Fix comment to match implementation.

The comment on line 48 states "use + ToPascal" but the implementation on line 56 uses strcase.ToCamel. While ToPascal and ToCamel are functionally equivalent (both produce PascalCase), the comment should match the code for clarity.

📝 Suggested fix
 // reservedNames are symbols exported by the React generator itself. Flag keys
-// whose generated hook name (use + ToPascal) matches one of these will be
+// whose generated hook name (use + ToCamel) matches one of these will be
 // excluded from the generated output and a warning will be emitted.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/generators/react/react.go` around lines 47 - 52, The comment above
the reservedNames map states that the generated hook name uses "use + ToPascal"
but the actual implementation uses strcase.ToCamel. Update the comment on line
48 to replace "ToPascal" with "ToCamel" to accurately reflect the implementation
of the hook name generation logic.
internal/generators/reserved_test.go (1)

18-80: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider additional test cases for edge scenarios.

The current test suite covers basic collision detection but misses some edge cases:

  1. Empty input flagset
  2. All flags colliding (should return empty flagset)
  3. Preservation of flag fields beyond Key and Type (if any exist)

Additionally, line 75 uses t.Fatalf inside a loop, which stops at the first mismatch. Consider t.Errorf to report all differences.

📝 Suggested additional test cases
+		{
+			name:      "empty flagset",
+			generator: "Go",
+			reserved:  map[string]bool{"Client": true},
+			transform: strcase.ToCamel,
+			input:     []string{},
+			want:      []string{},
+		},
+		{
+			name:      "all flags collide",
+			generator: "Go",
+			reserved:  map[string]bool{"Client": true, "Config": true},
+			transform: strcase.ToCamel,
+			input:     []string{"client", "config"},
+			want:      []string{},
+		},

And improve the assertion:

 			for i := range got {
 				if got[i] != tt.want[i] {
-					t.Fatalf("got %v, want %v", got, tt.want)
+					t.Errorf("index %d: got %v, want %v", i, got, tt.want)
 				}
 			}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/generators/reserved_test.go` around lines 18 - 80, The
TestFilterReservedFlags function needs additional test coverage for edge cases
and improved assertion logic. Add new test cases to cover empty input flagset,
scenario where all flags collide with reserved names and should return empty
result, and preservation of flag fields beyond Key and Type. Additionally, in
the test loop where the output is compared against expected values, replace
t.Fatalf with t.Errorf so that all mismatches are reported rather than stopping
at the first difference, allowing better visibility into all failures in a
single test run.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@internal/generators/react/react.go`:
- Around line 47-52: The comment above the reservedNames map states that the
generated hook name uses "use + ToPascal" but the actual implementation uses
strcase.ToCamel. Update the comment on line 48 to replace "ToPascal" with
"ToCamel" to accurately reflect the implementation of the hook name generation
logic.

In `@internal/generators/reserved_test.go`:
- Around line 18-80: The TestFilterReservedFlags function needs additional test
coverage for edge cases and improved assertion logic. Add new test cases to
cover empty input flagset, scenario where all flags collide with reserved names
and should return empty result, and preservation of flag fields beyond Key and
Type. Additionally, in the test loop where the output is compared against
expected values, replace t.Fatalf with t.Errorf so that all mismatches are
reported rather than stopping at the first difference, allowing better
visibility into all failures in a single test run.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: b055423f-b9f5-4827-9e3a-b10923c29a0b

📥 Commits

Reviewing files that changed from the base of the PR and between 879a742 and 7390c51.

📒 Files selected for processing (6)
  • internal/generators/README.md
  • internal/generators/golang/golang.go
  • internal/generators/nodejs/nodejs.go
  • internal/generators/react/react.go
  • internal/generators/reserved.go
  • internal/generators/reserved_test.go
✅ Files skipped from review due to trivial changes (1)
  • internal/generators/README.md

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