Skip to content

refactor: migrate Exceptionless.Web to Minimal APIs with Foundatio.Mediator#2257

Open
niemyjski wants to merge 53 commits into
mainfrom
niemyjski/minimal-api-mediator-openapi-migration
Open

refactor: migrate Exceptionless.Web to Minimal APIs with Foundatio.Mediator#2257
niemyjski wants to merge 53 commits into
mainfrom
niemyjski/minimal-api-mediator-openapi-migration

Conversation

@niemyjski
Copy link
Copy Markdown
Member

Summary

Migrate all Exceptionless.Web controllers to Minimal API endpoints using Foundatio.Mediator for command/query dispatch.

What Changed

  • Architecture: Controllers → thin endpoint functions that delegate to Mediator handlers
  • Entry Point: Consolidated Startup.cs into single minimal hosting Program.cs
  • Job Runner: Modernized to WebApplication.CreateBuilder() minimal hosting
  • OpenAPI: Preserved all documentation, tags, parameters, response codes via EndpointDocumentationOperationTransformer
  • Testing: Route manifest + OpenAPI snapshot tests ensure no regressions

Key Invariants Preserved

Invariant Status
All 184 routes ✅ Verified via endpoint manifest
All 137 documented OpenAPI operations
128/128 summaries
355 parameter descriptions
358 response descriptions
All tags match old controller-derived names
Security schemes
Route constraints (token, tokens, objectid)
Auth policies on every endpoint
ProblemDetails shape (instance, reference-id, errors, lower_underscore keys)
Delta<T> patch behavior
X-Exceptionless-Client header priority
ThrottlingMiddleware / OverageMiddleware ✅ Unchanged
Health checks (/health, /ready)

Structure

src/Exceptionless.Web/Api/
  Endpoints/     — 13 endpoint files (thin HTTP adapters)
  Messages/      — Command/query records
  Handlers/      — Use-case logic (ported from controller actions)
  Filters/       — AutoValidationEndpointFilter, ConfigurationResponseEndpointFilter
  Results/       — ApiResults, OkWithResourceLinks, CollectionResult
  Infrastructure/ — Pagination, TimeRangeParser, CurrentUserAccessor
  OpenApi/       — Build-time generation config

Migration Order (per OpenSpec)

  1. Contract/OpenAPI baseline tests → 2. Infrastructure → 3. Mediator registration → 4. Status/Utility → 5–17. Feature slices → 18. Remove controllers → 19. Final audit

Breaking Changes

None. All public routes, auth behavior, and response shapes are preserved.

Known Acceptable Differences

  • StringStringValuesKeyValuePair schema removed (MVC model binding artifact) → ProblemDetails schema added (from .ProducesProblem())
  • Versioned subscribe route /api/v{apiVersion:int}/webhooks/subscribe lacks =2 default (Minimal API limitation, canonical /api/v2/webhooks/subscribe route exists)

niemyjski and others added 18 commits May 26, 2026 17:15
Planning artifacts for migrating Exceptionless.Web controllers to
Minimal APIs with Foundatio.Mediator dispatch, preserving all existing
API behavior.

Change deliverables:
- proposal.md: justification, classification, rollback plan
- design.md: architecture, endpoint/mediator/handler patterns
- tasks.md: 19 ordered migration tasks with verification steps
- acceptance.md: SHALL/SHALL NOT acceptance criteria
- risks.md: 9 risks with mitigation strategies

New specs (testable SHALL statements):
- api-architecture: endpoint registration, mediator dispatch, DI
- api-contract: route/response/header preservation
- api-validation: DataAnnotation + MiniValidation
- api-problem-details: error response shape
- api-middleware: throttling, overage, filters, pipeline ordering
- api-openapi: runtime/build-time generation, snapshot tests
- api-patching: Delta<T> preservation, no JSON Patch

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Merge all service registrations and middleware pipeline into single Program.cs
- Use WebApplication.CreateBuilder() minimal hosting pattern
- Add Foundatio.Mediator 1.2.1 package reference
- Add Microsoft.Extensions.ApiDescription.Server for build-time OpenAPI
- Add stub MapApiEndpoints() extension for future endpoint registrations
- Update AppWebHostFactory to use WebApplicationFactory<Program>
- Remove Startup.cs

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- ApiEndpointGroups: shared route group builder with auth policy
- ApiResults: OkWithLinks, OkWithResourceLinks, Permission, WorkInProgress helpers
- Pagination: limit/page/skip helpers extracted from base controller
- TimeRangeParser: time range parsing extracted from base controller
- CurrentUserAccessor: HttpContext user helpers
- ConfigurationResponseEndpointFilter: config version header filter
- ApiResponseHeadersEndpointFilter: common response headers
- ApiValidation: MiniValidation wrapper for endpoint validation

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Create Messages/StatusMessages.cs with command/query records
- Create Handlers/StatusHandler.cs and UtilityHandler.cs with mediator handlers
- Create Endpoints/StatusEndpoints.cs and UtilityEndpoints.cs
- Remove StatusController.cs and UtilityController.cs
- Wire up MapApiEndpoints() in ApiEndpoints.cs

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- TokenEndpoints: full CRUD with org/project scoped routes
- WebHookEndpoints: CRUD plus Zapier subscribe/unsubscribe/test
- StripeEndpoints: webhook receiver with signature validation
- All use Foundatio.Mediator handler pattern
- Remove TokenController, WebHookController, StripeController

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ts with Foundatio.Mediator

Replace MVC controllers with the same Minimal API + Mediator pattern
used by Token, WebHook, and Status endpoints. Each controller is split
into Messages (records), Handler (business logic), and Endpoints
(HTTP routing via IMediator).

Preserves all routes, route constraints (:objectid, :token, :minlength),
auth policies (User, GlobalAdmin), named routes (GetSavedViewById,
GetUserById), and behavior including predefined saved view management,
email verification, admin role management, and rate-limited email updates.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- ProjectEndpoints: full CRUD, config, notifications, integrations, Slack
- OrganizationEndpoints: full CRUD, invoices, plans, suspend
- Preserve all routes, auth policies, route names
- Remove ProjectController.cs and OrganizationController.cs

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- AuthEndpoints: login, signup, OAuth, forgot-password, change-password
- Preserve AllowAnonymous on public auth routes
- Port complete OAuth flow (Google, Facebook, GitHub, Microsoft)
- Remove AuthController.cs

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace MVC controllers with Foundatio.Mediator-based Minimal API
endpoints following the established pattern (Messages/Handlers/Endpoints).
All routes, authorization policies, and route names are preserved.

- AdminController → AdminMessages + AdminHandler + AdminEndpoints
- StackController → StackMessages + StackHandler + StackEndpoints
- EventController → EventMessages + EventHandler + EventEndpoints
- Update ApiEndpoints.cs to register new endpoint groups
- Fix ControllerManifestTests assembly reference (no controllers remain)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove AddControllers() and MapControllers() from Program.cs
- Remove AddAutoValidation() (MVC-specific filter)
- Remove ExceptionlessApiController, ReadOnlyRepositoryApiController, RepositoryApiController base classes
- Keep shared types (PermissionResult, TimeInfo, WorkInProgressResult, ModelActionResults)
- Update ControllerManifestTests to verify no MVC controllers remain
- Full solution builds with 0 errors

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- EndpointManifestTests: verifies all endpoint classes are registered
- OpenApiSnapshotTests: lightweight test app for OpenAPI document verification
- MinimalApiTestApp: shared test host without Elasticsearch dependency
- SnapshotTestHelper: shared snapshot comparison utility
- Remove old OpenApiControllerTests (replaced by snapshot approach)
- Generate initial endpoint-manifest.json and openapi.json baselines

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ebhook subscribe route, user-agent

- Restore :token/:tokens route constraints on token endpoints
- Add canonical api/v2/webhooks/subscribe route (was only versioned)
- Create AutoValidationEndpointFilter for Minimal API auto-validation
- Register auto-validation filter on all endpoint groups
- Remove dead ApiEndpointGroups.cs
- Fix UserAgent header regression: prefer X-Exceptionless-Client over User-Agent
- Fix Stripe trailing slash: map POST directly without empty-string sub-route
- Delete obsolete controller-manifest.json test fixture

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Routes now match pre-migration manifest (184 endpoints, all constraints preserved).
Only remaining diff: versioned subscribe route template lacks =2 default
(Minimal API limitation; covered by canonical api/v2/webhooks/subscribe route).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace Host.CreateDefaultBuilder()/ConfigureWebHostDefaults() with
WebApplication.CreateBuilder() for consistency with the web project.
Preserves all behavior: health checks, Serilog, APM, job registration.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Port all XML doc summaries, parameter descriptions, and response
descriptions from the old MVC controllers to Minimal API endpoints
using .WithSummary() and .WithMetadata(EndpointDocumentation) with a
custom IOpenApiOperationTransformer.

Results vs old spec (128/348/244 target):
- Summaries: 128/128 (100%)
- Parameter descriptions: 298/348 (86% - gap is from params not in
  lambda signatures like headers and manual query params)
- Response descriptions: 266 total responses documented (exceeds 244)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Extend EndpointDocumentationOperationTransformer to support injecting
additional parameters (e.g. User-Agent header, query string arrays).

Add Produces<T>() and ProducesProblem() declarations across all endpoint
files to document response types and error codes. This brings coverage to:
- Summaries: 128 (unchanged)
- Parameters: 409 (was 287, added 122)
- Response codes: 353 (was 231, added 122)
- Schemas: 49 (was 43, added 6)

Update snapshot test assertion from 200 to 202 for user-description
endpoint to match its actual Accepted semantics.
Change endpoint group tags from plural to singular to match the old MVC
controller-derived tags (Event, Organization, Project, Stack, User, etc.).
Add explicit WithTags to Token, WebHook groups and all v1 endpoints that
previously inherited tags from their controller class.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@niemyjski niemyjski requested a review from Copilot May 27, 2026 03:46
@niemyjski niemyjski self-assigned this May 27, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot wasn't able to review this pull request because it exceeds the maximum number of lines (20,000). Try reducing the number of changed lines and requesting a review from Copilot again.

Comment thread src/Exceptionless.Job/Program.cs
Comment thread src/Exceptionless.Web/Api/Endpoints/StripeEndpoints.cs Fixed
Comment thread src/Exceptionless.Web/Api/Handlers/EventHandler.cs Fixed
Comment thread src/Exceptionless.Web/Api/Handlers/EventHandler.cs Fixed
Comment thread src/Exceptionless.Web/Api/Handlers/StackHandler.cs Fixed
Comment thread src/Exceptionless.Web/Program.cs Fixed
Comment thread src/Exceptionless.Web/Api/Filters/AutoValidationEndpointFilter.cs Fixed
Comment thread src/Exceptionless.Web/Api/Handlers/EventHandler.cs Fixed
Comment thread src/Exceptionless.Web/Api/Handlers/StackHandler.cs Fixed
Comment thread src/Exceptionless.Web/Api/Handlers/WebHookHandler.cs Fixed
- Disable ValidateOnBuild in WebApplication.CreateBuilder since the
  service graph uses lambda factories (queues, caching, Elasticsearch)
  that resolve dependencies at runtime via IServiceProvider. The old
  Generic Host path did not enable this validation.
- Add using/dispose to StreamReader in StripeEndpoints
- Add using/dispose to MemoryStream in EventHandler
- Add using/dispose to ScopedCacheClient in EventHandler and StackHandler
- Refactor AutoValidationEndpointFilter to use Where() filtering
- Refactor DeleteEvents/DeleteStacks to use LINQ Where() instead of
  mutating a list inside a foreach loop

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread src/Exceptionless.Web/Program.cs Fixed
Comment thread src/Exceptionless.Web/Api/Handlers/AuthHandler.cs Fixed
Comment thread src/Exceptionless.Web/Api/Filters/AutoValidationEndpointFilter.cs Fixed
Comment thread src/Exceptionless.Web/Api/Handlers/UserHandler.cs Fixed
Comment thread src/Exceptionless.Web/Program.cs Fixed
Comment thread src/Exceptionless.Web/Api/Handlers/EventHandler.cs Fixed
Comment thread src/Exceptionless.Web/Api/Handlers/AuthHandler.cs Fixed
Comment thread src/Exceptionless.Web/Api/Handlers/AdminHandler.cs
The ValidateOnBuild=false in Program.cs via builder.Host.UseDefaultServiceProvider()
does not take effect in the minimal hosting model when used with WebApplicationFactory.
The ConfigureHostBuilder stores but may not replay service provider options.

Fix: Add builder.UseDefaultServiceProvider() in AppWebHostFactory.ConfigureWebHost
where the IWebHostBuilder properly replaces the service provider factory.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@niemyjski niemyjski force-pushed the niemyjski/minimal-api-mediator-openapi-migration branch from ecc217a to 30aed31 Compare May 27, 2026 04:26
Comment thread src/Exceptionless.Web/Program.cs Fixed
Comment thread src/Exceptionless.Web/Program.cs Fixed
@niemyjski niemyjski requested a review from Copilot May 27, 2026 12:42
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot wasn't able to review any files in this pull request.

@niemyjski niemyjski requested a review from Copilot May 27, 2026 13:34
Comment thread src/Exceptionless.Web/Api/Handlers/OrganizationHandler.cs Fixed
niemyjski and others added 3 commits May 28, 2026 07:38
- Fix ModelActionResults with failures returning 202 instead of 400
  (preserving original controller bulk-delete behavior)
- Add not_implemented identifier handling to ApiResultMapper
  (matching ResultExtensions behavior for Stack promote → 501)
- Keep project_id validation in TokenEndpoints at 400
  (matching original MVC [ApiController] model binding behavior)
- Add defensive project_id check in TokenHandler.CanAddAsync

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Return ModelActionResults directly when all items are forbidden,
matching original controller behavior that included per-ID failure
information instead of a generic error message.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…atterns

Add comprehensive documentation for the new architecture:
- Endpoint + Handler pattern with Result<T>
- Result→HTTP status code mapping table
- Key rules and common pitfalls
- Updated authorization for endpoint groups
- Updated OpenAPI baseline workflow
- Mark old controller pattern as deprecated

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread src/Exceptionless.Web/Api/Handlers/AuthHandler.cs Fixed
Comment thread src/Exceptionless.Web/Api/Results/ApiResultMapper.cs Fixed
Comment thread src/Exceptionless.Web/Api/Results/ResultExtensions.cs Fixed
@niemyjski niemyjski requested a review from Copilot May 28, 2026 13:29
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot wasn't able to review this pull request because it exceeds the maximum number of lines (20,000). Try reducing the number of changed lines and requesting a review from Copilot again.

- Remove static _isFirstUserChecked guard from AuthHandler (writes to
  static field from instance method; user signups are rare enough that
  the extra CountAsync() is negligible)
- Use ternary expressions in error dict building (ApiResultMapper,
  ResultExtensions)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread src/Exceptionless.Web/Api/Handlers/AuthHandler.cs
Comment thread src/Exceptionless.Web/Api/Handlers/AuthHandler.cs
Comment thread src/Exceptionless.Web/Api/Handlers/OrganizationHandler.cs Fixed
Comment thread src/Exceptionless.Web/Api/Handlers/UtilityHandler.cs Fixed
@niemyjski niemyjski force-pushed the niemyjski/minimal-api-mediator-openapi-migration branch from 6d61b7e to 4ae63c3 Compare May 28, 2026 14:15
- Add missing ResultStatus.Accepted case to ApiResultMapper (was falling
  through to 500 for Accepted results)
- Cache PropertyInfo lookups in GetValue() using ConcurrentDictionary
  (eliminates per-request reflection allocation)
- Stream event body directly from Request.Body through mediator to
  EventPostService — eliminates byte[] buffering (up to 200KB per POST)
- Remove redundant Body property from SubmitEventByPost message; handler
  uses httpContext.Request.Body directly (matches original controller)
- Early-exit in endpoint when ContentLength <= 0 (avoids mediator overhead)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@niemyjski niemyjski force-pushed the niemyjski/minimal-api-mediator-openapi-migration branch from 4ae63c3 to 43cceec Compare May 28, 2026 22:01
Port the following from main:
- UsageService.IncrementDeletedAsync in EventHandler.DeleteEvents
- currentUsage.Deleted and currentHourUsage.Deleted in Org/Project handlers
- NotificationService replaces ICacheClient/IMessagePublisher in StatusHandler
- Add 'publish' parameter to PostSystemNotification
- Resolve merge conflicts (deleted controller files)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread src/Exceptionless.Web/Api/Handlers/EventHandler.cs Fixed
niemyjski and others added 3 commits May 30, 2026 08:06
The DELETE /api/v2/notifications/system endpoint was missing the
'publish' query parameter (default true) that the original controller
passed to NotificationService.ClearSystemNotificationAsync(). Without
this, the delete endpoint would always publish the notification
regardless of the caller's intent.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…d delete responses

- EventHandler/StackHandler: Replace Result.Forbidden() with PlanLimitResult<T>() helper
  for suspended-organization read paths. The mediator migration incorrectly mapped these
  to 403 instead of the original 426 (Upgrade Required).
- EventHandler/StackHandler: Restore ModelActionResults for mixed-delete scenarios instead
  of generic Result.BadRequest() strings.
- Add regression tests: GetByOrganizationAsync_SuspendedOrganization_ReturnsUpgradeRequired
  and DeleteAsync_WithMixedAccess_ReturnsModelActionResults for both controllers.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace custom Delta<T> partial-update mechanism with standard JSON Patch
(RFC 6902) using Microsoft.AspNetCore.JsonPatch.SystemTextJson v10.0.8.

Infrastructure:
- Add JsonPatchValidation.cs with ValidateOperations, ApplyPatch,
  AffectsPath, IsEmpty, and FromPartialObject helpers
- Add JsonPatchDocumentSchemaTransformer for OpenAPI array schema
- Remove Delta.cs, DeltaJsonConverter.cs, DeltaExtensions.cs,
  DeltaSchemaTransformer.cs

Endpoints/Messages/Handlers:
- All PATCH endpoints now accept JsonPatchDocument<T> parameters
- Messages carry JsonPatchDocument<T> instead of Delta<T>
- Handlers validate operations (immutable paths, max ops) then apply
- Legacy v1 PATCH endpoint accepts JsonElement and converts via
  FromPartialObject for backward compatibility

Frontend:
- Svelte: toJsonPatch() utility converts partial objects to RFC 6902 ops
- Angular: customPATCH with toJsonPatch() and json-patch+json content type

Tests:
- All PATCH tests converted to RFC 6902 JSON Patch array format
- JsonPatchHelper with PatchContent() and JsonPatchContent() extension
- Content type set to application/json-patch+json (required for binding)
- OpenAPI snapshot updated to reflect JsonPatchDocument schemas
- Fixed legacy v1 handler to treat URL id as reference_id (not event ID)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread src/Exceptionless.Web/Api/Infrastructure/JsonPatchValidation.cs Fixed
Comment thread src/Exceptionless.Web/Api/Infrastructure/JsonPatchValidation.cs Fixed
Comment on lines +208 to +211
catch (Exception ex)
{
_logger.LogCritical(ex, "Unexpected error getting invoice ({InvoiceId}): {Message}", invoiceId, ex.Message);
}
Comment thread src/Exceptionless.Web/Api/Handlers/OrganizationHandler.cs Fixed
niemyjski and others added 2 commits May 31, 2026 14:17
…nd add validation unit tests

- Svelte: export jsonPatchRequestOptions from json-patch.ts with correct
  Content-Type header; pass to all patchJSON() calls
- Angular: already fixed in prior commit (customPATCH headers)
- Harden ValidateOperations: reject root/empty/malformed/nested paths
- Add 24 focused unit tests for JsonPatchValidation covering:
  - Valid replace/test operations
  - Disallowed operations (add/remove/move/copy)
  - Root/empty/whitespace path rejection
  - Nested/double-slash path rejection
  - Immutable path enforcement (case-insensitive)
  - Max operations limit
  - ApplyPatch, AffectsPath, IsEmpty, FromPartialObject

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…TCH calls

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread src/Exceptionless.Web/Api/Infrastructure/JsonPatchValidation.cs Fixed
Comment thread src/Exceptionless.Web/Api/Infrastructure/JsonPatchValidation.cs Fixed
niemyjski and others added 5 commits May 31, 2026 14:27
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… compatibility

The JsonPatchValidationTests.ApplyPatch_ValidReplace_MutatesTarget test
failed in CI Release builds because JsonSerializerOptions without a
TypeInfoResolver cannot resolve metadata for TestDto.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Reject JSON Patch operation paths that do not start with '/' before normalization so RFC-invalid paths cannot silently mutate resources. Restore raw JSON/text request-body OpenAPI metadata for event ingestion endpoints.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment on lines +113 to +126
foreach (var op in patch.Operations)
{
var pathSegment = NormalizePath(op.path).TrimStart('/');
foreach (var prop in properties)
{
var jsonName = policy?.ConvertName(prop.Name) ?? prop.Name;
if (pathSegment.Equals(jsonName, StringComparison.OrdinalIgnoreCase)
|| pathSegment.Equals(prop.Name, StringComparison.OrdinalIgnoreCase))
{
affected.Add(prop.Name);
break;
}
}
}
Comment on lines +166 to +175
foreach (var prop in body.EnumerateObject())
{
var op = new JsonObject
{
["op"] = "replace",
["path"] = $"/{prop.Name}",
["value"] = JsonNode.Parse(prop.Value.GetRawText())
};
ops.Add(op);
}
Recreate test IFileStorage from the final AppOptions so parallel WebApplicationFactory instances use their own scoped folder storage. This prevents ResetDataAsync in one factory from deleting queued event payloads for another factory.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment on lines +666 to +669
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to increment deleted usage metrics for org {OrganizationId} project {ProjectId}: {Message}", projectGroup.Key.OrganizationId, projectGroup.Key.ProjectId, ex.Message);
}
@github-actions
Copy link
Copy Markdown

Code Coverage

Package Line Rate Branch Rate Complexity Health
Exceptionless.Insulation 24% 23% 203
Exceptionless.Web 78% 61% 5201
Exceptionless.AppHost 18% 9% 82
Exceptionless.Core 69% 63% 7905
Summary 71% (16874 / 23690) 61% (7722 / 12616) 13391

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants