feat(generators): expose underlying OpenFeature client on generated clients#236
feat(generators): expose underlying OpenFeature client on generated clients#236kriscoleman wants to merge 1 commit intoopen-feature:mainfrom
Conversation
…lients Allow users to access the underlying OpenFeature SDK client for ad-hoc flag evaluations beyond what is defined in the manifest.
There was a problem hiding this comment.
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.
| // Client is the underlying OpenFeature client used for flag evaluations. | ||
| // It can be used directly for ad-hoc flag evaluations beyond what is defined in the manifest. | ||
| var Client = openfeature.NewDefaultClient() |
There was a problem hiding this comment.
Using Client as the name for the exported package-level variable may cause a naming collision if a feature flag in the manifest is also named client. When transformed via ToPascal, the flag variable would also be named Client. Consider using a more distinct name like OpenFeatureClient to avoid this risk.
// OpenFeatureClient is the underlying OpenFeature client used for flag evaluations.
// It can be used directly for ad-hoc flag evaluations beyond what is defined in the manifest.
var OpenFeatureClient = openfeature.NewDefaultClient()
| Stringer: stringer({{ .Key | Quote }}), | ||
| Value: func(ctx context.Context, evalCtx openfeature.EvaluationContext) {{- if eq (.Type | OpenFeatureType) "Object"}}any{{- else}}{{ .Type | TypeString }}{{- end}} { | ||
| return client.{{ .Type | OpenFeatureType }}(ctx, {{ .Key | Quote }}, {{ if eq (.Type | OpenFeatureType) "Object" }}{{.DefaultValue | ToMapLiteral }}{{- else }}{{ .DefaultValue | QuoteString }}{{- end}}, evalCtx) | ||
| return Client.{{ .Type | OpenFeatureType }}(ctx, {{ .Key | Quote }}, {{ if eq (.Type | OpenFeatureType) "Object" }}{{.DefaultValue | ToMapLiteral }}{{- else }}{{ .DefaultValue | QuoteString }}{{- end}}, evalCtx) |
There was a problem hiding this comment.
Update this reference to match the suggested name change for the exported client variable.
return OpenFeatureClient.{{ .Type | OpenFeatureType }}(ctx, {{ .Key | Quote }}, {{ if eq (.Type | OpenFeatureType) "Object" }}{{.DefaultValue | ToMapLiteral }}{{- else }}{{ .DefaultValue | QuoteString }}{{- end}}, evalCtx)
| }, | ||
| ValueWithDetails: func(ctx context.Context, evalCtx openfeature.EvaluationContext) (openfeature.GenericEvaluationDetails[{{- if eq (.Type | OpenFeatureType) "Object"}}any{{- else}}{{ .Type | TypeString }}{{- end}}], error){ | ||
| return client.{{ .Type | OpenFeatureType }}ValueDetails(ctx, {{ .Key | Quote }}, {{ if eq (.Type | OpenFeatureType) "Object" }}{{.DefaultValue | ToMapLiteral }}{{- else }}{{ .DefaultValue | QuoteString }}{{- end}}, evalCtx) | ||
| return Client.{{ .Type | OpenFeatureType }}ValueDetails(ctx, {{ .Key | Quote }}, {{ if eq (.Type | OpenFeatureType) "Object" }}{{.DefaultValue | ToMapLiteral }}{{- else }}{{ .DefaultValue | QuoteString }}{{- end}}, evalCtx) |
There was a problem hiding this comment.
Update this reference to match the suggested name change for the exported client variable.
return OpenFeatureClient.{{ .Type | OpenFeatureType }}ValueDetails(ctx, {{ .Key | Quote }}, {{ if eq (.Type | OpenFeatureType) "Object" }}{{.DefaultValue | ToMapLiteral }}{{- else }}{{ .DefaultValue | QuoteString }}{{- end}}, evalCtx)
| fmt.Printf("themeCustomization: %v\n", themeCustomization) | ||
|
|
||
| // Verify the underlying client is accessible for ad-hoc evaluations | ||
| _ = generated.Client |
| /** The underlying OpenFeature client for ad-hoc flag evaluations. */ | ||
| readonly client: Client; |
There was a problem hiding this comment.
Adding a client property to the GeneratedClient interface introduces a potential naming collision with any feature flag named client. Since flags are exposed as methods on this interface (e.g., client(...)), having a property and a method with the same name will result in a TypeScript compilation error. Consider using a more specific name such as openFeatureClient.
/** The underlying OpenFeature client for ad-hoc flag evaluations. */
readonly openFeatureClient: Client;
| const client = domain ? OpenFeature.getClient(domain, context) : OpenFeature.getClient(context) | ||
|
|
||
| return { | ||
| client, |
| if (!client.client) { | ||
| throw new Error('Underlying OpenFeature client not exposed'); | ||
| } |
There was a problem hiding this comment.
Summary
Clientpackage-level variablereadonly client: Clientproperty onGeneratedClientinterfacegetOpenFeatureClient()method onGeneratedClientinterfacepublic IFeatureClient Clientproperty onGeneratedClientuseOpenFeatureClienthook from@openfeature/react-sdkclientgetter onGeneratedFeatureFlagServicereturning the underlyingFeatureFlagServiceself.client(no change needed)clientproperty from Node.jsGeneratedClient(no change needed)Test plan
generated.Clientaccessclient.clientfor ad-hoc evaluationsgetOpenFeatureClient()methodClientproperty accessuseOpenFeatureClientworks in a React appclientgetter works in an Angular appself.clientcontinues to work as expected