diff --git a/packages/core/src/lib/core.ts b/packages/core/src/lib/core.ts index 041d55f19..3e5d13c03 100644 --- a/packages/core/src/lib/core.ts +++ b/packages/core/src/lib/core.ts @@ -1,31 +1,32 @@ import { mapMutate, mapReturn } from './mappings/map'; import { - CUSTOM_NODE_INSPECT, - ERROR_HANDLER, - MAPPINGS, - METADATA_MAP, - METADATA_OBJECT_MAP, - NAMING_CONVENTIONS, - PROFILE_CONFIGURATION_CONTEXT, - RECURSIVE_COUNT, - RECURSIVE_DEPTH, - STRATEGY, + CUSTOM_NODE_INSPECT, + ERROR_HANDLER, + MAPPINGS, + METADATA_MAP, + METADATA_OBJECT_MAP, + NAMING_CONVENTIONS, + PROFILE_CONFIGURATION_CONTEXT, + RECURSIVE_COUNT, + RECURSIVE_DEPTH, + STRATEGY } from './symbols'; -import type { - ArrayKeyedMap, - Dictionary, - ErrorHandler, - MapOptions, - Mapper, - Mapping, - MappingConfiguration, - MappingStrategy, - MappingStrategyInitializer, - Metadata, - MetadataIdentifier, - ModelIdentifier, - NamingConventionInput, +import { + ArrayKeyedMap, + Dictionary, + ErrorHandler, + MapOptions, + Mapper, + Mapping, + MappingConfiguration, + MappingStrategy, + MappingStrategyInitializer, + Metadata, + MetadataIdentifier, + ModelIdentifier, + NamingConventionInput } from './types'; +import { mapAsyncHandler } from './utils/async-map-handler'; import { getMapping } from './utils/get-mapping'; import { AutoMapperLogger } from './utils/logger'; @@ -274,14 +275,42 @@ Mapper {} is an empty Object as a Proxy. The following methods are available to | MapOptions, options?: MapOptions ): Promise => { - const result = receiver['map']( + const mapped = receiver['map']( sourceObject, sourceIdentifier, destinationIdentifierOrOptions, options ); - return new Promise((res) => { - setTimeout(res, 0, result); + + // start get mappings + const { destinationIdentifier, mapOptions } = + getOptions( + sourceIdentifier, + destinationIdentifierOrOptions, + options + ); + + const mapping = getMapping( + receiver, + sourceIdentifier, + destinationIdentifier + ); + // + + return new Promise((resolve, reject) => { + mapAsyncHandler( + mapping, + sourceObject, + destinationIdentifier, + mapOptions || {}, + mapped + ) + .then((result) => { + resolve(result); + }) + .catch((err) => { + reject(err); + }); }); }; } @@ -380,15 +409,41 @@ Mapper {} is an empty Object as a Proxy. The following methods are available to | MapOptions, options?: MapOptions ) => { - const result = receiver['mapArray']( + const mapped = receiver['mapArray']( sourceArray, sourceIdentifier, destinationIdentifierOrOptions, options ); - return new Promise((res) => { - setTimeout(res, 0, result); - }); + const { destinationIdentifier, mapOptions } = + getOptions( + sourceIdentifier, + destinationIdentifierOrOptions, + options + ); + + const mapping = getMapping( + receiver, + sourceIdentifier, + destinationIdentifier + ); + + return Promise.all( + mapped.map( + async ( + sourceObject: TDestination, + i: number + ) => { + return mapAsyncHandler( + mapping, + sourceArray[i], + destinationIdentifier, + mapOptions || {}, + sourceObject + ); + } + ) + ); }; } @@ -449,16 +504,42 @@ Mapper {} is an empty Object as a Proxy. The following methods are available to | MapOptions, options?: MapOptions ) => { - return new Promise((res) => { - receiver['mutate']( - sourceObject, - destinationObject, + // + receiver['mutate']( + sourceObject, + destinationObject, + sourceIdentifier, + destinationIdentifierOrOptions, + options + ); + + // start get mappings + const { destinationIdentifier, mapOptions } = + getOptions( sourceIdentifier, destinationIdentifierOrOptions, options ); - setTimeout(res, 0); + const mapping = getMapping( + receiver, + sourceIdentifier, + destinationIdentifier + ); + + return new Promise((resolve, reject) => { + mapAsyncHandler( + mapping, + sourceObject, + destinationIdentifier, + mapOptions || {} + ) + .then(() => { + resolve(); + }) + .catch((err) => { + reject(err); + }); }); }; } @@ -552,17 +633,36 @@ Mapper {} is an empty Object as a Proxy. The following methods are available to | MapOptions, options?: MapOptions ) => { - return new Promise((res) => { - receiver['mutateArray']( - sourceArray, - destinationArray, + receiver['mutateArray']( + sourceArray, + destinationArray, + sourceIdentifier, + destinationIdentifierOrOptions, + options + ); + const { destinationIdentifier, mapOptions } = + getOptions( sourceIdentifier, destinationIdentifierOrOptions, options ); - setTimeout(res, 0); - }); + const mapping = getMapping( + receiver, + sourceIdentifier, + destinationIdentifier + ); + + return Promise.all( + sourceArray.map(async (sourceObject) => { + mapAsyncHandler( + mapping, + sourceObject, + destinationIdentifier, + mapOptions || {} + ); + }) + ); }; } diff --git a/packages/core/src/lib/types.ts b/packages/core/src/lib/types.ts index 58e07a5f9..1fb8aea7c 100644 --- a/packages/core/src/lib/types.ts +++ b/packages/core/src/lib/types.ts @@ -93,6 +93,16 @@ export interface Converter< convert(source: TSource): TConvertDestination; } +export type MapCallbackAsync< + TSource extends Dictionary, + TDestination extends Dictionary, + TExtraArgs extends Record = Record +> = ( + source: TSource, + destination: TDestination, + extraArguments?: TExtraArgs +) => Promise; + export type MapCallback< TSource extends Dictionary, TDestination extends Dictionary, diff --git a/packages/core/src/lib/utils/async-map-handler.ts b/packages/core/src/lib/utils/async-map-handler.ts new file mode 100644 index 000000000..7b2902a9e --- /dev/null +++ b/packages/core/src/lib/utils/async-map-handler.ts @@ -0,0 +1,31 @@ +import { Dictionary } from '@mikro-orm/core'; +import { MapCallbackAsync, Mapping } from '../types'; + +export async function mapAsyncHandler< + TSource extends Dictionary, + TDestination extends Dictionary, + TExtraArgs extends Record = Record +>( + mapping: Mapping, + source: TSource, + destination: TDestination, + extraArguments: TExtraArgs, + mapped?: TDestination +) { + if (!mapping[7]) { + mapping[7] = []; + } + const afterMap = mapping[7][1] as MapCallbackAsync< + TSource, + TDestination, + TExtraArgs + >; + if (afterMap) { + await afterMap(source, destination, extraArguments); + } + if (mapped) { + return Object.assign(mapped, destination); + } else { + return destination; + } +} diff --git a/packages/integration-test/src/classes/map-async.spec.ts b/packages/integration-test/src/classes/map-async.spec.ts index 46fbf946a..d98cd6a3e 100644 --- a/packages/integration-test/src/classes/map-async.spec.ts +++ b/packages/integration-test/src/classes/map-async.spec.ts @@ -1,10 +1,10 @@ import { classes } from '@automapper/classes'; import { - afterMap, - createMap, - createMapper, - forMember, - ignore, + afterMap, + createMap, + createMapper, + forMember, + ignore } from '@automapper/core'; import { SimpleUserDto } from './dtos/simple-user.dto'; import { SimpleUser } from './models/simple-user'; @@ -12,20 +12,21 @@ import { SimpleUser } from './models/simple-user'; describe('Map Async Classes', () => { const mapper = createMapper({ strategyInitializer: classes() }); - it('should map', async () => { + it('should map a single', async () => { createMap( mapper, SimpleUser, SimpleUserDto, forMember((d) => d.fullName, ignore()), afterMap(async (_, destination) => { - const fullName = await Promise.resolve().then( - () => 'Tran Chau' - ); + const fullName = await new Promise((resolve) => { + setTimeout(() => { + resolve('Tran Chau'); + }, 1000); + }); Object.assign(destination, { fullName }); }) ); - const dto = await mapper.mapAsync( new SimpleUser('Chau', 'Tran'), SimpleUser, @@ -33,4 +34,27 @@ describe('Map Async Classes', () => { ); expect(dto.fullName).toEqual('Tran Chau'); }); + + it('should map an array', async () => { + createMap( + mapper, + SimpleUser, + SimpleUserDto, + forMember((d) => d.fullName, ignore()), + afterMap(async (_, destination) => { + const fullName = await new Promise((resolve) => { + setTimeout(() => { + resolve('Tran Chau'); + }, 1000); + }); + destination.fullName = fullName; + }) + ); + const dtos = await mapper.mapArrayAsync( + [new SimpleUser('Chau', 'Tran')], + SimpleUser, + SimpleUserDto + ); + expect(dtos[0].fullName).toEqual('Tran Chau'); + }); });