Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/OpenFeature.Contrib.Providers.Flagd/FlagdProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public FlagdProvider(FlagdConfig config)

if (_config.ResolverType == ResolverType.IN_PROCESS)
{
var jsonSchemaValidator = new JsonSchemaValidator(null, _config.Logger);
var jsonSchemaValidator = new JsonSchemaValidator(_config.Logger);
_resolver = new InProcessResolver(_config, jsonSchemaValidator);
}
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@
<None Include="README.md" Pack="true" PackagePath="\" />
</ItemGroup>

<ItemGroup>
<EmbeddedResource Include="schemas\json\flags.json" Link="Resources\flags.json" />
<EmbeddedResource Include="schemas\json\targeting.json" Link="Resources\targeting.json" />
</ItemGroup>
<ItemGroup>
<!-- The schema.proto file referenced here will be used to automatically generate the Grpc
client when executing 'dotnet build' -->
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

#nullable enable

namespace OpenFeature.Contrib.Providers.Flagd.Resolver.InProcess;

internal sealed class FlagdJsonSchemaEmbeddedResourceReader : IFlagdJsonSchemaProvider
{
const string TargetingJsonResourceName = "OpenFeature.Contrib.Providers.Flagd.Resources.targeting.json";
const string FlagJsonResourceName = "OpenFeature.Contrib.Providers.Flagd.Resources.flags.json";

public Task<string> ReadTargetingSchemaAsync(CancellationToken cancellationToken = default)
{
return this.ReadAsStringAsync(TargetingJsonResourceName, cancellationToken);
}

public Task<string> ReadFlagSchemaAsync(CancellationToken cancellationToken = default)
{
return this.ReadAsStringAsync(FlagJsonResourceName, cancellationToken);
}

private async Task<string> ReadAsStringAsync(string resourceName, CancellationToken cancellationToken = default)
{
var assembly = typeof(FlagdJsonSchemaEmbeddedResourceReader).Assembly;
if (assembly == null)
{
throw new InvalidOperationException($"Unable to locate assembly for {nameof(FlagdJsonSchemaEmbeddedResourceReader)}.");
}

using var stream = assembly.GetManifestResourceStream(resourceName);
if (stream == null)
{
throw new InvalidOperationException($"Embedded resource not found: '{resourceName}'.");
}

using var streamReader = new StreamReader(stream);

#if NET8_0_OR_GREATER
return await streamReader.ReadToEndAsync(cancellationToken).ConfigureAwait(false);
#else
return await streamReader.ReadToEndAsync().ConfigureAwait(false);
#endif
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System.Threading;
using System.Threading.Tasks;

namespace OpenFeature.Contrib.Providers.Flagd.Resolver.InProcess;

#nullable enable

internal interface IFlagdJsonSchemaProvider
{
Task<string> ReadTargetingSchemaAsync(CancellationToken cancellationToken = default);

Task<string> ReadFlagSchemaAsync(CancellationToken cancellationToken = default);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Threading;
using System.Threading.Tasks;

#nullable enable

namespace OpenFeature.Contrib.Providers.Flagd.Resolver.InProcess;

internal interface IJsonSchemaValidator
{
Task InitializeAsync(CancellationToken cancellationToken = default);
void Validate(string configuration);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
Expand All @@ -8,62 +7,29 @@

namespace OpenFeature.Contrib.Providers.Flagd.Resolver.InProcess;

internal interface IJsonSchemaValidator
{
Task InitializeAsync(CancellationToken cancellationToken = default);
void Validate(string configuration);
}

internal class JsonSchemaValidator : IJsonSchemaValidator
{
private readonly HttpClient _client;
private readonly ILogger _logger;
private readonly IFlagdJsonSchemaProvider _flagdJsonSchemaProvider;

private JsonSchema _validator;

internal JsonSchemaValidator(HttpClient client, ILogger logger)
internal JsonSchemaValidator(ILogger logger)
: this(logger, new FlagdJsonSchemaEmbeddedResourceReader())
{
if (client == null)
{
client = new HttpClient
{
BaseAddress = new Uri("https://flagd.dev"),
};
}
}

_client = client;
_logger = logger;
internal JsonSchemaValidator(ILogger logger, IFlagdJsonSchemaProvider flagdJsonSchemaProvider)
{
this._logger = logger;
this._flagdJsonSchemaProvider = flagdJsonSchemaProvider;
}

public async Task InitializeAsync(CancellationToken cancellationToken = default)
{
try
{
var targetingTask = _client.GetAsync("/schema/v0/targeting.json", cancellationToken);
var flagTask = _client.GetAsync("/schema/v0/flags.json", cancellationToken);

await Task.WhenAll(targetingTask, flagTask).ConfigureAwait(false);

var targeting = targetingTask.Result;
var flag = flagTask.Result;

if (!targeting.IsSuccessStatusCode)
{
_logger.LogWarning("Unable to retrieve Flagd targeting JSON Schema, status code: {StatusCode}", targeting.StatusCode);
return;
}

if (!flag.IsSuccessStatusCode)
{
_logger.LogWarning("Unable to retrieve Flagd flags JSON Schema, status code: {StatusCode}", flag.StatusCode);
return;
}

#if NET5_0_OR_GREATER
var targetingJson = await targeting.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
#else
var targetingJson = await targeting.Content.ReadAsStringAsync().ConfigureAwait(false);
#endif

var targetingJson = await this._flagdJsonSchemaProvider.ReadTargetingSchemaAsync(cancellationToken).ConfigureAwait(false);
var targetingSchema = await JsonSchema.FromJsonAsync(targetingJson, "targeting.json", schema =>
{
var schemaResolver = new JsonSchemaResolver(schema, new SystemTextJsonSchemaGeneratorSettings());
Expand All @@ -72,11 +38,7 @@ public async Task InitializeAsync(CancellationToken cancellationToken = default)
return resolver;
}, cancellationToken).ConfigureAwait(false);

#if NET5_0_OR_GREATER
var flagJson = await flag.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false);
#else
var flagJson = await flag.Content.ReadAsStringAsync().ConfigureAwait(false);
#endif
var flagJson = await this._flagdJsonSchemaProvider.ReadFlagSchemaAsync(cancellationToken).ConfigureAwait(false);
var flagSchema = await JsonSchema.FromJsonAsync(flagJson, "flags.json", schema =>
{
var schemaResolver = new JsonSchemaResolver(schema, new SystemTextJsonSchemaGeneratorSettings());
Expand Down
Loading