Skip to content

Commit

Permalink
feat(core): add ability to pass in destinationObj and mutate on map i…
Browse files Browse the repository at this point in the history
…nstead of return
  • Loading branch information
nartc committed Jan 7, 2021
1 parent bdd6d17 commit 46e920d
Show file tree
Hide file tree
Showing 3 changed files with 171 additions and 22 deletions.
48 changes: 41 additions & 7 deletions packages/core/src/lib/create-mapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
MappingPropertiesClassId,
TransformationType,
} from '@automapper/types';
import { map, mapArray } from './map';
import { mapArray, mapMutate, mapReturn } from './map';
import { getMemberPath } from './utils';

/**
Expand Down Expand Up @@ -58,7 +58,7 @@ export function createMapper<TKey = unknown>({
profile(this);
return this;
},
map(sourceObj, destination, source, options?) {
map(sourceObj, destination, source, destinationObjOrOptions?, options?) {
const { preMap } = plugin;

// run preMap if available
Expand All @@ -67,17 +67,51 @@ export function createMapper<TKey = unknown>({
// get mapping between Source and Destination
const mapping = this.getMapping(source, destination);

// map
return map(
// check mutate or return

// if destinationObjOrOptions has beforeMap or afterMap
// or destinationObjOrOptions is null/undefined => this is a mapReturn
// TODO(chau): this might fail if destinationObj has a beforeMap/afterMap property on the consumer side.
if (
(destinationObjOrOptions &&
('beforeMap' in destinationObjOrOptions ||
'afterMap' in destinationObjOrOptions)) ||
destinationObjOrOptions == null
) {
return mapReturn(
sourceInstance ?? sourceObj,
mapping,
destinationObjOrOptions,
this,
errorHandler
);
}

mapMutate(
sourceInstance ?? sourceObj,
mapping,
options,
this,
errorHandler
errorHandler,
destinationObjOrOptions
);
},
mapAsync(sourceObj, destination, source, options?) {
return Promise.resolve(this.map(sourceObj, destination, source, options));
mapAsync(
sourceObj,
destination,
source,
destinationObjOrOptions,
options?
) {
return Promise.resolve(
this.map(
sourceObj,
destination,
source,
destinationObjOrOptions,
options
)
);
},
mapArray(sourceArr, destination, source, options) {
return mapArray(
Expand Down
101 changes: 86 additions & 15 deletions packages/core/src/lib/map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import type {
NullSubstitutionFunction,
} from '@automapper/types';
import { MapFnClassId, TransformationType } from '@automapper/types';
import { isEmpty, set } from './utils';
import { isEmpty, set, setMutate } from './utils';

/**
* Instruction on how to map a particular member on the destination
Expand Down Expand Up @@ -112,7 +112,7 @@ ${unmappedKeys.join(',\n')}
* @param {ErrorHandler} errorHandler - the error handler
* @param {boolean} [isMapArray = false] - whether the map operation is in Array mode
*/
export function map<
export function mapReturn<
TSource extends Dictionary<TSource> = unknown,
TDestination extends Dictionary<TDestination> = unknown
>(
Expand All @@ -122,6 +122,74 @@ export function map<
mapper: Mapper,
errorHandler: ErrorHandler,
isMapArray = false
): TDestination {
const setMemberReturn = (
destinationMemberPath: string,
destination: TDestination
) => (value: unknown) => {
destination = set(destination, destinationMemberPath, value);
};
return map(
sourceObj,
mapping,
options,
mapper,
errorHandler,
setMemberReturn,
isMapArray
);
}

/**
*
* @param {TSource} sourceObj - the source object
* @param {Mapping} mapping - the Mapping object of source <> destination
* @param {MapOptions} options - options used for this particular map operation
* @param {Mapper} mapper - the mapper instance
* @param {ErrorHandler} errorHandler - the error handler
* @param {TDestination} destinationObj - the destination obj to be mutated
*/
export function mapMutate<
TSource extends Dictionary<TSource> = unknown,
TDestination extends Dictionary<TDestination> = unknown
>(
sourceObj: TSource,
mapping: Mapping<TSource, TDestination>,
options: MapOptions<TSource, TDestination>,
mapper: Mapper,
errorHandler: ErrorHandler,
destinationObj: TDestination
): void {
const setMemberMutate = (destinationMember: string) => (value: unknown) => {
setMutate(destinationObj, destinationMember, value);
};
map(sourceObj, mapping, options, mapper, errorHandler, setMemberMutate);
}

/**
*
* @param {TSource} sourceObj - the source object
* @param {Mapping} mapping - the Mapping object of source <> destination
* @param {MapOptions} options - options used for this particular map operation
* @param {Mapper} mapper - the mapper instance
* @param {ErrorHandler} errorHandler - the error handler
* @param {Function} setMemberFn
* @param {boolean} [isMapArray = false] - whether the map operation is in Array mode
*/
function map<
TSource extends Dictionary<TSource> = unknown,
TDestination extends Dictionary<TDestination> = unknown
>(
sourceObj: TSource,
mapping: Mapping<TSource, TDestination>,
options: MapOptions<TSource, TDestination>,
mapper: Mapper,
errorHandler: ErrorHandler,
setMemberFn: (
destinationMemberPath: string,
destination?: TDestination
) => (value: unknown) => void,
isMapArray = false
) {
// destructure the mapping
let [
Expand Down Expand Up @@ -164,8 +232,7 @@ export function map<
] = propsToMap[i];

// Setup a shortcut function to set destinationMemberPath on destination with value as argument
const setMember = (value: unknown) =>
set(destination, destinationMemberPath, value);
const setMember = setMemberFn(destinationMemberPath, destination);

// This destination key is being configured. Push to configuredKeys array
configuredKeys.push(destinationMemberPath);
Expand All @@ -175,7 +242,7 @@ export function map<
transformationPreCondPredicate &&
!transformationPreCondPredicate(sourceObj)
) {
destination = setMember(preCondDefaultValue);
setMember(preCondDefaultValue);
continue;
}

Expand All @@ -190,13 +257,13 @@ export function map<

// if null/undefined
if (mapInitializedValue == null) {
destination = setMember(mapInitializedValue);
setMember(mapInitializedValue);
continue;
}

// if isDate
if (mapInitializedValue instanceof Date) {
destination = setMember(new Date(mapInitializedValue));
setMember(new Date(mapInitializedValue));
continue;
}

Expand All @@ -205,17 +272,17 @@ export function map<
const [first] = mapInitializedValue;
// if first item is a primitive
if (typeof first !== 'object') {
destination = setMember(mapInitializedValue.slice());
setMember(mapInitializedValue.slice());
continue;
}

// if first is empty
if (isEmpty(first)) {
destination = setMember([]);
setMember([]);
continue;
}

destination = setMember(
setMember(
mapArray(
mapInitializedValue,
nestedDestinationMemberKey,
Expand All @@ -234,24 +301,28 @@ export function map<
nestedSourceMemberKey,
nestedDestinationMemberKey
);
destination = setMember(
// for nested model, we do not care about mutate or return. we will always need to return
setMember(
map(
mapInitializedValue,
nestedMapping,
undefined,
mapper,
errorHandler
errorHandler,
(memberPath, nestedDestination) => (value) => {
nestedDestination = set(nestedDestination, memberPath, value);
}
)
);
continue;
}

// if is primitive
destination = setMember(mapInitializedValue);
setMember(mapInitializedValue);
continue;
}

destination = setMember(
setMember(
mapMember(
transformationMapFn,
sourceObj,
Expand Down Expand Up @@ -308,7 +379,7 @@ export function mapArray<
for (let i = 0, len = sourceArray.length; i < len; i++) {
const mapping = mapper.getMapping(source, destination);
destinationArray.push(
map(
mapReturn(
sourceArray[i],
mapping as Mapping<TSource, TDestination>,
undefined,
Expand Down
44 changes: 44 additions & 0 deletions packages/types/src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,28 @@ export interface Mapper<TKey = unknown> {
options?: MapOptions<TSource, TDestination>
): TDestination;

map<
TSource extends Dictionary<TSource> = unknown,
TDestination extends Dictionary<TDestination> = unknown
>(
sourceObj: TSource,
destination: new (...args: unknown[]) => TDestination,
source: new (...args: unknown[]) => TSource,
destinationObj: TDestination,
options?: MapOptions<TSource, TDestination>
): void;

map<
TSource extends Dictionary<TSource>,
TDestination extends Dictionary<TDestination>
>(
sourceObj: TSource,
destination: string,
source: string,
destinationObj: TDestination,
options?: MapOptions<TSource, TDestination>
): void;

mapAsync<
TSource extends Dictionary<TSource> = unknown,
TDestination extends Dictionary<TDestination> = unknown
Expand All @@ -289,6 +311,28 @@ export interface Mapper<TKey = unknown> {
options?: MapOptions<TSource, TDestination>
): Promise<TDestination>;

mapAsync<
TSource extends Dictionary<TSource> = unknown,
TDestination extends Dictionary<TDestination> = unknown
>(
sourceObj: TSource,
destination: new (...args: unknown[]) => TDestination,
source: new (...args: unknown[]) => TSource,
destinationObj: TDestination,
options?: MapOptions<TSource, TDestination>
): Promise<void>;

mapAsync<
TSource extends Dictionary<TSource>,
TDestination extends Dictionary<TDestination>
>(
sourceObj: TSource,
destination: string,
source: string,
destinationObj: TDestination,
options?: MapOptions<TSource, TDestination>
): Promise<void>;

mapArray<
TSource extends Dictionary<TSource> = unknown,
TDestination extends Dictionary<TDestination> = unknown
Expand Down

0 comments on commit 46e920d

Please sign in to comment.