netglade / auto_mappr Goto Github PK
View Code? Open in Web Editor NEWCode generation for mapping between different objects with ease.
Home Page: https://pub.dev/packages/auto_mappr
License: MIT License
Code generation for mapping between different objects with ease.
Home Page: https://pub.dev/packages/auto_mappr
License: MIT License
Converting does not work for mapping from/to sub-classes.
"in this type" is meant literally 🤦
analyzer
to version 6auto_mappr_annotation
RESOLVED
the below issue arose when trying to convert from UInt8list to List without a custom field mapping.
Describe the bug
Apologies for the lack of a clear description.
I have been using mappr succesfully for about 50 conversions with varying complexity.
I then created another 30+ mappings with custom fields, and now out of the blue, whenever i run the tool i get
[SEVERE] auto_mappr on lib/clients/transport_models/mappr.dart (cached):
Bad state: No element
[SEVERE] Failed after 101ms
To Reproduce
dart run build_runner build
Expected behavior
generation to complete
Version info
auto_mappr: ^1.3.1
Additional context
[SEVERE] auto_mappr on lib/clients/transport_models/mappr.dart:
Bad state: No element
dart:core _Array.first
package:auto_mappr/src/builder/value_assignment_builder.dart 77:80 ValueAssignmentBuilder._assignIterableValue
package:auto_mappr/src/builder/value_assignment_builder.dart 44:14 ValueAssignmentBuilder.build
package:auto_mappr/src/builder/map_model_body_method_builder.dart 255:13 MapModelBodyMethodBuilder._mapConstructor
package:auto_mappr/src/builder/map_model_body_method_builder.dart 227:12 MapModelBodyMethodBuilder._processConstructorMapping
package:auto_mappr/src/builder/map_model_body_method_builder.dart 58:35 MapModelBodyMethodBuilder.build
package:auto_mappr/src/builder/auto_mappr_builder.dart 94:15 AutoMapprBuilder._buildMethods.<fn>
package:code_builder/src/specs/method.g.dart 333:33 _$MethodBuilder.update
package:code_builder/src/specs/method.g.dart 38:29 new _$Method
package:auto_mappr/src/builder/auto_mappr_builder.dart 79:9 AutoMapprBuilder._buildMethods
package:auto_mappr/src/builder/auto_mappr_builder.dart 40:34 AutoMapprBuilder.build.<fn>.<fn>
package:code_builder/src/specs/class.g.dart 292:33 _$ClassBuilder.update
package:code_builder/src/specs/class.g.dart 34:28 new _$Class
package:auto_mappr/src/builder/auto_mappr_builder.dart 37:13 AutoMapprBuilder.build.<fn>
package:code_builder/src/specs/library.g.dart 186:33 _$LibraryBuilder.update
package:code_builder/src/specs/library.g.dart 24:30 new _$Library
package:auto_mappr/src/builder/auto_mappr_builder.dart 32:12 AutoMapprBuilder.build
package:auto_mappr/src/generator/auto_mappr_generator.dart 89:28 AutoMapprGenerator.generateForAnnotatedElement
package:source_gen/src/generator_for_annotation.dart 53:30 GeneratorForAnnotation.generate
package:source_gen/src/builder.dart 355:33 _generate
Nothing more to add, is there anyway to get any more information on this issue.
I am happy to post my _Mappr class but it is quite large.
Hello,
It seems that flutter pub run build_runner build
command from the README.md gives a deprecated message, as shown below. Using the second stated command dart run build_runner build
does not give this message (as expected).
Do you recommend to use the dart run build_runner build
command from now on, even for flutter projects?
C:\work\app-flutter>flutter pub run build_runner build
Deprecated. Use `dart run` instead.
Building package executable... (21.1s)
Built build_runner:build_runner.
[INFO] Generating build script completed, took 673ms
[INFO] Precompiling build script... completed, took 9.8s
... snip ...
C:\work\app-flutter>flutter --version
Flutter 3.10.0 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 84a1e904f4 (7 days ago) • 2023-05-09 07:41:44 -0700
Engine • revision d44b5a94c9
Tools • Dart 3.0.0 • DevTools 2.23.1
Describe the bug
A clear and concise description of what the bug is.
To Reproduce
Code to reproduce issue:
I followed this documentation:
https://pub.dev/packages/auto_mappr#map-objects-mapping
These steps:
"Modules
Each AutoMappr class can be used as a module. That means a mappr used inside of another mappr. Each AutoMappr class can include a list of modules that can be used to nest modules and use all of its underlying mappings.
Applications are often split into independent parts (we will call them features). Each feature should probably have its own independent mappr, that is used as a module.
Imagine that in a feature you have a local mappr UserMappr.
// file: user_mappr.dart
@AutoMappr([
MapType<UserDto, User>(),
])
class UserMappr extends $UserMappr {
const UserMappr(); // must implement const constructor
}
And in some global place, you can have a main mappr that unifies all smaller mapprs (UserMappr in this case). As usual, it can also set it's own mappings (MapType<GroupDto, Group>()).
// file: main_mappr.dart
@AutoMappr(
[
MapType<GroupDto, Group>(),
],
modules: [
UserMappr(), // use module
],
)
class MainMappr extends $MainMappr {}"
Expected behavior
A clear and concise description of what you expected to happen.
Version info
Additional context
Expected: Mapping of 2 classes while mapping parent class.
Result: Trying to map nested object from "HiveSetImages images -> SetImages images" but no mapping is configured.
In README, there is no mention of the convert
(in the future also canConvert
) method, which should be mentioned in the How to use chapter.
Hi,
Imagine you have these 2 classes:
@freezed
class Equipment with _$Equipment {
const factory Equipment({
int id,
int type,
}) = _Equipment;
}
@freezed
class EquipmentForm with _$EquipmentForm {
const factory EquipmentForm ({
int id,
required String name,
}) = EquipmentForm ;
}
And your mapper is like this:
@AutoMappr([
MapType<Equipment, EquipmentForm>(
fields: [
Field('name', whenNull: ''),
],
),
])
When running the generator, you get this message:
Can't generate mapping Equipment → EquipmentForm as there is non mapped not-nullable required named parameter name
I'd say this should work and set the whenNull value, right?
Thanks.
Right now, we support only exact mappings of fields. Like auto
into auto
. We should add support for non-matched fields to also try to look up other forms like Auto
, AUTO
, áuto
, aútó
etc. It should consider:
We are currently using the "use the one with the most parameters" strategy to find the best constructor. Or one can also set the constructor in the annotation.
With this enhancement, the strategy should find a better-fitter constructor according to field names, types etc.
The convert
method returns T
, and when a source is null, it returns the whenSourceIsNull
value if it is present or an Exception
.
While it might be the use case we want, some people might want a less strict method that could return null when the source is null, etc. To make it type-safe, the original convert
method should still return T
while the new method tryConvert
should be created with return type T?
.
With spec
Target convert<Source, Target>(Source? model);
Target? tryConvert<Source, Target>(Source? model);
I am trying to map my graphql classes to my local model, but I keep running into:
[SEVERE] auto_mappr on lib/services/graphql/mappr.dart:
dynamic is not a class and cannot be mapped from
package:nurtio/services/graphql/mappr.dart:17:7
╷
17 │ class Mappr extends $Mappr {}
│ ^^^^^
If I copy the class from my "services/graphql/queries.graphql.dart" file to the mappr.dart file, it works fine. So expect some ordering issue while generating the files.
I have tried both runs_before
and required_inputs
, but both seem to have no effect.
My build.yaml
targets:
$default:
builders:
graphql_codegen:
options:
clients:
- graphql
- graphql_flutter
scalars:
AWSDateTime:
type: DateTime
JSON:
type: Map<String, dynamic>
runs_before:
- auto_mappr:auto_mappr
auto_mappr:
enabled: true
options:
required_inputs:
[
"services/graphql/queries.graphql.dart",
".freezed.dart",
".drift.dart",
]
Is there somebody that knows what I missed? Thanks.
class a{
final String x;
final B b;
a(this.x, this.b);
}
class B{
final String y;
B(this.y);
}
Describe the bug
I have multiple mappers that uses dame custom field mapping. Lets say there are multiple fields across mappers where I use same custom converters.
In this case static methods without any Source model argument is required, but looks like converters are not allowed without the source model
For example :
From your example I cant make
int mapAge(UserDto _) => 42;
as
int mapAge() => 42;
To Reproduce
Code to reproduce issue:
Expected behavior
Global methods should support methods without any source model
Version info
Additional context
Add any other context about the problem here.
I am trying to add a custom map value gotten from a future as such:
@AutoMappr([
MapType<UserClass, LearningClass>(
fields: [
Field('id', from: 'id'),
Field('name', whenNull: 'JS1'),
Field('isAvailable', custom: Mappr.mapIsAvailable),
],
),
])
class Mappr extends _$Mappr{
static Future<bool> mapIsAvailable(UserClass userClass) async{
SecureStorageController secureStorageController = SecureStorageController();
final grade = await secureStorageController.getUserClass();
return userClass.name == grade;
}
However i dont want to assign the future directly to the isAvailable. Just the pure value. Are there any provisions for that without having to explicitly use "then" in the Field block.
I have the use case that I need to define a custom mapping function for an entire object. The problem is that this Object has inner objects which can be "automatically" mapped by other MapType definitions.
My proposed solution would be to dynamically inject an instance of the mapper into the custom convert function. This could be done for both a new MapType.custom
but also the existing custom conversion functions in TypeConverter
s and Field.custom
s. The idea would be to "detect" that the provided function takes in two parameters instead of one e.g. when looking at Field.custom
not only accepting Target Function(Source dto)
but also Target Function(MapprInstance mappr, Source dto)
.
A full example of this imaginary syntax could look like this:
class WrapperDto {
final SuperStringDto? superStringDto;
final SuperIntDto? superIntDto;
}
sealed class Wrapper {}
class StringWrapper implements Wrapper {
final SuperString superString;
}
class IntWrapper implements Wrapper {
final SuperInt superInt;
}
class SuperStringDto {}
class SuperString {}
class SuperIntDto {}
class SuperInt {}
@AutoMappr([
MapType<WrapperDto, Wrapper>.custom(mapWrapper),
MapType<SuperStringDto, SuperString>(),
MapType<SuperIntDto, SuperInt>(),
])
class ImaginaryMappr extends $ImaginaryWrapper {
const ImaginaryMappr();
}
Wrapper mapWrapper(ImaginaryMappr mappr, WrapperDto dto) {
if (dto.superStringDto != null) {
return StringWrapper(mappr.convert(dto.superStringDto!));
}
if (dto.superIntDto != null) {
return IntWrapper(mappr.convert(dto.superIntDto!));
}
throw Exception('Invalid wrapper dto');
}
I'd be open to implement this feature because unless I overlooked some existing feature to handle my use case I am kinda dependent on a solution :D
Describe the bug
I have the following mapping:
@AutoMappr([
MapType<UserInfo, UserInfoTblCompanion>(constructor: 'insert'),
])
class Mappr extends $Mappr {}
I get the build error:
Can't generate mapping UserInfo -> UserInfoTblCompanion as there is non mapped not-nullable required named parameter email
Of note:
Foo
class that I created for debuggingSpeculation:
Does this have something to do with:
Here is my sample freezed class
class UserInfo with _$UserInfo {
factory UserInfo(
{int? id,
required String email,
required String loginIdentifier,
required DateTime updatedAt,
@Default(0) int primarySectionId}) = _UserInfo;
}
Freezed and Drift are often used in mappr. Now users need to tweak build.yaml for correct order of generators.
With this change, we can support freezed and Drift by default.
If an object has nested objects, right now all nested objects have to be added to the @AutoMappr
annotation. With this feature, the unknown mappings should try to generate code for them implicitly, so users can eventually only mark the topmost object mapping.
Aka for this object structure
it will be possible to only map AlphaDto to Alpha
without specifying BetaDto to Beta
and GamaDto to Gama
.
The mapping UserDto -> User
should convert:
UserDto
to User
using convert<UserDto, User>
UserDto
to User
or null
using tryConvert<UserDto, User>
#21But also an iterable to iterable. Because we cannot make union types, we have to create a new function convertIterable
:
List/Set/Iterable<UserDto>
to Iterable<User>
using convertIterable<UserDto, User>
List/Set/Iterable<UserDto>
to Iterable<User>
or null
using tryConvertIterable<UserDto, User>
With specs:
Iterable<Target> convertIterable(Iterable<Source?> modelList);
Iterable<Target?> tryConvertIterable(Iterable<Source?> modelList);
Sometimes you generally want to convert some TypeA
to TypeB
for a dynamic number of fields. I'd like to define general mapping functions for this use case. Something like:
class Dto {
final String dateTimeValue;
}
class Domain {
final DateTime dateTime;
}
@AutoMappr([
MapType<Domain, Dto>(
fields: [
Field('dateTimeValue', from: 'dateTime'),
],
types: [
TypeConvert<DateTime, String>(Mappr._dateTimeToIso8601String),
],
),
])
class Mappr extends $Mappr {
static String _dateTimeToIso8601String(DateTime dateTime) {
return dateTime.toIso8601String();
}
}
The API is just an example to illustrate the idea.
Also this API should probably be available both on the MapType
as well as on the AutoMappr
level.
Describe the bug
If you use a class name with a $ sign, it will cause compiler errors in the .g.dart file. It seems the name is directly inserted into a Exception message without escaping.
This might occur with the default setting of the graphql_codegen package, which generates class names like Query$character$character
.
For example:
throw Exception(
'Mapping Query$character$character → Character failed because Query$character$character was null, and no default value was provided. '
'Consider setting the whenSourceIsNull parameter on the MapType<Query$character$character, Character> to handle null values during mapping.');
To Reproduce
Code to reproduce issue:
flutter pub run build_runner build
Demo project: mapprgraphql.zip
Expected behavior
The class names are espaced before they are merged into strings.
For example:
throw Exception(
'Mapping Query\$character\$character → Character failed because Query\$character\$character was null, and no default value was provided. '
'Consider setting the whenSourceIsNull parameter on the MapType<Query\$character\$character, Character> to handle null values during mapping.');
Version info
Additional context
As a workaround the graphql_codegen
can be instructed to change all the class names to something safe, like using the _.
targets:
$default:
builders:
graphql_codegen:
options:
clients:
- graphql
- graphql_flutter
namingSeparator: "_" # <------- overwrite the default behavior to a safe char
auto_mappr:
enabled: false
auto_mappr_after_graphql_codegen:
enabled: true
builders:
auto_mappr_after_graphql_codegen:
import: "package:auto_mappr/auto_mappr.dart"
builder_factories: ["autoMapprBuilder"]
build_extensions: { ".dart": [".auto_mappr.g.part"] }
auto_apply: root_package
build_to: cache
applies_builders: ["source_gen:combining_builder"]
required_inputs: [".freezed.dart", ".drift.dart", ".graphql.dart"]
Add option to force "non-nullable" value for nullable source's field.
Proposal:
Configurable option for generating forced non-null value when source's field is nullable but target's field not. E.g. it should generate
sourceField: targetField!
ignoreNullableSourceField
Similar to:
felangel/bloc#139
reactiveui/ReactiveUI#979
PrismLibrary/Prism#1016
orientechnologies/orientdb#4806
Use the following template:
If available on stores
iOS:
Android:
If open source
Source Code:
Your opinion
Version: ?
Years of experience: ?
Good: ?
Bad: ?
Thank you very much!
Type converters in MapType
and @AutoMappr
right now accept only <Object, Object>
(final List<TypeConverter<Object, Object>> converters;
).
While this makes sense for mappings, for type converters we want to distinguish and be able to handle nullable types, ergo the contract should probably be <Object?, Object?>
and the logic behind type converters should be enhanced to support that.
This is for cases when I have a converter for String to DateTime, but the source/target is nullable. For example, it would pass source String? to that converter which ends in a type mismatch.
Implementation is straightforward, just keep in mind that:
In cases when Object
is expected (source or target) and a type converter is set with Object?
, this converter should handle it.
In cases when Object?
is expected (source or target) and a type converter is set with Object
, this converter should not be used.
Is your feature request related to a problem? Please describe.
--
Describe the solution you'd like
A reverse
option to @AutoMapper
annotation to generate a reverse mapping.
/// Reverse mapping will be generated as well (from TARGET to SOURCE).
///
/// Note that if concrete mapping from TARGET -> SOURCE is configured, reverse flag is ignored.
final bool reverse;
Describe alternatives you've considered
--
Additional context
While reverse makes sense in cases like UserDto
to/from User
, it might be hard to do the mapping in cases like List<Complex>
vs List<String
. We should think of how to handle non-trivial cases.
When enums are not the same class, it should try to convert the values by name. Aka, the target has to be a superset of the source. Using ignore
, from
, custom
, and defaults should work the same way as for classes.
We use DCM in this project, and it fails for "external" PRs. Since this is quite hard to set correctly since secrets for the DCM key are not shared (GitHub security), we should switch to DCM OSS, which would enable the CI and also devs in IDEs so people see DCM lints, etc.
https://dcm.dev/blog/2023/10/18/announcing-dcm-license-for-oss-projects/
Hey,
I've stumbled upon this library a couple of days ago and it looks awesome! I'm experimenting at the moment and so far i was able to make it work with every case i've tried, except one.
I have a Data Model:
@freezed
class ExtendedContactDataModel with _$ExtendedContactDataModel {
const factory ExtendedContactDataModel({
required String id,
required String? phoneNumber,
required String? remoteId,
required String name,
}) = _ExtendedContactDataModel;
}
And a sealed class (Domain model)
@freezed
sealed class ContactModel with _$ContactModel {
const factory ContactModel.phonebook({
required final String id,
required final String name,
required final String phoneNumber,
}) = PhonebookContact;
const factory ContactModel.synced({
required final String id,
required final String name,
required final String remoteId,
}) = SyncedContact;
factory ContactModel.fromData({
required final String id,
required final String? remoteId,
required final String name,
required final String? phoneNumber,
}) {
if (remoteId == null) {
return ContactModel.phonebook(
id: id,
name: name,
phoneNumber: phoneNumber,
);
} else {
return ContactModel.synced(
id: id,
remoteId: remoteId,
name: name,
);
}
}
}
Unfortunately, the generated code from automappr is missing the phonenumber and remoteId:
return _i8.ContactModel.fromData(
id: model.id,
name: model.name,
);
And here is the definition:
mappr.MapType<ExtendedContactDataModel, ContactModel>(
constructor: 'fromData',
)
Hi, i currently have a project that uses drift db and mappr together.
Obviously, mapping types to drift's generated dataclass is trivial, however, i need to map types to drift's generated companion class.
Now, all fields of a companion class are types wrapped in a "Value"
So far i can achieve primitives mapping like so..
MapType<int, Value<int>>(fields: [
Field('value', custom: Mappr.testInt),
whenSourceIsNull: Value.absent()
]),
static int testInt(int int) => int;
however, when trying to map an Enum to a Value i am running to issues with whatever i try.
MapType<DocFileType, Value<DocFileType>>(
fields: [Field('value', custom: Mappr.testEnum)],
whenSourceIsNull: Value.absent()),
static T testEnum<T>(T enu) => enu;
i get a Failed to map DocFileType → Value<DocFileType> because target Value<DocFileType> is not an enum
error.
Now, according to the docs -
The target enum can either be a superset of the source or has to define whenSourceIsNull which will be used for unknown enum values. If the target is not a superset of the source enum the generator will throw.
i have also tried a generic MapType<Enum, Value<Enum>>
with a custom function which appears to work, and generates
if ((sourceTypeOf == _typeOf<Enum>() || sourceTypeOf == _typeOf<Enum?>()) &&
(targetTypeOf == _typeOf<Value<Enum>>() ||
targetTypeOf == _typeOf<Value<Enum>?>())) {
if (canReturnNull && model == null) {
return null;
}
return (_map_Enum_To_Value$Enum((model as Enum?)) as TARGET);
}
however, this doesnt pick up my enums whens mapping occurs.
Not sure what im doing wrong, or if this is even possible.
Any help appreciated.
Thanks
Is your feature request related to a problem? Please describe.
Implement a flattening feature for mapping similar to https://docs.automapper.org/en/stable/Flattening.html
Describe the solution you'd like
The mapping would flatten source properties automatically or with an enabled flag into target properties.
get*
prefixMaybes:
set*
prefix?I'd like to define a fallback enum value for "unknown" values.
It may be beneficial to make some "mapping profiles" that would basically extend the mapper with some other mappers.
Imagine modules A, B, C, where you create MapprA
, MapprB
, MapprC
, because you do not want to import the whole project into the root. (And the mappr library has to have direct imports because the generated part file uses the types directly) With this feature, each module would only need to export a mapper, and you can create one grouping mapper that will
@AutoMappr([
MapType<...>(),
...
], import: [
MapprA(),
MapprB(),
MapprC(),
],
)
class Mappr
To make it typeSafe, we should probably create some interface that every generated $Mappr
class would extend with interface for try/convert
, try/convert{Iterable, List, Set}
, and a new function canConvert
.
Then each convert, tryConvert, ... method would do something like this:
TARGET convert<SOURCE, TARGET>(SOURCE? model) {
if (canConvert(model)) {
return _convert(model)!;
}
for (final mappr in mappers) {
if (mappr.canConvert) {
return mappr.convert(model)!;
}
}
// or exception
}
which would allow grouping mappers. Each can still work standalone but with the benefit of making one super mappr with imported sub-mapprs.
canConvert
methodAutoMapprInterface
(?) abstract class with try/convert
, try/convert{Iterable, List, Set}
, canConvert
method interfacesIf I declare multiple dto/domain mappings, where each of them is a two-directional mapping, I'd like to have a single place that lets me generate such code. Right now, there are two ways of achieving that:
AutoMappr
mappersreverse
flag in each AutoMappr
mapperConsider adding reverse
flag to the build config options so that the behavior can be configured globally:
targets:
$default:
auto_mappr:
options:
reverse: true
I just found the use case that I have two Models like the following (shortened):
class Dto {
final String dateTimeValue;
}
class Domain {
final DateTime dateTime;
}
@AutoMappr([
MapType<Domain, Dto>(fields: [
Field(
'dateTimeValue',
from: 'dateTime',
custom: Mappr._dateTimeToIso8601String,
),
]),
])
class Mappr extends $Mappr {
static String _dateTimeToIso8601String(DateTime dateTime) {
return dateTime.toIso8601String();
}
}
I therefore need to convert the DateTime to a String using a custom function but also define the from
Parameter.
I don't want to write the custom mapping function over and over again so to increase reusability I'd like the from
parameter to narrow the input for the custom mapping function.
We currently do not support mapping objects with generics.
To do so, the mapping should hold a note of source/target generics information and consider them in mappings.
The information can be accessed in two lists, one with real types (int
etc.) and the second with generic alias (T
etc.):
(sourceType as ParameterizedType).typeArguments // -> [int]
(sourceType.element! as ClassElement).typeParameters // -> [T]
Currently, automappr recognize typedefs as "standalone" type so MapType has to be configured.
It would be nice if we could lookup which type is aliased with typedef and check for existing MapType configuration
Allow configuring build options on the Mapper or even MapType
level. For example the option ignoreNullableSourceField
could be useful for a subset of mappings but currently would need to be enabled for the entire package. I'd propose adding something like a AutoMapprConfig
object which could be assigned to Mappers / MapType
s to enable/disable build rules.
It would be nice to support async
mapping, such as "custom" on Field can return Future.
This proposal needs to be specified
Hi,
I don't know if there's a workaround for this.
In my project I have the case where one single class (form) is a combination of several other classes (model objects that reflect tables from the database).
I have for example these 2 models:
@freezed
class EquipmentBase with _$EquipmentBase {
const factory EquipmentBase({
required int id,
required String name,
}) = _EquipmentBase;
}
@freezed
class Equipment with _$Equipment {
const factory Equipment({
required int id,
required int type,
}) = _Equipment;
}
And this single form object:
@freezed
class EquipmentForm with _$EquipmentForm {
const factory EquipmentForm({
required int id,
required String name,
required int type,
}) = _EquipmentForm;
}
I'd like to be able to get an EquipmentForm (TARGET) based on both Equipment and EquipmentBase (SOURCES)
At the moment I just make a double conversion and combine manually the properties at the end:
Is there anyway I can do this at the moment? Maybe through a custom converter or similar?
Thanks.
I sometimes have identically named models in an api and domain package. Therefore I need to alias the import of the api package. This unfortunately generates the model names without the import alias resulting in invalid mapping code.
import 'package:api/api.dart' as api;
import 'package:auto_mappr_annotation/auto_mappr_annotation.dart';
import 'package:domain/domain.dart';
part 'test_mapper.g.dart';
@AutoMappr([
MapType<Test, api.Test>(),
MapType<api.Test, Test>(),
])
class Mappr extends $Mappr {}
When field types mismatch when primitive, right now, we ignore it.
We can either:
Global converters seem to work incorrectly when combined with the reverse flag. Here's a simple example where I was able to reproduce that behavior using a simple types structure:
import 'package:auto_mappr_annotation/auto_mappr_annotation.dart';
import 'mappr.auto_mappr.dart';
@AutoMappr(
[
MapType<PostDto, Post>(reverse: true),
],
converters: [
userDtoConverter,
userConverter,
],
)
class Mappr extends $Mappr {}
class Post {
final User user;
Post({required this.user});
}
class PostDto {
final UserDto user;
PostDto({required this.user});
}
class User {
final String id;
User({required this.id});
}
class UserDto {
final String id;
UserDto({required this.id});
}
const userDtoConverter = TypeConverter(_userDtoToUser);
const userConverter = TypeConverter(_userToUserDto);
UserDto _userToUserDto(User source) => UserDto(id: source.id);
User _userDtoToUser(UserDto source) => User(id: source.id);
Now, if mappers are expressed as two separate entries, the error is gone:
@AutoMappr(
[
MapType<PostDto, Post>(),
MapType<Post, PostDto>(),
],
converters: [
userDtoConverter,
userConverter,
],
)
class Mappr extends $Mappr {}
Package version:
auto_mappr: 2.0.0
auto_mappr_annotation: 2.0.0
When the target field and its constructor parameter mismatch in naming, we cannot detect whether the field has been set. We can only detect that for formal constructor parameters (this.alpha
or super.alpha
), but not for non-formal that use the constructor's initializer list (Target(int something): alpha = something;
).
It is probably not possible to access the initializer list using Elements, but it should be possible to access it using AST tree. https://www.reddit.com/r/dartlang/comments/11ulrs7/constructors_initializer_list_in_code_generation/
Suppose we have shared Dto which describes common data like
class CommonDto {
final String id;
final String name;
...
}
This Dto can be used acrros different dtos. For each mappr's module where we define concrete mappings for Dtos from such module.
Expectation is that mapping "CommonDto -> CommonObject" which is defined in main mapper should be available.
There should be way to concrete "AutoMappr" either mark as "Only as module" type thus some mappr's checks will be removed OR somehow tell AutoMappr that there is "globally" available mapping.
Concrete example:
Mapping between CommonDto -> CommonObject should be defined "globally"
@AutoMappr([
MapType<CommonDto, CommonObject>()
], modules: [
ModuleAMappr()
])
class Mappr extends $Mappr {}
In ModuleA we have defined mapping between UserDto -> User which has dependency on CommonDto (CommonObject)
class UserDto {
final int id;
final CommonDto roleKey;
}
Ideally we could defined ModuleA mappr like
@AutoMappr([
MapType<UserDto , User>()
])
class ModuleAMappr extends $ModuleAMappr {}
How to extend it ?
@AutoMappr([
MapType<UserDto , User>()
], isModule: true)
class ModuleAMappr extends $ModuleAMappr {}
This could disable some checks, etc., but not sure if it can be done this way. Needs more investegation.
@AutoMappr([
MapType<UserDto , User>()
], dependsOn: [
CommonMappr //Type, not instance of CommonMappr
])
class ModuleAMappr extends $ModuleAMappr {}
where CommonMappr should be defined as standalone mappr
@AutoMappr([
MapType<CommonDto, CommonObject>()
])
class ComnonMappr extends $CommonMappr {}
Final global main mappr could be just
@AutoMappr([
], modules: [
CommonMappr(),
ModuleAMappr()
])
class Mappr extends $Mappr {}
With this approach, each automappr could proably read @AutoMappr
annotation from its dependencies and check if there is existing mapping etc.
Version info
I want to propose another build option I'd call strict
mode which disables automatically setting fields to null if there is no mapper function available. The warning can be ignored to easily this option would fail the build if a mapper cannot be found.
Sidenote: The implementation should probably find as many errors as possible before failing in the end.
When reverse
is used, support that Field
's mapping from
parameter is also reversed
Such that
class AddressDto extends Equatable {
final String street;
final String city;
@override
List<Object?> get props => [street, city];
const AddressDto({required this.street, required this.city});
}
class SpecialAddress extends Equatable {
final String specialStreet;
final String specialCity;
@override
List<Object?> get props => [specialStreet, specialCity];
const SpecialAddress({required this.specialStreet, required this.specialCity});
}
// mappr
MapType<AddressDto, SpecialAddress>(
fields: [
Field('specialStreet', from: 'street'),
Field('specialCity', from: 'city'),
],
reverse: true,
),
Make sure inheritance works: I can map ADto
to A
and instead of ADto
I can use B
or C
.
class A {
final int value;
A(this.value);
}
// case 1
class B extends A {
B(super.value);
}
// case 2
class C extends A {
final String text;
C(this.text, super.value);
}
Implement support for record types.
Note: we might merge it even before we have Dart 3 since the analyzer already can know the record type.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.