1.1.1 • Published 7 years ago

dee-mapper v1.1.1

Weekly downloads
3
License
MIT
Repository
github
Last release
7 years ago

Dee Mapper

npm npm Coverage Status dependency Status devDependency Status Build Status Known Vulnerabilities node

Object to Object mapper for NodeJS.

Table of contents

Installation

npm install dee-mapper --save

NodeJS >=6.0.0 is required.

Usage

To use the mapper you need to accomplish 3 simple steps:

1) Create mapper instance.

  • Just import dee-mapper and call it with new keyword.

2) Register new mapping.

  • Specify fields convention, mapping method name, source object type, destination object type and configuration callback (optionally). Configuration callback is used for mapping customizations.

3) Call the method from step 2 on a mapper instance.

The example of code:

const Mapper = require('dee-mapper');
const mapper = new Mapper(); // step 1: create mapper instance

// step 2: register new map instance
mapper.register(mapper.SNAKE_CASE_CONVENTION, 'testMap', Object, Object, (map) => {
  // here 'map' is a mapping instance
  // you can configure the instance to customize mapping
   
  map.ignoreField('field_3')
    .mapField('field_2', obj => obj.field2.toUpperCase())
    .mapFieldByPath('field_1', 'field1');
});

// step 3: call the method from step 2 on a mapper instance
const result = mapper.testMap({
  field1: 'test1',
  field2: 'test2',
  field3: 'test3'
});
console.log(result); // { field_1: 'test1', field_2: 'TEST2' }

Conventions

Conventions are responsible for converting field names of source object in appropriate field names of destination object.

For example, snake case convention means that field with name 'firstName' in source object will be mapped to 'first_name' field in destination object. Dee-mapper supports 3 types of conventions: camel case, pascal case and snake case.

You can create custom conventions using method registerConvention.

Source and destination types

You can use simple types and custom types for source and destination objects. There is only one simple type - Object. Custom types include all user defined types.

Type can be constructor function or class. The result of mapping depends on types that you used while configuring the mapping. Here are the rules (in format source type -> destination type) applied for types mapping:

1) Simple -> Simple

  • all fields from source type will be converted and mapped to destination type by convention.
const sourceType = Object;
const destinationType = Object;

mapper.register(mapper.SNAKE_CASE_CONVENTION, 'testMap', sourceType, destinationType);

const result = mapper.testMap({
  field1: 'test1',
  field2: 'test2',
  field3: 'test3'
});

console.log(result); //{ field_1: 'test1', field_2: 'test2', field_3: 'test3' }
console.log(result instanceof Object); // true

2) Simple -> Custom

  • In a result there will be only converted fields from source value that are included in Custom type.
const sourceType = Object;
class DestinationType {
  constructor() {
    this.field_2 = undefined;
    this.field_3 = undefined;
  }
}

mapper.register(mapper.SNAKE_CASE_CONVENTION, 'testMap', sourceType, DestinationType);

const result = mapper.testMap({
  field1: 'test1',
  field2: 'test2',
  field3: 'test3',
  field4: 'test4'
});

console.log(result); //{ field_2: 'test2', field_3: 'test3' }
console.log(result instanceof DestinationType); // true

3) Custom -> Simple

  • only fields from source value that are included in source custom type will be converted and added to the result.
class SourceType {
  constructor() {
    this.field1 = undefined;
    this.field2 = undefined;
  }
}
const destinationType = Object;

mapper.register(mapper.SNAKE_CASE_CONVENTION, 'testMap', SourceType, destinationType);

const result = mapper.testMap({
  field1: 'test1',
  field2: 'test2',
  field3: 'test3',
  field4: 'test4'
});

console.log(result); //{ field_1: 'test1', field_2: 'test2' }
console.log(result instanceof Object); // true

4) Custom -> Custom

  • only fields from source value that are included in source custom type will be converted. Converted fields will be added to the result only if they are included in destination type.
class SourceType {
  constructor() {
    this.field1 = undefined;
    this.field2 = undefined;
  }
}
class DestinationType {
  constructor() {
    this.field_2 = undefined;
    this.field_3 = undefined;
  }
}

mapper.register(mapper.SNAKE_CASE_CONVENTION, 'testMap', SourceType, DestinationType);

const result = mapper.testMap({
  field1: 'test1',
  field2: 'test2',
  field3: 'test3',
  field4: 'test4'
});

console.log(result); //{ field_2: 'test2' }
console.log(result instanceof DestinationType); // true

API

Mapper instance

mapper.register(conventionName, methodName, SourceType, DestType[, configCb])

Register new map for the mapper.

Args:

  • conventionName [String]: name of fields convention.
  • methodName [String]: name of method on mapper instance.
  • SourceType [Function]: type of source object.
  • DestType [Function]: type of destination object.
  • configCb [Function]: optional. Function for additional mapping configuration. Mapping instance is passed as a parameter.

mapper.generateType(typeName, fields)

Generates new custom type. Use the method as a helper if you don't want to write boilerplate code like:

function TypeName() {
    this.field1 = undefined;
    this.field2 = undefined;
}

// the same using mapper.generateType
const TypeName = mapper.generateType('TypeName', ['field1', 'field2']);

Args:

  • typeName [String]: type name.
  • fields Array([String]): array of fields that will be created for the type.

Returns: custom type function constructor.

mapper.registerConvention(conventionName, ConventionImplementation)

Register new field convention. See conventions for more details.

Args:

  • conventionName [String]: name of the convention.
  • ConventionImplementation [Function]: function constructor of the convention.

Object that is created with ConventionImplementation constructor should have method getField.

Example of upper case convention (each field name will be transformed to upper case field name):

class Convention {
    getField(sourceFieldName) {
        return sourceFieldName.toUpperCase();
    }
}

mapper.extendMap(methodName, implementation)

Extend mapping instance with custom method.

Args:

  • methodName [String]: name of the method.
  • implementation [Function]: function that takes source object as a parameter.

mapper.CAMEL_CASE_CONVENTION

Constant for camel case convention. You don't have to implement the convention manually.

mapper.PASCAL_CASE_CONVENTION

Constant for pascal case convention. You don't have to implement the convention manually.

mapper.SNAKE_CASE_CONVENTION

Constant for snake case convention. You don't have to implement the convention manually.

Mapping instance

mapping.mapField(destFieldName, sourceFieldNameOrCallback)

Map destination field on source field.

Args:

  • destFieldName [String]: destination object field name.
  • sourceFieldNameOrCallback [String|Function]: if string - name of source field that's used in mapping. If function - callback that executes with source value.
const sourceType = Object;
const destinationType = Object;

mapper.register(mapper.SNAKE_CASE_CONVENTION, 'testMap', sourceType, destinationType, (map) => {
  map.mapField('field_1', 'field4')
    .mapField('field_2', value => value.field2.toUpperCase());
});

const result = mapper.testMap({
  field2: 'test2',
  field4: 'test4'
});

console.log(result); //{ field_1: 'test4', field_2: 'TEST2' }

mapping.mapFieldByPath(destFieldName, pathInSourceObj)

Map destination field on source field by path. (see get-value for path variants.)

Args:

  • destFieldName [String]: destination object field name.
  • pathInSourceObj [String]: path in source object.
const sourceType = Object;
const destinationType = Object;

mapper.register(mapper.SNAKE_CASE_CONVENTION, 'testMap', sourceType, destinationType, (map) => {
  map.mapFieldByPath('field_1', 'field4')
    .mapFieldByPath('field_2', 'field2.name');
});

const result = mapper.testMap({
  field2: {
    name: 'test2'
  },
  field4: 'test4'
});

console.log(result); //{ field_1: 'test4', field_2: 'test2' }

mapping.ignoreField(destFieldName)

Ignore destination field. The field will be excluded from destination object.

Args:

  • destFieldName [String]: destination object field name.
const sourceType = Object;
const destinationType = Object;

mapper.register(mapper.SNAKE_CASE_CONVENTION, 'testMap', sourceType, destinationType, (map) => {
  map.ignoreField('field_2');
});

const result = mapper.testMap({
  field1: 'test1',
  field2: 'test2'
});

console.log(result); //{ field_1: 'test1' }

mapping.convert(convertCb)

Use custom converter for mapping.

Args:

  • convertCb [Function]: function that makes mapping. The function takes single parameter - source value.
const sourceType = Object;
const destinationType = Object;

mapper.register(mapper.SNAKE_CASE_CONVENTION, 'testMap', sourceType, destinationType, (map) => {
  map.convert((obj) => {
    return {
       newField1: obj.field1,
       newField2: obj.field2
    };
  });
});

const result = mapper.testMap({
  field1: 'test1',
  field2: 'test2'
});

console.log(result); //{ newField1: 'test1', newField2: 'test2' }

Async Mapping

To make mapping async you should pass name that ends with 'Async' in register method.

const sourceType = Object;
const destinationType = Object;

mapper.register(mapper.SNAKE_CASE_CONVENTION, 'testMapAsync', sourceType, destinationType, (map) => {
  map.convert((obj) => {
    return Promise.resolve({
       newField1: obj.field1,
       newField2: obj.field2
    });
  });
});

mapper.testMapAsync({
  field1: 'test1',
  field2: 'test2'
}).then((result) => {
  console.log(result); //{ newField1: 'test1', newField2: 'test2' }
});

What's in a name?

Dee is one of my favorite detective characters - Judge Dee.

Author

Ilya Markevich - @ilya_mark91

1.1.1

7 years ago

1.0.1

7 years ago

1.0.0

7 years ago

0.1.3

7 years ago

0.1.2

7 years ago

0.1.1

7 years ago

0.1.0

7 years ago