Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Ugo evola committed May 16, 2022
0 parents commit 9333437
Show file tree
Hide file tree
Showing 20 changed files with 690 additions and 0 deletions.
36 changes: 36 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# compiled output
/dist
/node_modules

# Logs
logs
*.log
npm-debug.log*
pnpm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
publish.sh

# OS
.DS_Store

# Tests
/coverage
/.nyc_output

# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace

# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
137 changes: 137 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# TS-mapstruct

TS-Mapstruct is an approach of the JAVA [MapStruct](https://mapstruct.org/) addapted in **TypeScript**.
It's a code generator that simplifies the implementation of mappings over configuration approach.

## Installation

```
npm install ts-mapstruct
```
## prerequisites

The DTO used in the mapper must have well named getters and setters in camelCase for each mapped properties.\
It is recommended that the DTO constructor can be empty for simplify the code, but it is not a problem if it cannot.
## Usage
For the exemple, I will take a **UserMapper** that maps a **UserDto** into **UserEntity**.
### DTO

```ts
export class UserDto {
private fname: string;
private lname: string;
private bdate: number;
private isMajor: boolean;
private gender: GenderEnum;

constructor() {}

getFname(): string {
return this.fname;
}

setFname(fname: string): void {
return this.fname = fname;
}

// **OTHER GETTERS AND SETTERS**
}
```
```ts
export class UserEntity {
private fullName: string;
private cn: string;
private sn: string;
private bdate: number;
private isMajor: boolean;
private lastConnexionTime: number;

constructor(fullName?: string) {
this.fullName = fullName;
}

// **GETTERS AND SETTERS**
}
```
### Mapper

Create a Mapper class that wrap the mapping functions.\
It is necessary to type the arguments and the return value of each function.\
This function must also return an object with the good type without which it will not work.\
That's why it simplifies the code to have an empty constructor for the DTO.\
Finally decorate your method with the **@Mappings** decorator by passing the different mapping options.

```ts
export class UserMapper {

/*-------------------*\
UserDto -> User
\*-------------------*/

@Mappings(
{ target: 'fullName', expression: 'getConcatProperties(fname, lname)' },
{ target: 'cn', source: 'fname' },
{ target: 'sn', source: 'sname' },
{ target: 'lastConnexionTime', value: Date.now()},
)
entityFromDto(_userDto: UserDto): UserEntity {
return new UserEntity;
}

entitiesFromDtos(userDto: UserDto[]): UserEntity[] {
return userDto.map(this.entityFromDto);
}

/*-------------------*\
Mapping methods
\*-------------------*/

getConcatProperties(...properties: [...any, string?]): string {
...
}
}
```

In this case, the fullName will be computed with the expression passed in expression options.\
The porperties bdate and isMajor will automatically retrieve their match value from the DTO.\
The property lastConnexionTime is set with the value passed in the value option.\
The others are mapped with the value of the DTO's property passed to the source option.\
And the gender property is ignored.

### Mapping Options
| Properties | Description | Required |
| ----------- | ----------- | ------------ |
| target | The target name property | true |
| source | The source name property | false |
| value | A direct value | false |
| expression | A JS expression | false |

You must provide exactly one of the non required properties in the @Mappings decorator, an Exception will be throw otherwise.

### Supplied Mapping Functions
the mapper provides some functions to pass via the "expression" property to facilitate the mapping:
```ts
/**
* concatenates each property provided in the running order.
* You have the possibility to define a separator passed at the last index
* @params properties: properties to concat
* @return string: the concatenation of each properties
* @required each property must be a string
*/
getConcatProperties(...properties: [...string, string?]): string
```
## Exceptions thrown
**BadExpressionExceptionMapper**
**EmptySourceNameExceptionMapper**
**IllegalMappingOptionsExceptionMapper**
**IllegalObjectAccessExceptionMapper**
**IllegalPropertyKeyExceptionMapper**
**IllegalPropertyValueExceptionMapper**
**InvalidSourceNameExceptionMapper**
3 changes: 3 additions & 0 deletions index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './src/decorators/mappings.decorator'
export * from './src/models/mapping-options'
export * from './src/shared/mapping-functions'
181 changes: 181 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 28 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"name": "ts-mapstruct",
"version": "1.0.1",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
"/dist"
],
"repository": {
"type": "git",
"url": "https://github.com/ugoevola/ts-mapstruct"
},
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"@nestjs/common": "^8.0.6",
"class-validator": "^0.13.2",
"lodash": "^4.17.21",
"reflect-metadata": "^0.1.13"
},
"devDependencies": {
"@types/express": "^4.17.13",
"@types/lodash": "^4.14.182",
"@types/sanitizer": "0.0.28",
"rxjs": "^6.5.4"
}
}
4 changes: 4 additions & 0 deletions publish.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/bash
tsc
npm -f unpublish
npm publish --access public
Loading

0 comments on commit 9333437

Please sign in to comment.