Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
24bd01b
Replace SwashBuckle with Scalar, Replace JSON.NET from API Surface wi…
niemyjski Dec 6, 2025
284609c
Removes duplicate package references
niemyjski Jan 16, 2026
646fe83
Merge branch 'main' into feature/scalar-system-text-json
niemyjski Jan 16, 2026
70580ff
Removed json.net
niemyjski Jan 16, 2026
af322d5
Removes Delta operation filter.
niemyjski Jan 16, 2026
4d43aae
Updates Scalar.AspNetCore package
niemyjski Jan 16, 2026
b69261a
Improves docs and authentication setup
niemyjski Jan 16, 2026
90bbf20
Merge branch 'main' into feature/scalar-system-text-json
niemyjski Jan 17, 2026
6d19c4b
update deps
niemyjski Jan 17, 2026
ac1a6d9
Uses System.Text.Json for WebSocket serialization.
niemyjski Jan 17, 2026
9d1c919
Configures STJ for tests
niemyjski Jan 17, 2026
0520e74
Merge branch 'main' into feature/scalar-system-text-json
niemyjski Jan 19, 2026
41a2fcf
reverted changes
niemyjski Jan 19, 2026
4df5271
Fixed url
niemyjski Jan 19, 2026
57989c1
updated node
niemyjski Jan 19, 2026
c995b4f
Removes required keyword from auth models
niemyjski Jan 19, 2026
ca51ba6
Moves to System.Text.Json serializer
niemyjski Jan 19, 2026
6f27fe3
Configures JsonSerializer to ignore null values
niemyjski Jan 19, 2026
554573a
Merge branch 'main' into feature/scalar-system-text-json
niemyjski Jan 20, 2026
4640b5d
Adds support for inferred types during deserialization
niemyjski Jan 21, 2026
cfed374
Enhances DataDictionary deserialization
niemyjski Jan 21, 2026
f94c5e8
Adds JsonSerializerOptions to EventExtensions
niemyjski Jan 22, 2026
4bd1f7c
Enhances OpenAPI schema generation
niemyjski Jan 22, 2026
5e7c340
Merge branch 'main' into feature/scalar-system-text-json
niemyjski Jan 22, 2026
a36c494
Optimizes number deserialization in JSON converter
niemyjski Jan 22, 2026
3be699d
Updates Foundatio and Scalar dependencies
niemyjski Jan 22, 2026
06f16ae
Migrates to System.Text.Json
niemyjski Jan 22, 2026
b837f52
Removes ITextSerializer contract tests
niemyjski Jan 22, 2026
e9fd971
Ensures correct type inference in serializer
niemyjski Jan 22, 2026
4ebe289
Improves JSON deserialization accuracy.
niemyjski Jan 22, 2026
86c6882
Merge branch 'main' into feature/scalar-system-text-json
niemyjski Jan 22, 2026
78f0cd1
Updates Scalar.AspNetCore package
niemyjski Jan 22, 2026
f03171c
Adds authentication descriptions to OpenAPI
niemyjski Jan 23, 2026
dff0b27
Fixed Scalar Auth
niemyjski Jan 23, 2026
a340bc4
Merge branch 'main' into feature/scalar-system-text-json
niemyjski Jan 24, 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
4 changes: 2 additions & 2 deletions build/generate-client.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
curl -X POST https://generator3.swagger.io/api/generate \
-H 'content-type: application/json' \
-d '{
"specURL" : "https://collector.exceptionless.io/docs/v2/swagger.json",
"specURL" : "https://collector.exceptionless.io/docs/v2/openapi.json",
"lang" : "typescript-fetch",
"type" : "CLIENT",
"options" : {
Expand All @@ -11,4 +11,4 @@ curl -X POST https://generator3.swagger.io/api/generate \
}
},
"codegenVersion" : "V3"
}' --output exceptionless-ts.zip
}' --output exceptionless-ts.zip
5 changes: 2 additions & 3 deletions src/Exceptionless.Core/Configuration/AppOptions.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
using System.Diagnostics;
using System.Text.Json.Serialization;
using Exceptionless.Core.Configuration;
using Exceptionless.Core.Extensions;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

namespace Exceptionless.Core;

Expand All @@ -26,7 +25,7 @@ public class AppOptions
/// </summary>
public string? ExceptionlessServerUrl { get; internal set; }

[JsonConverter(typeof(StringEnumConverter))]
[JsonConverter(typeof(JsonStringEnumConverter))]
public AppMode AppMode { get; internal set; }
public string AppScope { get; internal set; } = null!;

Expand Down
7 changes: 3 additions & 4 deletions src/Exceptionless.Core/Configuration/EmailOptions.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
using Exceptionless.Core.Extensions;
using System.Text.Json.Serialization;
using Exceptionless.Core.Extensions;
using Exceptionless.Core.Utility;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

namespace Exceptionless.Core.Configuration;

Expand All @@ -26,7 +25,7 @@ public class EmailOptions

public int SmtpPort { get; internal set; }

[JsonConverter(typeof(StringEnumConverter))]
[JsonConverter(typeof(JsonStringEnumConverter))]
public SmtpEncryption SmtpEncryption { get; internal set; }

public string? SmtpUser { get; internal set; }
Expand Down
29 changes: 21 additions & 8 deletions src/Exceptionless.Core/Models/Stack.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
using Foundatio.Repositories.Models;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

namespace Exceptionless.Core.Models;
Expand Down Expand Up @@ -123,13 +123,26 @@ public static class KnownTypes
}
}

[JsonConverter(typeof(StringEnumConverter))]
[JsonConverter(typeof(JsonStringEnumConverter))]
[Newtonsoft.Json.JsonConverter(typeof(StringEnumConverter))]
public enum StackStatus
{
[EnumMember(Value = "open")] Open,
[EnumMember(Value = "fixed")] Fixed,
[EnumMember(Value = "regressed")] Regressed,
[EnumMember(Value = "snoozed")] Snoozed,
[EnumMember(Value = "ignored")] Ignored,
[EnumMember(Value = "discarded")] Discarded
[JsonStringEnumMemberName("open")]
[EnumMember(Value = "open")]
Open,
[JsonStringEnumMemberName("fixed")]
[EnumMember(Value = "fixed")]
Fixed,
[JsonStringEnumMemberName("regressed")]
[EnumMember(Value = "regressed")]
Regressed,
[JsonStringEnumMemberName("snoozed")]
[EnumMember(Value = "snoozed")]
Snoozed,
[JsonStringEnumMemberName("ignored")]
[EnumMember(Value = "ignored")]
Ignored,
[JsonStringEnumMemberName("discarded")]
[EnumMember(Value = "discarded")]
Discarded
}
1 change: 1 addition & 0 deletions src/Exceptionless.Web/Controllers/EventController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
using Exceptionless.Web.Extensions;
using Exceptionless.Web.Models;
using Exceptionless.Web.Utility;
using Exceptionless.Web.Utility.OpenApi;
using FluentValidation;
using Foundatio.Caching;
using Foundatio.Queues;
Expand Down
24 changes: 12 additions & 12 deletions src/Exceptionless.Web/Controllers/StackController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
using McSherry.SemanticVersioning;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json.Linq;
using System.Text.Json;

namespace Exceptionless.Web.Controllers;

Expand Down Expand Up @@ -131,14 +131,14 @@ public async Task<ActionResult> MarkFixedAsync(string ids, string? version = nul
[HttpPost("mark-fixed")]
[Consumes("application/json")]
[ApiExplorerSettings(IgnoreApi = true)]
public async Task<ActionResult> MarkFixedAsync(JObject data)
public async Task<ActionResult> MarkFixedAsync(JsonDocument data)
{
string? id = null;
if (data.TryGetValue("ErrorStack", out var value))
id = value.Value<string>();
if (data.RootElement.TryGetProperty("ErrorStack", out var errorStackProp))
id = errorStackProp.GetString();

if (data.TryGetValue("Stack", out value))
id = value.Value<string>();
if (data.RootElement.TryGetProperty("Stack", out var stackProp))
id = stackProp.GetString();

if (String.IsNullOrEmpty(id))
return NotFound();
Expand Down Expand Up @@ -215,22 +215,22 @@ public async Task<IActionResult> AddLinkAsync(string id, ValueFromBody<string?>
[HttpPost("add-link")]
[Consumes("application/json")]
[ApiExplorerSettings(IgnoreApi = true)]
public async Task<IActionResult> AddLinkAsync(JObject data)
public async Task<IActionResult> AddLinkAsync(JsonDocument data)
{
string? id = null;
if (data.TryGetValue("ErrorStack", out var value))
id = value.Value<string>();
if (data.RootElement.TryGetProperty("ErrorStack", out var errorStackProp))
id = errorStackProp.GetString();

if (data.TryGetValue("Stack", out value))
id = value.Value<string>();
if (data.RootElement.TryGetProperty("Stack", out var stackProp))
id = stackProp.GetString();

if (String.IsNullOrEmpty(id))
return NotFound();

if (id.StartsWith("http"))
id = id.Substring(id.LastIndexOf('/') + 1);

string? url = data.GetValue("Link")?.Value<string>();
string? url = data.RootElement.TryGetProperty("Link", out var linkProp) ? linkProp.GetString() : null;
return await AddLinkAsync(id, new ValueFromBody<string?>(url));
}

Expand Down
12 changes: 6 additions & 6 deletions src/Exceptionless.Web/Controllers/WebHookController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
using Foundatio.Repositories;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json.Linq;
using System.Text.Json;

namespace Exceptionless.App.Controllers.API;

Expand Down Expand Up @@ -101,10 +101,10 @@ public Task<ActionResult<WorkInProgressResult>> DeleteAsync(string ids)
[HttpPost("~/api/v1/projecthook/subscribe")]
[Consumes("application/json")]
[ApiExplorerSettings(IgnoreApi = true)]
public async Task<ActionResult<WebHook>> SubscribeAsync(JObject data, int apiVersion = 1)
public async Task<ActionResult<WebHook>> SubscribeAsync(JsonDocument data, int apiVersion = 1)
{
string? eventType = data.GetValue("event")?.Value<string>();
string? url = data.GetValue("target_url")?.Value<string>();
string? eventType = data.RootElement.TryGetProperty("event", out var eventProp) ? eventProp.GetString() : null;
string? url = data.RootElement.TryGetProperty("target_url", out var urlProp) ? urlProp.GetString() : null;
if (String.IsNullOrEmpty(eventType) || String.IsNullOrEmpty(url))
return BadRequest();

Expand Down Expand Up @@ -139,9 +139,9 @@ public async Task<ActionResult<WebHook>> SubscribeAsync(JObject data, int apiVer
[HttpPost("~/api/v1/projecthook/unsubscribe")]
[Consumes("application/json")]
[ApiExplorerSettings(IgnoreApi = true)]
public async Task<IActionResult> UnsubscribeAsync(JObject data)
public async Task<IActionResult> UnsubscribeAsync(JsonDocument data)
{
string? targetUrl = data.GetValue("target_url")?.Value<string>();
string? targetUrl = data.RootElement.TryGetProperty("target_url", out var urlProp) ? urlProp.GetString() : null;

// don't let this anon method delete non-zapier hooks
if (targetUrl is null || !targetUrl.StartsWith("https://hooks.zapier.com"))
Expand Down
7 changes: 7 additions & 0 deletions src/Exceptionless.Web/Exceptionless.Web.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@
<PackageReference Include="NEST.JsonNetSerializer" Version="7.17.5" />
<PackageReference Include="OAuth2" Version="0.10.3" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="10.0.0" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.0" />
<PackageReference Include="MiniValidation" Version="0.9.2" />
<PackageReference Include="NEST.JsonNetSerializer" Version="7.17.5" />
<PackageReference Include="OAuth2" Version="0.10.3" />
<PackageReference Include="Scalar.AspNetCore" Version="2.11.1" />
<PackageReference Include="Serilog.AspNetCore" Version="10.0.0" />
<PackageReference Include="Serilog.Enrichers.Span" Version="3.1.0" />
<PackageReference Include="Serilog.Enrichers.Environment" Version="3.0.1" />
Expand All @@ -35,6 +41,7 @@
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.14.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.Process" Version="1.14.0-beta.2" />
<PackageReference Include="Swashbuckle.AspNetCore.Newtonsoft" Version="10.1.0" />
<PackageReference Include="OpenTelemetry.Instrumentation.Process" Version="1.13.0-beta.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Exceptionless.Insulation\Exceptionless.Insulation.csproj" />
Expand Down
14 changes: 9 additions & 5 deletions src/Exceptionless.Web/Hubs/WebSocketConnectionManager.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
using System.Collections.Concurrent;
using System.Net.WebSockets;
using System.Text;
using System.Text.Json;
using Exceptionless.Core;
using Newtonsoft.Json;
using Exceptionless.Web.Utility;

namespace Exceptionless.Web.Hubs;

Expand All @@ -11,12 +12,15 @@ public class WebSocketConnectionManager : IDisposable
private static readonly ArraySegment<byte> _keepAliveMessage = new(Encoding.ASCII.GetBytes("{}"), 0, 2);
private readonly ConcurrentDictionary<string, WebSocket> _connections = new();
private readonly Timer? _timer;
private readonly JsonSerializerSettings _serializerSettings;
private readonly JsonSerializerOptions _serializerOptions;
private readonly ILogger _logger;

public WebSocketConnectionManager(AppOptions options, JsonSerializerSettings serializerSettings, ILoggerFactory loggerFactory)
public WebSocketConnectionManager(AppOptions options, ILoggerFactory loggerFactory)
{
_serializerSettings = serializerSettings;
_serializerOptions = new JsonSerializerOptions
{
PropertyNamingPolicy = LowerCaseUnderscoreNamingPolicy.Instance
};
_logger = loggerFactory.CreateLogger<WebSocketConnectionManager>();
if (!options.EnableWebSockets)
return;
Expand Down Expand Up @@ -119,7 +123,7 @@ private Task SendMessageAsync(WebSocket socket, object message)
if (!CanSendWebSocketMessage(socket))
return Task.CompletedTask;

string serializedMessage = JsonConvert.SerializeObject(message, _serializerSettings);
string serializedMessage = JsonSerializer.Serialize(message, _serializerOptions);
Task.Factory.StartNew(async () =>
{
if (!CanSendWebSocketMessage(socket))
Expand Down
8 changes: 5 additions & 3 deletions src/Exceptionless.Web/Models/Auth/ExternalAuthInfo.cs
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
using System.ComponentModel.DataAnnotations;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System.Text.Json.Serialization;

namespace Exceptionless.Web.Models;

// NOTE: This will bypass our LowerCaseUnderscorePropertyNamesContractResolver and provide the correct casing.
[JsonObject(NamingStrategyType = typeof(CamelCaseNamingStrategy))]
public record ExternalAuthInfo
{
[Required]
[JsonPropertyName("clientId")]
public required string ClientId { get; init; }

[Required]
[JsonPropertyName("code")]
public required string Code { get; init; }

[Required]
[JsonPropertyName("redirectUri")]
public required string RedirectUri { get; init; }

[JsonPropertyName("inviteToken")]
public string? InviteToken { get; init; }
}
Loading
Loading