Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
a4ecfba
Eureka: Add unit test for ToString on JSON objects
bart-vmware Mar 4, 2026
7826e45
Eureka: Remove unused type
bart-vmware Mar 4, 2026
e89902d
Eureka: Improve existing serialization tests
bart-vmware Mar 4, 2026
47bc23a
Eureka: Use JSON source generator
bart-vmware Mar 4, 2026
2fdbda0
Eureka: Use source generator for ToString debug methods
bart-vmware Mar 4, 2026
76d0ac6
HttpClients: Use JSON source generator
bart-vmware Mar 4, 2026
981dc00
Replace JsonSerializer usage from certificate tests with simple strin…
bart-vmware Mar 4, 2026
ad5898f
SpringBootAdmin client: Use JSON source generator
bart-vmware Mar 5, 2026
e2e21c4
Cloud foundry middleware: gracefully handle failure to parse permissi…
bart-vmware Mar 5, 2026
34914d0
Switch to v3 of cloud controller API for permission checks
bart-vmware Mar 5, 2026
3f9bfd1
CloudFoundry actuator middleware: Use JSON source generator
bart-vmware Mar 5, 2026
1907b89
Actuator port middleware: Use JSON source generator
bart-vmware Mar 5, 2026
916c251
Loggers actuator fixes: Respect serializer options when reading reque…
bart-vmware Mar 6, 2026
2d1d38b
Add missing [JsonPropertyName] to JSON-serialized types because 1) th…
bart-vmware Mar 6, 2026
93d79bb
Revert "Switch to v3 of cloud controller API for permission checks"
bart-vmware Mar 6, 2026
70d1123
Fix broken log level reset in Spring Boot Admin
bart-vmware Mar 9, 2026
ad5690b
Revert "SpringBootAdmin client: Use JSON source generator"
bart-vmware Mar 9, 2026
1aea4f6
Add test for loggers reset from Apps Manager
bart-vmware Mar 10, 2026
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
3 changes: 3 additions & 0 deletions src/Common/src/Common/HealthChecks/HealthCheckResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public sealed class HealthCheckResult
/// <remarks>
/// Used by the health middleware to determine the HTTP Status code.
/// </remarks>
[JsonPropertyName("status")]
[JsonConverter(typeof(SnakeCaseAllCapsEnumMemberJsonConverter))]
public HealthStatus Status { get; set; } = HealthStatus.Unknown;

Expand All @@ -28,11 +29,13 @@ public sealed class HealthCheckResult
/// <remarks>
/// Currently only used on check failures.
/// </remarks>
[JsonPropertyName("description")]
public string? Description { get; set; }

/// <summary>
/// Gets details of the health check.
/// </summary>
[JsonPropertyName("details")]
[JsonIgnoreEmptyCollection]
public IDictionary<string, object> Details { get; } = new Dictionary<string, object>();
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text.Json;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
Expand Down Expand Up @@ -212,19 +211,25 @@ public async Task CertificateOptions_update_on_changed_path(string certificateNa

private static string BuildAppSettingsJson(string certificateName, string certificatePath, string keyPath)
{
string certificateBlock = $"""
"CertificateFilePath": {JsonSerializer.Serialize(certificatePath)},
"PrivateKeyFilePath": {JsonSerializer.Serialize(keyPath)}
""";

string namedCertificateSection = string.IsNullOrEmpty(certificateName)
? certificateBlock
: $"{JsonSerializer.Serialize(certificateName)}: {{ {certificateBlock} }}";
string escapedCertificatePath = certificatePath.Replace(@"\", @"\\", StringComparison.Ordinal);
string escapedKeyPath = keyPath.Replace(@"\", @"\\", StringComparison.Ordinal);

return $$"""
return string.IsNullOrEmpty(certificateName)
? $$"""
{
"Certificates": {
"CertificateFilePath": "{{escapedCertificatePath}}",
"PrivateKeyFilePath": "{{escapedKeyPath}}"
}
}
"""
: $$"""
{
"Certificates": {
{{namedCertificateSection}}
"{{certificateName}}": {
"CertificateFilePath": "{{escapedCertificatePath}}",
"PrivateKeyFilePath": "{{escapedKeyPath}}"
}
}
}
""";
Expand Down
2 changes: 1 addition & 1 deletion src/Discovery/src/Eureka/AppInfo/ApplicationInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ private List<InstanceInfo> GetInstancesSnapshot()
/// <inheritdoc />
public override string ToString()
{
return JsonSerializer.Serialize(this, DebugSerializerOptions.Instance);
return JsonSerializer.Serialize(this, DebugJsonSerializerContext.Default.ApplicationInfo);
}

internal void Add(InstanceInfo instance)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ internal ReadOnlyCollection<InstanceInfo> GetInstancesByVipAddress(string vipAdd
/// <inheritdoc />
public override string ToString()
{
return JsonSerializer.Serialize(this, DebugSerializerOptions.Instance);
return JsonSerializer.Serialize(this, DebugJsonSerializerContext.Default.ApplicationInfoCollection);
}

IEnumerator IEnumerable.GetEnumerator()
Expand Down
3 changes: 3 additions & 0 deletions src/Discovery/src/Eureka/AppInfo/DataCenterName.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@
#pragma warning disable SA1602 // Enumeration items should be documented
#endif

using System.Text.Json.Serialization;

namespace Steeltoe.Discovery.Eureka.AppInfo;

[JsonConverter(typeof(JsonStringEnumConverter<DataCenterName>))]
public enum DataCenterName
{
Netflix,
Expand Down
2 changes: 1 addition & 1 deletion src/Discovery/src/Eureka/AppInfo/InstanceInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ public override int GetHashCode()
/// <inheritdoc />
public override string ToString()
{
return JsonSerializer.Serialize(this, DebugSerializerOptions.Instance);
return JsonSerializer.Serialize(this, DebugJsonSerializerContext.Default.InstanceInfo);
}

internal static InstanceInfo FromConfiguration(EurekaInstanceOptions options, TimeProvider timeProvider)
Expand Down
2 changes: 1 addition & 1 deletion src/Discovery/src/Eureka/AppInfo/LeaseInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ private LeaseInfo()
/// <inheritdoc />
public override string ToString()
{
return JsonSerializer.Serialize(this, DebugSerializerOptions.Instance);
return JsonSerializer.Serialize(this, DebugJsonSerializerContext.Default.LeaseInfo);
}

internal static LeaseInfo? FromJson(JsonLeaseInfo? jsonLeaseInfo)
Expand Down
20 changes: 2 additions & 18 deletions src/Discovery/src/Eureka/EurekaClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
using System.Net.Http.Json;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
Expand Down Expand Up @@ -37,21 +36,6 @@ public sealed partial class EurekaClient
private static readonly Task<object?> TaskOfNull = Task.FromResult<object?>(null);
private static readonly TimeSpan GetAccessTokenTimeout = TimeSpan.FromSeconds(10);

private static readonly JsonSerializerOptions RequestSerializerOptions = new()
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};

private static readonly JsonSerializerOptions ResponseSerializerOptions = new()
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
Converters =
{
new JsonApplicationConverter(),
new JsonInstanceInfoConverter()
}
};

private readonly IHttpClientFactory _httpClientFactory;
private readonly IOptionsMonitor<EurekaClientOptions> _optionsMonitor;
private readonly EurekaServiceUriStateManager _eurekaServiceUriStateManager;
Expand Down Expand Up @@ -98,7 +82,7 @@ public async Task RegisterAsync(InstanceInfo instance, CancellationToken cancell
string requestBody = JsonSerializer.Serialize(new JsonInstanceInfoRoot
{
Instance = instance.ToJson()
}, RequestSerializerOptions);
}, EurekaJsonSerializerContext.Default.JsonInstanceInfoRoot);

string path = $"apps/{WebUtility.UrlEncode(instance.AppName)}";
await ExecuteRequestAsync(HttpMethod.Post, path, null, requestBody, cancellationToken);
Expand Down Expand Up @@ -224,7 +208,7 @@ private async Task<ApplicationInfoCollection> GetApplicationsAtPathAsync(string
{
return await ExecuteRequestAsync(HttpMethod.Get, path, null, null, async response =>
{
var root = await response.Content.ReadFromJsonAsync<JsonApplicationsRoot>(ResponseSerializerOptions, cancellationToken);
JsonApplicationsRoot? root = await response.Content.ReadFromJsonAsync(EurekaJsonSerializerContext.Default.JsonApplicationsRoot, cancellationToken);
return ApplicationInfoCollection.FromJson(root?.Applications, _timeProvider);
}, cancellationToken);
}
Expand Down
12 changes: 12 additions & 0 deletions src/Discovery/src/Eureka/Transport/DebugJsonSerializerContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information.

using System.Text.Json.Serialization;
using Steeltoe.Discovery.Eureka.AppInfo;

namespace Steeltoe.Discovery.Eureka.Transport;

[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
[JsonSerializable(typeof(ApplicationInfoCollection))]
internal sealed partial class DebugJsonSerializerContext : JsonSerializerContext;
20 changes: 0 additions & 20 deletions src/Discovery/src/Eureka/Transport/DebugSerializerOptions.cs

This file was deleted.

14 changes: 14 additions & 0 deletions src/Discovery/src/Eureka/Transport/EurekaJsonSerializerContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information.

using System.Text.Json.Serialization;

namespace Steeltoe.Discovery.Eureka.Transport;

[JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
[JsonSerializable(typeof(JsonApplicationsRoot))]
[JsonSerializable(typeof(List<JsonApplication?>))]
[JsonSerializable(typeof(JsonInstanceInfoRoot))]
[JsonSerializable(typeof(List<JsonInstanceInfo?>))]
internal sealed partial class EurekaJsonSerializerContext : JsonSerializerContext;
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ internal sealed class JsonApplicationConverter : JsonConverter<IList<JsonApplica
{
if (reader.TokenType == JsonTokenType.StartArray)
{
return JsonSerializer.Deserialize<List<JsonApplication?>>(ref reader, options)!;
return JsonSerializer.Deserialize(ref reader, EurekaJsonSerializerContext.Default.ListJsonApplication)!;
}

var application = JsonSerializer.Deserialize<JsonApplication>(ref reader, options);
JsonApplication? application = JsonSerializer.Deserialize(ref reader, EurekaJsonSerializerContext.Default.JsonApplication);
return application != null ? [application] : [];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ internal sealed class JsonInstanceInfoConverter : JsonConverter<IList<JsonInstan
{
if (reader.TokenType == JsonTokenType.StartArray)
{
return JsonSerializer.Deserialize<List<JsonInstanceInfo?>>(ref reader, options)!;
return JsonSerializer.Deserialize(ref reader, EurekaJsonSerializerContext.Default.ListJsonInstanceInfo)!;
}

var instanceInfo = JsonSerializer.Deserialize<JsonInstanceInfo>(ref reader, options);
JsonInstanceInfo? instanceInfo = JsonSerializer.Deserialize(ref reader, EurekaJsonSerializerContext.Default.JsonInstanceInfo);
return instanceInfo != null ? [instanceInfo] : [];
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the Apache 2.0 License.
// See the LICENSE file in the project root for more information.

using System.Text.Json.Serialization;

namespace Steeltoe.Discovery.HttpClients.LoadBalancers;

[JsonSourceGenerationOptions]
[JsonSerializable(typeof(ServiceInstancesResolver.JsonSerializableServiceInstance[]))]
internal sealed partial class ServiceInstancesJsonSerializerContext : JsonSerializerContext;
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,8 @@ public async Task<IList<IServiceInstance>> ResolveInstancesAsync(string serviceI
{
if (cacheValue is { Length: > 0 })
{
var serializableInstances = JsonSerializer.Deserialize<List<JsonSerializableServiceInstance>>(cacheValue);
JsonSerializableServiceInstance[]? serializableInstances =
JsonSerializer.Deserialize(cacheValue, ServiceInstancesJsonSerializerContext.Default.JsonSerializableServiceInstanceArray);

if (serializableInstances != null)
{
Expand All @@ -127,10 +128,10 @@ public async Task<IList<IServiceInstance>> ResolveInstancesAsync(string serviceI
return null;
}

private static byte[] ToCacheValue(IEnumerable<IServiceInstance> instances)
private static byte[] ToCacheValue(List<IServiceInstance> instances)
{
JsonSerializableServiceInstance[] serializableInstances = instances.Select(JsonSerializableServiceInstance.CopyFrom).ToArray();
return JsonSerializer.SerializeToUtf8Bytes(serializableInstances);
return JsonSerializer.SerializeToUtf8Bytes(serializableInstances, ServiceInstancesJsonSerializerContext.Default.JsonSerializableServiceInstanceArray);
}

[LoggerMessage(Level = LogLevel.Warning, Message = "No discovery clients are registered.")]
Expand All @@ -142,7 +143,7 @@ private static byte[] ToCacheValue(IEnumerable<IServiceInstance> instances)
[LoggerMessage(Level = LogLevel.Error, Message = "Failed to get instances from {DiscoveryClient}.")]
private partial void LogFailedToGetInstances(Exception exception, Type discoveryClient);

private sealed class JsonSerializableServiceInstance : IServiceInstance
internal sealed class JsonSerializableServiceInstance : IServiceInstance
{
// Trust that deserialized instances meet the IServiceInstance contract, so suppress nullability warnings.

Expand Down
Loading
Loading