Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Process json key-values without reflection #538

Merged
merged 34 commits into from
Apr 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
a990049
WIP adding utf8jsonreader
amerjusupovic Mar 22, 2024
17bec87
WIP jsondocument
amerjusupovic Mar 22, 2024
0c8edb6
replace all json adapters with jsondocument
amerjusupovic Mar 22, 2024
148ebb7
combine code to serialize feature flag with code for adding key value…
amerjusupovic Mar 22, 2024
282211e
remove unused classes from jsonserializer approach
amerjusupovic Mar 22, 2024
b692eb3
remove jsonserializer from testhelpers
amerjusupovic Mar 22, 2024
380fdf0
WIP add handling and error messages for invalid values
amerjusupovic Mar 22, 2024
1af7017
WIP adding error messages
amerjusupovic Mar 22, 2024
d832b5a
use method to create formatexception
amerjusupovic Mar 26, 2024
eaffbdb
validate for key vault uri and throw exception, fix logic in featurem…
amerjusupovic Mar 26, 2024
a6a3343
update error message, fix expected type for enabled
amerjusupovic Mar 26, 2024
36c5b16
WIP
amerjusupovic Mar 26, 2024
4676f39
WIP testing aot compatible
amerjusupovic Mar 26, 2024
08276ec
small logic updates
amerjusupovic Mar 27, 2024
3b17e13
improve variable names
amerjusupovic Mar 27, 2024
61b160b
edit error message
amerjusupovic Mar 27, 2024
88fd3fe
add isaotcompatible property to packages
amerjusupovic Mar 28, 2024
85cc8d5
fix error message for bool
amerjusupovic Apr 3, 2024
e06c167
Merge branch 'main' into ajusupovic/replace-jsonserializer
amerjusupovic Apr 3, 2024
cb75e10
Merge branch 'main' of https://github.com/Azure/AppConfiguration-Dotn…
amerjusupovic Apr 17, 2024
ea15264
WIP use utf8jsonreader
amerjusupovic Apr 17, 2024
57257f1
WIP
amerjusupovic Apr 17, 2024
016f5a1
use utf8jsonreader for feature flags
amerjusupovic Apr 18, 2024
c588780
use utf8jsonreader for keyvault secret reference
amerjusupovic Apr 18, 2024
1d4b7a0
PR comment revision
amerjusupovic Apr 18, 2024
8beb4ae
Merge branch 'main' of https://github.com/Azure/AppConfiguration-Dotn…
amerjusupovic Apr 22, 2024
696d6fd
WIP adding tests, PR revisions
amerjusupovic Apr 22, 2024
dc998d9
WIP add some tests, PR revisions
amerjusupovic Apr 22, 2024
a94611e
key vault tests, use keyvaultreferenceexception for all scenarios bec…
amerjusupovic Apr 23, 2024
f7ebb1f
fix missing setup
amerjusupovic Apr 24, 2024
c0ba5ce
PR revisions
amerjusupovic Apr 25, 2024
4bfbf39
update tests again, PR revisions
amerjusupovic Apr 25, 2024
e3662c0
don't handle invalidoperationexception
amerjusupovic Apr 25, 2024
0f51238
remove unused exception var
amerjusupovic Apr 26, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@

<PropertyGroup>
<CodeAnalysisRuleSet>..\..\AzureAppConfigurationRules.ruleset</CodeAnalysisRuleSet>
<EnableNETAnalyzers>True</EnableNETAnalyzers>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<IsAotCompatible>true</IsAotCompatible>
</PropertyGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@

<PropertyGroup>
<CodeAnalysisRuleSet>..\..\AzureAppConfigurationRules.ruleset</CodeAnalysisRuleSet>
<EnableNETAnalyzers>True</EnableNETAnalyzers>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<IsAotCompatible>true</IsAotCompatible>
</PropertyGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -29,22 +29,12 @@ public AzureKeyVaultKeyValueAdapter(AzureKeyVaultSecretProvider secretProvider)
/// returns the keyname and actual value
public async Task<IEnumerable<KeyValuePair<string, string>>> ProcessKeyValue(ConfigurationSetting setting, Logger logger, CancellationToken cancellationToken)
{
KeyVaultSecretReference secretRef;

// Content validation
try
{
secretRef = JsonSerializer.Deserialize<KeyVaultSecretReference>(setting.Value);
}
catch (JsonException e)
{
throw CreateKeyVaultReferenceException("Invalid Key Vault reference.", setting, e, null);
}
string secretRefUri = ParseSecretReferenceUri(setting);

// Uri validation
if (string.IsNullOrEmpty(secretRef.Uri) || !Uri.TryCreate(secretRef.Uri, UriKind.Absolute, out Uri secretUri) || !KeyVaultSecretIdentifier.TryCreate(secretUri, out KeyVaultSecretIdentifier secretIdentifier))
if (string.IsNullOrEmpty(secretRefUri) || !Uri.TryCreate(secretRefUri, UriKind.Absolute, out Uri secretUri) || !KeyVaultSecretIdentifier.TryCreate(secretUri, out KeyVaultSecretIdentifier secretIdentifier))
{
throw CreateKeyVaultReferenceException("Invalid Key vault secret identifier.", setting, null, secretRef);
throw CreateKeyVaultReferenceException("Invalid Key vault secret identifier.", setting, null, secretRefUri);
}

string secret;
Expand All @@ -55,11 +45,11 @@ public async Task<IEnumerable<KeyValuePair<string, string>>> ProcessKeyValue(Con
}
catch (Exception e) when (e is UnauthorizedAccessException || (e.Source?.Equals(AzureIdentityAssemblyName, StringComparison.OrdinalIgnoreCase) ?? false))
{
throw CreateKeyVaultReferenceException(e.Message, setting, e, secretRef);
throw CreateKeyVaultReferenceException(e.Message, setting, e, secretRefUri);
}
catch (Exception e) when (e is RequestFailedException || ((e as AggregateException)?.InnerExceptions?.All(e => e is RequestFailedException) ?? false))
{
throw CreateKeyVaultReferenceException("Key vault error.", setting, e, secretRef);
throw CreateKeyVaultReferenceException("Key vault error.", setting, e, secretRefUri);
}

return new KeyValuePair<string, string>[]
Expand All @@ -68,15 +58,15 @@ public async Task<IEnumerable<KeyValuePair<string, string>>> ProcessKeyValue(Con
};
}

KeyVaultReferenceException CreateKeyVaultReferenceException(string message, ConfigurationSetting setting, Exception inner, KeyVaultSecretReference secretRef = null)
KeyVaultReferenceException CreateKeyVaultReferenceException(string message, ConfigurationSetting setting, Exception inner, string secretRefUri = null)
{
return new KeyVaultReferenceException(message, inner)
{
Key = setting.Key,
Label = setting.Label,
Etag = setting.ETag.ToString(),
ErrorCode = (inner as RequestFailedException)?.ErrorCode,
SecretIdentifier = secretRef?.Uri
SecretIdentifier = secretRefUri
};
}

Expand All @@ -102,5 +92,50 @@ public bool NeedsRefresh()
{
return _secretProvider.ShouldRefreshKeyVaultSecrets();
}

private string ParseSecretReferenceUri(ConfigurationSetting setting)
{
string secretRefUri = null;

try
{
var reader = new Utf8JsonReader(System.Text.Encoding.UTF8.GetBytes(setting.Value));

if (reader.Read() && reader.TokenType != JsonTokenType.StartObject)
{
throw CreateKeyVaultReferenceException(ErrorMessages.InvalidKeyVaultReference, setting, null, null);
}

while (reader.Read() && reader.TokenType != JsonTokenType.EndObject)
{
if (reader.TokenType != JsonTokenType.PropertyName)
{
continue;
}

if (reader.GetString() == KeyVaultConstants.SecretReferenceUriJsonPropertyName)
{
if (reader.Read() && reader.TokenType == JsonTokenType.String)
{
secretRefUri = reader.GetString();
}
else
{
throw CreateKeyVaultReferenceException(ErrorMessages.InvalidKeyVaultReference, setting, null, null);
}
}
else
{
reader.Skip();
}
}
}
catch (JsonException e)
{
throw CreateKeyVaultReferenceException(ErrorMessages.InvalidKeyVaultReference, setting, e, null);
}

return secretRefUri;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,7 @@ namespace Microsoft.Extensions.Configuration.AzureAppConfiguration.AzureKeyVault
internal class KeyVaultConstants
{
public const string ContentType = "application/vnd.microsoft.appconfig.keyvaultref+json";

public const string SecretReferenceUriJsonPropertyName = "uri";
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,8 @@ internal class ErrorMessages
{
public const string CacheExpirationTimeTooShort = "The cache expiration time cannot be less than {0} milliseconds.";
public const string SecretRefreshIntervalTooShort = "The secret refresh interval cannot be less than {0} milliseconds.";
public const string FeatureFlagInvalidJsonProperty = "Invalid property '{0}' for feature flag. Key: '{1}'. Found type: '{2}'. Expected type: '{3}'.";
public const string FeatureFlagInvalidFormat = "Invalid json format for feature flag. Key: '{0}'";
public const string InvalidKeyVaultReference = "Invalid Key Vault reference.";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,29 +31,59 @@ public static bool TryCreatePushNotification(this EventGridEvent eventGridEvent,

if (Uri.TryCreate(eventGridEvent.Subject, UriKind.Absolute, out Uri resourceUri))
{
JsonElement eventGridEventData;
string syncToken = null;

try
{
eventGridEventData = JsonDocument.Parse(eventGridEvent.Data.ToString()).RootElement;
var reader = new Utf8JsonReader(System.Text.Encoding.UTF8.GetBytes(eventGridEvent.Data.ToString()));

if (reader.Read() && reader.TokenType != JsonTokenType.StartObject)
{
return false;
}

while (reader.Read() && reader.TokenType != JsonTokenType.EndObject)
{
if (reader.TokenType != JsonTokenType.PropertyName)
{
continue;
}

if (reader.GetString() == SyncTokenPropertyName)
{
if (reader.Read() && reader.TokenType == JsonTokenType.String)
{
syncToken = reader.GetString();
}
else
{
return false;
}
}
else
{
reader.Skip();
}
}
}
catch (JsonException)
{
return false;
}

if (eventGridEventData.ValueKind == JsonValueKind.Object &&
eventGridEventData.TryGetProperty(SyncTokenPropertyName, out JsonElement syncTokenJson) &&
syncTokenJson.ValueKind == JsonValueKind.String)
if (syncToken == null)
{
pushNotification = new PushNotification()
{
SyncToken = syncTokenJson.GetString(),
EventType = eventGridEvent.EventType,
ResourceUri = resourceUri
};
return true;
return false;
}

pushNotification = new PushNotification()
{
SyncToken = syncToken,
EventType = eventGridEvent.EventType,
ResourceUri = resourceUri
};

return true;
}

return false;
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,13 @@
// Licensed under the MIT license.
//
using System.Text.Json;
using System.Text.Json.Serialization;

namespace Microsoft.Extensions.Configuration.AzureAppConfiguration.FeatureManagement
{
internal class ClientFilter
{
[JsonPropertyName("name")]
public string Name { get; set; }

[JsonPropertyName("parameters")]
public JsonElement Parameters { get; set; }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,13 @@
// Licensed under the MIT license.
//
using System.Collections.Generic;
using System.Text.Json.Serialization;

namespace Microsoft.Extensions.Configuration.AzureAppConfiguration.FeatureManagement
{
internal class FeatureConditions
{
[JsonPropertyName("client_filters")]
public List<ClientFilter> ClientFilters { get; set; } = new List<ClientFilter>();

[JsonPropertyName("requirement_type")]
public string RequirementType { get; set; }
}
}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
//
using System.Text.Json.Serialization;

namespace Microsoft.Extensions.Configuration.AzureAppConfiguration.FeatureManagement
{
internal class FeatureFlag
{
[JsonPropertyName("id")]
public string Id { get; set; }

[JsonPropertyName("enabled")]
public bool Enabled { get; set; }

[JsonPropertyName("conditions")]
public FeatureConditions Conditions { get; set; }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,13 @@ internal class FeatureManagementConstants
public const string SectionName = "FeatureManagement";
public const string EnabledFor = "EnabledFor";
public const string RequirementType = "RequirementType";

public const string EnabledJsonPropertyName = "enabled";
public const string IdJsonPropertyName = "id";
public const string ConditionsJsonPropertyName = "conditions";
public const string RequirementTypeJsonPropertyName = "requirement_type";
public const string ClientFiltersJsonPropertyName = "client_filters";
public const string NameJsonPropertyName = "name";
public const string ParametersJsonPropertyName = "parameters";
}
}
Loading