Giter Site home page Giter Site logo

auto_mappr's People

Contributors

dependabot[bot] avatar devnico avatar ekerik220 avatar petrnymsa avatar tenhobi avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

auto_mappr's Issues

Bad state: No element

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.

`flutter pub run build_runner build` shows 'Deprecated' message

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

Nested mapping (modules) doesn't work

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

  • Package version :
    auto_mappr_annotation: ^1.1.0
    auto_mappr: ^1.4.0

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.

Document convert methods

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.

Generation fails when required TARGET field doesn't exist in SOURCE even with whenNull

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.

Map non-exactly matched fields

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:

Improved constructor selection strategy

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.

Add nullable convert method

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);

Is it possible to combine graphql_codegen and auto_mappr?

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.

Global Methods dont work with out type

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

  • Package version :

Additional context
Add any other context about the problem here.

Custom Mapping Futures

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.

Custom MapType

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 TypeConverters and Field.customs. 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

Can't use freezed generated class as a source

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:

  • UserInfoTblCompanion is generated by drift
  • UserInfo is generated by freezed, but is in a different package (imported via path: ../client)
  • I have tried various shared and non shared builders in build.yaml with no result
  • UserInfoTblCompanion does not appear to be the issue as I can map it OK to a test Foo class that I created for debugging

Speculation:

Does this have something to do with:

  • The freezed class being in a different package
  • The freezed class using a factory constructor
  • The freezed class not having top level getters (e.g. for email) as they are generated as part of the mixin (_$UserInfo)

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;
}

Implicit mappings of nested objects

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

  • alpha
    • beta
      • gama

it will be possible to only map AlphaDto to Alpha without specifying BetaDto to Beta and GamaDto to Gama.

Add support for converting iterables

The mapping UserDto -> User should convert:

  • UserDto to User using convert<UserDto, User>
  • UserDto to User or null using tryConvert<UserDto, User> #21

But 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);

Allow specifying type converters

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.

Class names including $ cause compile error

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:

  1. Add a class with a $ in the mapping annotation
  2. Run 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

  • Package version : 1.3.1

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"]

Allow to force non-nullable type on nullable source field.

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!
  • Globally configurable option via build.yaml ignoreNullableSourceField
  • On MapType level - within type mapping every source nullable field affected except those with whenNull configuration, custom mapping or default value
  • On Field level - generates field if whenNull or custom is not set

Nullable input/output with type converters

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.

Reverse mapping

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.

Convert enums

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.

  • enums
  • enums with values

Mapping to sealed class

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',
)

Mapping Enums to custom type

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

Flattening into simpler model

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* prefix
  • object flattening

Maybes:

  • make it work for also set* prefix?

Merging mappers

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.

  • create canConvert method
  • create AutoMapprInterface (?) abstract class with try/convert, try/convert{Iterable, List, Set}, canConvert method interfaces
  • extend convert methods to support imported extended mappers

Support reverse flag in build.yaml config

If 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:

  • add dto -> domain and domain -> dto entries separately in AutoMappr mappers
  • set reverse flag in each AutoMappr mapper

Consider adding reverse flag to the build config options so that the behavior can be configured globally:

targets:
  $default:
      auto_mappr:
        options:
          reverse: true

Allow using `from` and `custom` together

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.

Mapping objects with generics

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]

Support for typedefs

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

Build Options as Config object

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 / MapTypes to enable/disable build rules.

Async support

It would be nice to support async mapping, such as "custom" on Field can return Future.

This proposal needs to be specified

Combining multiple SOURCEs into one single TARGET

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:

  • Convert EquipmentBase to EquipmentForm
  • Convert Equipment to EquipmentForm
  • Use copyWith() to combine both EquipmentForm objects

Is there anyway I can do this at the moment? Maybe through a custom converter or similar?

Thanks.

Support import aliases

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 {}

Primitive types mismatch

When field types mismatch when primitive, right now, we ignore it.

We can either:

  1. Be pedantic and don't do anything magical. -- primitive types mismatch -> fail
  2. Be optimistic and try to convert what we can -- String to int, int to String, etc.

Global converter not found when used with reverse flag

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

Field name doesn't match constructor argument name

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/

Add support for mappr's module dependencies

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 ?

  1. First - just mark it as "module"
@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.

  1. Second - define "ancestors" / "dependencies"
@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

  • Package version : 1.4.0

Strict mode

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.

Support field name mapping in reverse mode

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 properly

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 Dart 3 recods

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.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.