Skip to content

Conversation

@kylejuliandev
Copy link
Contributor

This PR

Initial PR to add support for syncing context state from Flagd for use by the in process resolver. This was discovered while I was adding support for the newer flagd-testbed Gherkin tests.

Related Issues

Not sure we have any related issues for this at the moment, but this should bring the dotnet flagd provider in line with the others.

Notes

Follow-up Tasks

How to test

@kylejuliandev kylejuliandev added the provider:flagd Issues related to provider flagd label Dec 2, 2025
@kylejuliandev kylejuliandev changed the title feat(flagd): Add code to sync context from Flagd for the InProcess resolver [work in progress] feat(flagd): Add code to sync context from Flagd for the InProcess resolver Dec 3, 2025
@kylejuliandev kylejuliandev marked this pull request as ready for review December 3, 2025 23:21
@kylejuliandev kylejuliandev requested review from a team as code owners December 3, 2025 23:21
@kylejuliandev
Copy link
Contributor Author

/gemini review

Copy link

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

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 introduces support for syncing context from Flagd for the in-process resolver, which is a great feature to align with other flagd providers. The changes are well-structured, decoupling the resolvers from the provider's event channel by using a callback mechanism. The introduction of FlagdProviderEvent and SyncMetadataHook is clean. The new functionality is also well-covered by unit tests.

I have a couple of suggestions to improve maintainability by reducing code duplication. One is in FlagdProvider where event handling logic is repeated, and the other is regarding the protobuf value conversion logic which is duplicated between InProcessResolver and RpcResolver.

Comment on lines +128 to +133
var context = EvaluationContext.Builder();
foreach (var item in payload.SyncMetadata.AsDictionary())
{
context.Set(item.Key, item.Value);
}
this._enrichedContext = context.Build();

Choose a reason for hiding this comment

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

medium

This block of code for building the EvaluationContext is duplicated in the ProviderReady case (lines 159-164). To improve maintainability and reduce code duplication, consider extracting this logic into a private helper method or a local function.

Comment on lines +166 to 193
private static Value ExtractValue(Google.Protobuf.WellKnownTypes.Value value)
{
_mtx.WaitOne();
_eventStreamRetryBackoff = Math.Min(_eventStreamRetryBackoff * 2, MaxEventStreamRetryBackoff);
if (connected)
switch (value.KindCase)
{
connected = false;
_eventChannel.Writer.TryWrite(new ProviderEventPayload { Type = ProviderEventTypes.ProviderError, ProviderName = _providerMetadata.Name });
case Google.Protobuf.WellKnownTypes.Value.KindOneofCase.BoolValue:
return new Value(value.BoolValue);
case Google.Protobuf.WellKnownTypes.Value.KindOneofCase.NumberValue:
return new Value(value.NumberValue);
case Google.Protobuf.WellKnownTypes.Value.KindOneofCase.StringValue:
return new Value(value.StringValue);
case Google.Protobuf.WellKnownTypes.Value.KindOneofCase.StructValue:
{
var val = Structure.Builder();
foreach (var item in value.StructValue.Fields)
{
val.Set(item.Key, ExtractValue(item.Value));
}
return new Value(val.Build());
}
case Google.Protobuf.WellKnownTypes.Value.KindOneofCase.ListValue:
return new Value(value.ListValue.Values.Select(ExtractValue));
case Google.Protobuf.WellKnownTypes.Value.KindOneofCase.NullValue:
case Google.Protobuf.WellKnownTypes.Value.KindOneofCase.None:
break;
}
_mtx.ReleaseMutex();
await Task.Delay(_eventStreamRetryBackoff * 1000).ConfigureAwait(false);
}

private void HandleProviderChangeEvent()
{
_mtx.WaitOne();
if (connected)
{
_eventChannel.Writer.TryWrite(new ProviderEventPayload { Type = ProviderEventTypes.ProviderConfigurationChanged, ProviderName = _providerMetadata.Name });
}
_mtx.ReleaseMutex();
return new Value();
}

Choose a reason for hiding this comment

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

medium

The ExtractValue method is very useful. However, similar logic for converting Google.Protobuf.WellKnownTypes.Value to OpenFeature.Model.Value exists in RpcResolver.cs (spread across ConvertToValue, ConvertObjectToValue, and ConvertToPrimitiveValue methods). To improve code reuse and maintainability, consider moving this conversion logic to a shared utility class that both InProcessResolver and RpcResolver can use.

@askpt
Copy link
Member

askpt commented Dec 4, 2025

@kylejuliandev Is this related to #478?

@kylejuliandev
Copy link
Contributor Author

@kylejuliandev Is this related to #478?

I don't think so, that appears to be related to some environment variable changes in the flagd testbed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

provider:flagd Issues related to provider flagd

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants