Skip to content

Conversation

@niemyjski
Copy link
Member

Summary

Migrates from AutoMapper (runtime reflection-based mapping) to Mapperly (compile-time source generation) for improved performance and type safety.

Changes

Package Updates

  • Added: \Riok.Mapperly 4.2.1\ to \Exceptionless.Web\
  • Removed: \AutoMapper 14.0.0\ from \Exceptionless.Core\

Architecture Changes

  • Controllers now use abstract mapping methods instead of generic \MapAsync\
  • Base controllers require derived classes to implement:
    • \MapToModel(TNewModel)\
    • \MapToViewModel(TModel)\
    • \MapToViewModels(IEnumerable)\

Mapping Structure

Created dedicated mapper files per type in \src/Exceptionless.Web/Mapping/:

Mapper Source → Destination
\OrganizationMapper\ \NewOrganization\ → \Organization, \Organization\ → \ViewOrganization\
\ProjectMapper\ \NewProject\ → \Project, \Project\ → \ViewProject\
\TokenMapper\ \NewToken\ → \Token, \Token\ → \ViewToken\
\UserMapper\ \User\ → \ViewUser\
\WebHookMapper\ \NewWebHook\ → \WebHook\
\InvoiceMapper\ \Stripe.Invoice\ → \InvoiceGridModel\

\ApiMapper\ acts as a facade delegating to individual mappers.

Testing

  • Added 29 comprehensive unit tests for all mappers in \ ests/Exceptionless.Tests/Mapping/\
  • Tests follow backend-testing skill patterns (AAA, descriptive naming)

Benefits

  • ✅ Compile-time type safety for mappings
  • ✅ Better performance (no runtime reflection)
  • ✅ Cleaner separation of concerns with per-type mappers
  • ✅ Source-generated code is inspectable and debuggable

Breaking Changes

Controllers that inherit from \RepositoryApiController\ or \ReadOnlyRepositoryApiController\ must now implement the abstract mapping methods.

- Replace AutoMapper (runtime reflection) with Mapperly (compile-time source generation)
- Add Riok.Mapperly 4.2.1 to Exceptionless.Web
- Remove AutoMapper 14.0.0 from Exceptionless.Core

Breaking changes:
- Controllers now use abstract mapping methods instead of generic MapAsync<T>
- Base controllers require derived classes to implement MapToModel, MapToViewModel, MapToViewModels

Mapping structure:
- Created dedicated mapper files per type in src/Exceptionless.Web/Mapping/
  - OrganizationMapper: NewOrganization -> Organization, Organization -> ViewOrganization
  - ProjectMapper: NewProject -> Project, Project -> ViewProject
  - TokenMapper: NewToken -> Token, Token -> ViewToken
  - UserMapper: User -> ViewUser
  - WebHookMapper: NewWebHook -> WebHook
  - InvoiceMapper: Stripe.Invoice -> InvoiceGridModel
- ApiMapper facade delegates to individual mappers

Testing:
- Added comprehensive unit tests for all mappers (29 tests)
- Tests follow backend-testing skill patterns

Benefits:
- Compile-time type safety for mappings
- Better performance (no runtime reflection)
- Cleaner separation of concerns with per-type mappers
Copy link
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.

Pull request overview

This PR replaces the previous AutoMapper-based runtime mapping with Mapperly-based (and some manual) mappings, refactors controller base classes to use explicit mapping hooks, and wires up an ApiMapper facade plus dedicated per-type mappers, along with unit tests to validate the new mappings.

Changes:

  • Removed AutoMapper dependencies and registration from Exceptionless.Core and Exceptionless.Web, adding Riok.Mapperly and an ApiMapper facade with per-type mappers (OrganizationMapper, ProjectMapper, TokenMapper, UserMapper, WebHookMapper, InvoiceMapper).
  • Refactored ReadOnlyRepositoryApiController and RepositoryApiController to use abstract MapToModel / MapToViewModel / MapToViewModels methods implemented by concrete controllers instead of generic MapAsync/MapCollectionAsync.
  • Updated controllers (OrganizationController, ProjectController, TokenController, UserController, WebHookController, EventController, StackController) to depend on ApiMapper and implement the new mapping hooks, and added focused mapper unit tests under tests/Exceptionless.Tests/Mapping.

Reviewed changes

Copilot reviewed 27 out of 27 changed files in this pull request and generated no comments.

Show a summary per file
File Description
src/Exceptionless.Core/Exceptionless.Core.csproj Removes the AutoMapper package reference, completing the core-layer dependency removal.
src/Exceptionless.Core/Models/CoreMappings.cs Deletes the empty CoreMappings AutoMapper profile, as mappings are no longer configured in core.
src/Exceptionless.Core/Bootstrapper.cs Removes AutoMapper profile registration and IMapper singleton wiring, ensuring DI no longer constructs an AutoMapper instance.
src/Exceptionless.Web/Exceptionless.Web.csproj Adds a reference to Riok.Mapperly so Mapperly source generation can run for the new mapping classes.
src/Exceptionless.Web/Bootstrapper.cs Drops the AutoMapper ApiMappings profile and IMapper registration, and registers ApiMapper as a singleton for controller injection.
src/Exceptionless.Web/Mapping/ApiMapper.cs Introduces an ApiMapper facade that composes OrganizationMapper, ProjectMapper, TokenMapper, UserMapper, WebHookMapper, and InvoiceMapper and exposes strongly-typed mapping methods used by controllers.
src/Exceptionless.Web/Mapping/OrganizationMapper.cs Adds a Mapperly-based mapper from NewOrganizationOrganization and OrganizationViewOrganization, with explicit post-processing of IsOverMonthlyLimit using Organization.IsOverMonthlyLimit(TimeProvider), plus list mapping helpers.
src/Exceptionless.Web/Mapping/ProjectMapper.cs Adds a Mapperly-based mapper from NewProjectProject and a two-stage ProjectViewProject mapping (core Mapperly map plus explicit HasSlackIntegration computation), plus list mapping helpers.
src/Exceptionless.Web/Mapping/TokenMapper.cs Adds a Mapperly-based mapper from NewTokenToken that ignores Token.Type (leaving its default) and maps TokenViewToken, plus list mapping helpers; preserves prior AutoMapper semantics around token type.
src/Exceptionless.Web/Mapping/UserMapper.cs Adds a Mapperly-based mapper from UserViewUser with list mapping helpers, replacing the AutoMapper user mapping.
src/Exceptionless.Web/Mapping/WebHookMapper.cs Adds a Mapperly-based mapper from NewWebHookWebHook, relying on domain defaults and WebHookController.AddModelAsync to normalize Version as before.
src/Exceptionless.Web/Mapping/InvoiceMapper.cs Introduces a manual mapper from Stripe.InvoiceInvoiceGridModel that strips the "in_" prefix from Id and copies Created and Paid, plus list mapping helpers, matching the old AutoMapper AfterMap behavior.
src/Exceptionless.Web/Controllers/Base/ReadOnlyRepositoryApiController.cs Replaces generic AutoMapper-based MapAsync/MapCollectionAsync with abstract MapToViewModel / MapToViewModels, injects ApiMapper instead of IMapper, and ensures AfterResultMapAsync still strips sensitive data from IData.Data.
src/Exceptionless.Web/Controllers/Base/RepositoryApiController.cs Updates to depend on ApiMapper, adds an abstract MapToModel(TNewModel) hook, and refactors PostImplAsync / UpdateModelAsync / UpdateModelsAsync to use the new mapping hooks plus AfterResultMapAsync rather than the removed AutoMapper helpers.
src/Exceptionless.Web/Controllers/OrganizationController.cs Switches constructor from IMapper to ApiMapper, implements organization-specific MapToModel/MapToViewModel/MapToViewModels using OrganizationMapper, replaces generic mapping calls with these methods (and explicit AfterResultMapAsync calls), and maps Stripe invoices via ApiMapper.MapToInvoiceGridModels.
src/Exceptionless.Web/Controllers/ProjectController.cs Switches to ApiMapper, implements project-specific mapping methods using ProjectMapper, replaces MapAsync/MapCollectionAsync usage with these methods, and ensures AfterResultMapAsync still enriches ViewProject instances with organization data and usage stats.
src/Exceptionless.Web/Controllers/TokenController.cs Switches to ApiMapper, adds token-specific mapping overrides using TokenMapper, and replaces generic collection mapping with MapToViewModels plus AfterResultMapAsync for organization- and project-based token listing endpoints.
src/Exceptionless.Web/Controllers/UserController.cs Removes AutoMapper usage, injects ApiMapper, implements mapping overrides (using ViewUser as both new and view model and mapping UserViewUser via UserMapper), and updates organization-based user listing to use the new mapping flow plus AfterResultMapAsync.
src/Exceptionless.Web/Controllers/WebHookController.cs Replaces IMapper with ApiMapper, implements webhook mapping overrides using WebHookMapper and identity mapping for the WebHook view model, while leaving webhook permission and version-normalization logic intact.
src/Exceptionless.Web/Controllers/StackController.cs Switches injection to ApiMapper and implements mapping overrides that treat Stack as both model and view model (identity mapping), ensuring the new base class’ requirements are satisfied without changing behavior.
src/Exceptionless.Web/Controllers/EventController.cs Switches from IMapper to ApiMapper, implements identity mapping overrides for PersistentEvent, and replaces the AutoMapper-based UserDescriptionEventUserDescription mapping in SetUserDescriptionAsync with an explicit object initializer that sets all relevant properties.
tests/Exceptionless.Tests/Mapping/OrganizationMapperTests.cs Adds unit tests that validate OrganizationMapper correctly maps NewOrganizationOrganization and OrganizationViewOrganization, including suspension state and list mappings.
tests/Exceptionless.Tests/Mapping/ProjectMapperTests.cs Adds unit tests checking ProjectMapper mappings, including Slack integration detection and collection mappings.
tests/Exceptionless.Tests/Mapping/TokenMapperTests.cs Adds unit tests for TokenMapper, ensuring organization/project/notes are mapped, Token.Type is left at its default, and collection mappings work.
tests/Exceptionless.Tests/Mapping/UserMapperTests.cs Adds unit tests verifying UserMapper correctly maps core user properties, roles, organization IDs, and collections.
tests/Exceptionless.Tests/Mapping/WebHookMapperTests.cs Adds unit tests for WebHookMapper, verifying that organization/project IDs, URL, and event types are correctly transferred, including null and empty edge cases for project/event types.
tests/Exceptionless.Tests/Mapping/InvoiceMapperTests.cs Adds unit tests ensuring InvoiceMapper strips the "in_" prefix from Stripe invoice IDs, maps Created and Paid, and supports collection mapping.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

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

Development

Successfully merging this pull request may close these issues.

2 participants