|
| 1 | +# TS-mapstruct |
| 2 | + |
| 3 | +TS-Mapstruct is an approach of the JAVA [MapStruct](https://mapstruct.org/) addapted in **TypeScript**. |
| 4 | +It's a code generator that simplifies the implementation of mappings over configuration approach. |
| 5 | + |
| 6 | +## Installation |
| 7 | + |
| 8 | +``` |
| 9 | +npm install ts-mapstruct |
| 10 | +``` |
| 11 | +## prerequisites |
| 12 | + |
| 13 | +The DTO used in the mapper must have well named getters and setters in camelCase for each mapped properties.\ |
| 14 | +It is recommended that the DTO constructor can be empty for simplify the code, but it is not a problem if it cannot. |
| 15 | +## Usage |
| 16 | +For the exemple, I will take a **UserMapper** that maps a **UserDto** into **UserEntity**. |
| 17 | +### DTO |
| 18 | + |
| 19 | +```ts |
| 20 | +export class UserDto { |
| 21 | + private fname: string; |
| 22 | + private lname: string; |
| 23 | + private bdate: number; |
| 24 | + private isMajor: boolean; |
| 25 | + private gender: GenderEnum; |
| 26 | + |
| 27 | + constructor() {} |
| 28 | + |
| 29 | + getFname(): string { |
| 30 | + return this.fname; |
| 31 | + } |
| 32 | + |
| 33 | + setFname(fname: string): void { |
| 34 | + return this.fname = fname; |
| 35 | + } |
| 36 | + |
| 37 | + // **OTHER GETTERS AND SETTERS** |
| 38 | +} |
| 39 | +``` |
| 40 | +```ts |
| 41 | +export class UserEntity { |
| 42 | + private fullName: string; |
| 43 | + private cn: string; |
| 44 | + private sn: string; |
| 45 | + private bdate: number; |
| 46 | + private isMajor: boolean; |
| 47 | + private lastConnexionTime: number; |
| 48 | + |
| 49 | + constructor(fullName?: string) { |
| 50 | + this.fullName = fullName; |
| 51 | + } |
| 52 | + |
| 53 | + // **GETTERS AND SETTERS** |
| 54 | +} |
| 55 | +``` |
| 56 | +### Mapper |
| 57 | + |
| 58 | +Create a Mapper class that wrap the mapping functions.\ |
| 59 | +It is necessary to type the arguments and the return value of each function.\ |
| 60 | +This function must also return an object with the good type without which it will not work.\ |
| 61 | +That's why it simplifies the code to have an empty constructor for the DTO.\ |
| 62 | +Finally decorate your method with the **@Mappings** decorator by passing the different mapping options. |
| 63 | + |
| 64 | +```ts |
| 65 | +export class UserMapper { |
| 66 | + |
| 67 | + /*-------------------*\ |
| 68 | + UserDto -> User |
| 69 | + \*-------------------*/ |
| 70 | + |
| 71 | + @Mappings( |
| 72 | + { target: 'fullName', expression: 'getConcatProperties(fname, lname)' }, |
| 73 | + { target: 'cn', source: 'fname' }, |
| 74 | + { target: 'sn', source: 'sname' }, |
| 75 | + { target: 'lastConnexionTime', value: Date.now()}, |
| 76 | + ) |
| 77 | + entityFromDto(_userDto: UserDto): UserEntity { |
| 78 | + return new UserEntity; |
| 79 | + } |
| 80 | + |
| 81 | + entitiesFromDtos(userDto: UserDto[]): UserEntity[] { |
| 82 | + return userDto.map(this.entityFromDto); |
| 83 | + } |
| 84 | + |
| 85 | + /*-------------------*\ |
| 86 | + Mapping methods |
| 87 | + \*-------------------*/ |
| 88 | + |
| 89 | + getConcatProperties(...properties: [...any, string?]): string { |
| 90 | + ... |
| 91 | + } |
| 92 | +} |
| 93 | +``` |
| 94 | + |
| 95 | +In this case, the fullName will be computed with the expression passed in expression options.\ |
| 96 | +The porperties bdate and isMajor will automatically retrieve their match value from the DTO.\ |
| 97 | +The property lastConnexionTime is set with the value passed in the value option.\ |
| 98 | +The others are mapped with the value of the DTO's property passed to the source option.\ |
| 99 | +And the gender property is ignored. |
| 100 | + |
| 101 | +### Mapping Options |
| 102 | +| Properties | Description | Required | |
| 103 | +| ----------- | ----------- | ------------ | |
| 104 | +| target | The target name property | true | |
| 105 | +| source | The source name property | false | |
| 106 | +| value | A direct value | false | |
| 107 | +| expression | A JS expression | false | |
| 108 | + |
| 109 | +You must provide exactly one of the non required properties in the @Mappings decorator, an Exception will be throw otherwise. |
| 110 | + |
| 111 | +### Supplied Mapping Functions |
| 112 | +the mapper provides some functions to pass via the "expression" property to facilitate the mapping: |
| 113 | +```ts |
| 114 | +/** |
| 115 | +* concatenates each property provided in the running order. |
| 116 | +* You have the possibility to define a separator passed at the last index |
| 117 | +* @params properties: properties to concat |
| 118 | +* @return string: the concatenation of each properties |
| 119 | +* @required each property must be a string |
| 120 | +*/ |
| 121 | +getConcatProperties(...properties: [...string, string?]): string |
| 122 | +``` |
| 123 | +## Exceptions thrown |
| 124 | +
|
| 125 | +**BadExpressionExceptionMapper** |
| 126 | +
|
| 127 | +**EmptySourceNameExceptionMapper** |
| 128 | +
|
| 129 | +**IllegalMappingOptionsExceptionMapper** |
| 130 | +
|
| 131 | +**IllegalObjectAccessExceptionMapper** |
| 132 | +
|
| 133 | +**IllegalPropertyKeyExceptionMapper** |
| 134 | +
|
| 135 | +**IllegalPropertyValueExceptionMapper** |
| 136 | +
|
| 137 | +**InvalidSourceNameExceptionMapper** |
0 commit comments