-
-
Notifications
You must be signed in to change notification settings - Fork 513
refactor: Migrate from AutoMapper to Mapperly #2093
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
base: main
Are you sure you want to change the base?
Conversation
- 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
8ed51f6 to
1328834
Compare
There was a problem hiding this 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.CoreandExceptionless.Web, addingRiok.Mapperlyand anApiMapperfacade with per-type mappers (OrganizationMapper,ProjectMapper,TokenMapper,UserMapper,WebHookMapper,InvoiceMapper). - Refactored
ReadOnlyRepositoryApiControllerandRepositoryApiControllerto use abstractMapToModel/MapToViewModel/MapToViewModelsmethods implemented by concrete controllers instead of genericMapAsync/MapCollectionAsync. - Updated controllers (
OrganizationController,ProjectController,TokenController,UserController,WebHookController,EventController,StackController) to depend onApiMapperand implement the new mapping hooks, and added focused mapper unit tests undertests/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 NewOrganization → Organization and Organization → ViewOrganization, 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 NewProject → Project and a two-stage Project → ViewProject mapping (core Mapperly map plus explicit HasSlackIntegration computation), plus list mapping helpers. |
src/Exceptionless.Web/Mapping/TokenMapper.cs |
Adds a Mapperly-based mapper from NewToken → Token that ignores Token.Type (leaving its default) and maps Token → ViewToken, plus list mapping helpers; preserves prior AutoMapper semantics around token type. |
src/Exceptionless.Web/Mapping/UserMapper.cs |
Adds a Mapperly-based mapper from User → ViewUser with list mapping helpers, replacing the AutoMapper user mapping. |
src/Exceptionless.Web/Mapping/WebHookMapper.cs |
Adds a Mapperly-based mapper from NewWebHook → WebHook, relying on domain defaults and WebHookController.AddModelAsync to normalize Version as before. |
src/Exceptionless.Web/Mapping/InvoiceMapper.cs |
Introduces a manual mapper from Stripe.Invoice → InvoiceGridModel 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 User→ViewUser 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 UserDescription→EventUserDescription 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 NewOrganization→Organization and Organization→ViewOrganization, 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.
Summary
Migrates from AutoMapper (runtime reflection-based mapping) to Mapperly (compile-time source generation) for improved performance and type safety.
Changes
Package Updates
Architecture Changes
Mapping Structure
Created dedicated mapper files per type in \src/Exceptionless.Web/Mapping/:
\ApiMapper\ acts as a facade delegating to individual mappers.
Testing
Benefits
Breaking Changes
Controllers that inherit from \RepositoryApiController\ or \ReadOnlyRepositoryApiController\ must now implement the abstract mapping methods.