Skip to content

Operation configuration

Simone Bembi edited this page Feb 4, 2018 · 5 revisions

Operation configuration

The operation configuration lets you define a configuration for a single map, defining which properties should be mapped and how.

The available methods are:

  • mapFrom(selector: (source: Source) => any)
  • ignore()
  • mapAs(selector: (source: Source) => any, signature: MapSignature)
  • immutably(value: boolean = true)
  • `getParent<ParentSource, ParentDestination>() => IOperationConfiguration<ParentSource, ParentDestination>;

The available properties are:

  • depth: number
  • source: Source
  • destination: Destination
  • signature: Signature

These properties can be used to access the source and destination objects directly.


Map from

The mapFrom method is used to enable copying a property from the source to the destination object.

e.g.

mapper.createMap<Source, Destination>(signature, Destination)
    .forMember('a', operation => operation
        .mapFrom(source => source.a)
    );

Ignore

Simply ignore the property.


Map as

It enables nested mappings: an object containing another complex object can be mapped referring to a previously set map.

e.g.

Given these classes:

class ProductEntity {
    productName: string;
    prices: Array<PriceEntity>;
}
class PriceEntity {
    price: number;
}

class ProductModel {
    name: string;
    prices: Array<PriceModel>;
}
class PriceModel {
    amount: number;
    product: ProductModel;
}

We want to map from entities to models, so we create two maps:

const productSign = {
    source: Symbol('ProductEntity'),
    destination: Symbol('ProductModel')
};

const priceSign = {
    source: Symbol('PriceEntity'),
    destination: Symbol('PriceModel')
};

const mapper = new Mapper();
mapper
    .withConfiguration(conf => conf
        .shouldRequireExplicitlySetProperties(true) // Explicitly set properties only
        .shouldAutomaticallyMapArrays(true));       // Map arrays also

mapper.createMap<ProductEntity, ProductModel>(productSign, ProductModel)
    .forMember('name', opt => opt.mapFrom(src => src.productName))
    .forMember('prices', opt => opt.mapAs(src => src.prices, priceSign));

mapper.createMap<PriceEntity, PriceModel>(priceSign, PriceModel)
    .forMember('amount', opt => opt.mapFrom(src => src.price))
    .forMember('product', opt => opt
        .mapFrom(() => {
            const parent = opt.getParent<ProductEntity, ProductModel>();
            // preserve a reference of the product in which this price is contained
            return parent && parent.destination;
        })
        .withPrecondition(() => {
            // Precondition not required but recommended
            const parent = opt.getParent<ProductEntity, ProductModel>();
            return parent && (parent.source instanceof ProductEntity);
        })
    );

This configuration will map ProductEntity into a ProductModel, and the source's PriceEntity's into PriceModel's.
As a bonus, PriceModel preserves a reference to the parent ProductModel in which the price is contained.


Immutably

Defines if a property should be mapped immutably.

Parent

The getParent method returns the parent operation configuration object, set if the current map action is triggered by another mapping (e.g. with a mapAs).
So the result of the method can be undefined.
The parent object contains its depth, source entity, destination entity and signature.

A usage example has been shown here


Source configuration

The source operation configuration is accessed when using the forSourceMember method in the Map configuration.
It supports only the ignore method.

e.g.

mapper.createMap<Source, Destination>(signature, Destination)
    .withConfiguration(configuration => configuration
        .shouldRequireExplicitlySetProperties(false)
        .shouldIgnoreSourcePropertiesIfNotInDestination(false))
    .forMember('a', operation => operation
        .mapFrom(source => source.a)
        .withPrecondition(() => operation.destination.a === undefined)
        .immutably()
    )
    .forSourceMember('z', operation => operation.ignore());

const dest = mapper.map<Source, Destination>(signature, src);

console.log((dest as any).z);
// logs 'undefined' even if shouldRequireExplicitlySetProperties and  
// shouldIgnoreSourcePropertiesIfNotInDestination are set to false because 
// the property is set to be ignored