feat: add support for typesafe object flags#248
Conversation
Signed-off-by: Michael Beemer <michael.beemer@dynatrace.com>
|
Hey @kriscoleman and @tomflenner, I'm still experimenting with this but I figured I'd get early feedback from both of you. |
There was a problem hiding this comment.
Code Review
This pull request introduces support for an optional 'schema' field on object-type feature flags, enabling type-safe code generation and runtime validation across multiple languages (Go, TypeScript, Java, C#, Python). It includes updates to the CLI command structure to support a '--runtime-validation' flag, adds documentation for the new schema capabilities, and provides comprehensive test coverage for the generated code and validation logic. All review comments regarding missing trailing newlines in golden files and sample manifests have been identified as actionable improvements and are being addressed.
| return new GeneratedClient(Api.Instance.GetClient(domain)); | ||
| } | ||
| } | ||
| } No newline at end of file |
| } | ||
| } | ||
| } | ||
| } No newline at end of file |
| return client.getObjectDetails("themeCustomization", {"header":{"fontSize":16,"visible":true},"primaryColor":"#007bff","secondaryColor":"#6c757d","tags":["light"]}, context, { ...options, hooks: [...(options?.hooks ?? []), createThemeCustomizationHook()] }) as Promise<EvaluationDetails<ThemeCustomization>>; | ||
| }, | ||
| } | ||
| } No newline at end of file |
| } | ||
| } | ||
| } | ||
| } No newline at end of file |
|
|
||
| By default, object flags return generic types in generated code (`any` in Go, `JsonValue` in TypeScript, `object` in Python, etc.). This forces developers to manually cast values and provides no compile-time safety. | ||
|
|
||
| With the `schema` field, generated code includes: |
|
|
||
| ## Runtime Validation | ||
|
|
||
| When code is generated with `--runtime-validation` (enabled by default), the CLI generates OpenFeature `after` hooks that validate the provider's response at evaluation time. This catches cases where a feature flag provider returns an object that doesn't match the expected schema. |
There was a problem hiding this comment.
I am a little iffy about "runtime-validation" simply because it doesn't actually run at runtime, right?
Maybe simply type-validation or validation? 🤔
Considering the fact that this runs after generation and before runtime use.
| baseType = "bool" | ||
| case "array": | ||
| if schema.Items != nil { | ||
| baseType = "List<" + csharpSchemaType(schema.Items, true, parentName+"Item") + ">" |
There was a problem hiding this comment.
I think we should use an IEnumerable or IList here, for better C# SOLID practice
| if schema.Properties != nil { | ||
| baseType = parentName | ||
| } else { | ||
| baseType = "Dictionary<string, object>" |
There was a problem hiding this comment.
Similarly to above, I think it'll be more C# idiomatic here to use IDictionary<TKey, TValue>
|
[REVIEW AGENT] Automated code review — major findings and positive observations below. Major Findings1. C#
The Java generator already handles this correctly by deserializing into the typed record and returning a fully typed 2. Go: The generated Go code does: b, _ := json.Marshal(raw)
json.Unmarshal(b, &result)Both error return values are discarded. A marshal failure (e.g. a non-serializable type from the provider) or unmarshal failure (type mismatch) would silently return a zero-value struct — a subtle, hard-to-debug failure mode. For b, err := json.Marshal(raw)
if err != nil {
return openfeature.GenericEvaluationDetails[ThemeCustomizationValue]{}, fmt.Errorf("marshal: %w", err)
}
if err := json.Unmarshal(b, &result); err != nil {
return openfeature.GenericEvaluationDetails[ThemeCustomizationValue]{}, fmt.Errorf("unmarshal: %w", err)
}For the Positive Observations
|
kriscoleman
left a comment
There was a problem hiding this comment.
Looks pretty awesome so far!
I left a couple notes; one regarding the runtime-validation flag and another regarding Dependency Inversion Principle best practices for c#.
For other typed languages (typescript, java) I would also suggest reviewing the type matching and ensuring that any collection types (arrays, dicts) are using interfaces and not concretions.
I also dispatched a review agent which found a couple more nits.
Aside from that, it's looking great! 🥇 Really great improvements here, nice work!
This PR
Notes
This is a bit of an experiment. We can scrape the idea, but I think this could be very valuable as the line between feature flagging and dynamic configuration continues to blur.
Follow-up Tasks
I need to double-check that the validators work as expected in all SDKs.
How to test
I've updated all the tests and manually tested the Node.js generator.