Skip to content

Commit

Permalink
feat(class-transformers): add the EnumFallback decorator
Browse files Browse the repository at this point in the history
  • Loading branch information
Bendakh authored and amarlankri committed Dec 21, 2023
1 parent a1bcd4e commit c895953
Show file tree
Hide file tree
Showing 9 changed files with 199 additions and 6 deletions.
25 changes: 21 additions & 4 deletions package-lock.json

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

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,12 @@
}
},
"dependencies": {
"@algoan/nestjs-class-validators": "file:packages/class-validators",
"@algoan/nestjs-custom-decorators": "file:packages/custom-decorators",
"@algoan/nestjs-google-pubsub-client": "file:packages/google-pubsub-client",
"@algoan/nestjs-google-pubsub-microservice": "file:packages/google-pubsub-microservice",
"@algoan/nestjs-http-exception-filter": "file:packages/http-exception-filter",
"@algoan/nestjs-logging-interceptor": "file:packages/logging-interceptor",
"@algoan/nestjs-pagination": "file:packages/pagination",
"@algoan/nestjs-class-validators": "file:packages/class-validators"
"@algoan/nestjs-pagination": "file:packages/pagination"
}
}
30 changes: 30 additions & 0 deletions packages/class-transformers/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Nestjs class transformers

Extends [class-transformers package](https://github.com/typestack/class-transformer) with additional features.

## Installation

```bash
npm install --save @algoan/nestjs-class-transformers
```

## EnumFallback

### Usage

```ts
import { EnumFallback } from '@algoan/nestjs-class-transformers';

export enum UserRole {
ADMIN = 'ADMIN',
READER = 'READER',
}

class User {
@EnumFallback({
type: UserRole,
fallback: (value: UserRole) => UserRole.READER // if the role is not "ADMIN" or "READER", then the role will be "READER".
})
public role?: UserRole;
}
```
8 changes: 8 additions & 0 deletions packages/class-transformers/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module.exports = {
...require('../jest.common'),

coverageDirectory: "coverage",
collectCoverageFrom: ["./src/**/*.ts"],

rootDir: ".",
};
16 changes: 16 additions & 0 deletions packages/class-transformers/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "@algoan/nestjs-class-transformers",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "tsc -p .",
"test:cov": "jest --coverage",
"test": "jest"
},
"author": "",
"license": "ISC",
"dependencies": {
"class-transformer": "^0.5.1"
}
}
40 changes: 40 additions & 0 deletions packages/class-transformers/src/enum-fallback.decorator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { Transform, TransformOptions } from 'class-transformer';

/**
* Options for EnumFallback decorator.
*/
export interface EnumFallbackOptions<T> {
/**
* The enum type to check against.
*/
type: { [s: string]: T };
/**
* A function that returns the fallback value.
* @param value The invalid value.
*/
fallback: (value: T) => T;
}

/**
* Return given literal value if it is included in the specific enum type.
* Otherwise, return the value provided by the given fallback function.
*/
export const EnumFallback = <T>(
params: EnumFallbackOptions<T>,
transformOptions?: TransformOptions,
): PropertyDecorator => {
const { type, fallback } = params;

return Transform(({ value }) => {
// eslint-disable-next-line no-null/no-null
if (value === undefined || value === null) {
return value;
}

if (!Object.values(type).includes(value)) {
return fallback(value);
}

return value;
}, transformOptions);
};
1 change: 1 addition & 0 deletions packages/class-transformers/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './enum-fallback.decorator';
66 changes: 66 additions & 0 deletions packages/class-transformers/test/enum-fallback-decorator.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/* eslint-disable no-null/no-null */
/* eslint-disable max-classes-per-file */
import { plainToInstance } from 'class-transformer';
import { EnumFallback } from '../src';

describe('EnumFallback Decorator', () => {
enum UserRole {
ADMIN = 'ADMIN',
READER = 'READER',
}

it('should return the given value if it is valid', async () => {
class User {
@EnumFallback({ type: UserRole, fallback: () => UserRole.READER })
public role?: UserRole;
}

const user = plainToInstance(User, { role: 'ADMIN' });

expect(user.role).toEqual(UserRole.ADMIN);
});

it('should return the fallback value if the given value is invalid', async () => {
class User {
@EnumFallback({ type: UserRole, fallback: () => UserRole.READER })
public role?: UserRole;
}

const user = plainToInstance(User, { role: 'WRITER' });

expect(user.role).toEqual(UserRole.READER);
});

it('should return undefined if the given value is undefined', async () => {
class User {
@EnumFallback({ type: UserRole, fallback: () => UserRole.READER })
public role?: UserRole;
}

const user = plainToInstance(User, { role: undefined });

expect(user.role).toBeUndefined();
});

it('should return undefined if the given value is null', async () => {
class User {
@EnumFallback({ type: UserRole, fallback: () => UserRole.READER })
public role?: UserRole;
}

const user = plainToInstance(User, { role: null });

expect(user.role).toBe(null);
});

it('should take into account the transform options', async () => {
class User {
@EnumFallback({ type: UserRole, fallback: () => UserRole.READER }, { groups: ['group1'] })
public role?: UserRole;
}

const user = plainToInstance(User, { role: 'WRITER' }, { groups: ['group2'] });

expect(user.role).toEqual('WRITER');
});
});
15 changes: 15 additions & 0 deletions packages/class-transformers/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"extends": "../tsconfig.base.json",
"compilerOptions": {
"declaration": true,
"experimentalDecorators": true,
"outDir": "dist",
},
"include": [
"./src/**/*",
"./test/**/*.ts"
],
"exclude": [
"node_modules"
]
}

0 comments on commit c895953

Please sign in to comment.